PCell instantiating and accessing other cells (for things like guard rings)

Hi Matthias,

While thinking about ways to create things like guard rings (i.e. contacts surrounding a cell instance), I thought of using PCells for that. Currently the way we create guard rings is roughly as follows:

  1. Create a guard ring cell which instantiates the "child cell" (i.e. the one to be surrounded by the contacts).
  2. Get the bbox of the "child cell" and calculate the dimensions of two vertical and two horizontal contact PCell variants.
  3. Instantiate these variants such that they form a ring around the "child cell" instance.

Since we learned that PCells can instantiate other PCells (which we use as in the pcell_variant method), I was wondering if it was possible to create a PCell to implement the same functionality and without having to care about cell instance names and changing parameters and such.

When I ran into problems, I remembered that the layout used to create PCell variants might not be the same as the layout in which the "child cell" exists, which now seems to make any PCells instantiating other non-PCells impossible.

Here is a complete example which (beware!) makes KLayout crash when the inst.bbox method is called within the CellInstantiatingPCell.produce_impl call:

##
# Creates a variant of a PCell based on the given parameters.
#
# @param lib_cell_name [String] Name of the PCell, with its library, of which a variant is being made.
# @param params [{various}] Parameters for the PCell in a hash.
# @param layout [RBA::Layout] Layout to which the new PCell variant will be added.

def self.pcell_variant(lib_cell_name, params, layout)
  libname, cellname = lib_cell_name.split(".")

  # first try to get the pcell from the current layout (see http://klayout.de/forum/comments.php?DiscussionID=404&page=1#Item_7)
  pcell_decl = layout.pcell_declaration(cellname)

  if pcell_decl.nil? # ...and if it doesn't exist, get the library first.
    lib = RBA::Library.library_by_name(libname)
    lib || raise("Unknown PCell library '#{libname}'")

    # find the pcell within the lib
    pcell_decl = lib.layout.pcell_declaration(cellname)
    pcell_decl || raise("Unknown PCell '#{cellname}'")
  end

  unused_params = params.dup
  pv_indices = {}
  # build a param array from the defaults
  pv = pcell_decl.get_parameters.collect do |p|
    pv_indices[p.name.to_sym] = pv_indices.size # also record the index for each name
    p.default
  end

  # call coerce_parameters to make sure the parameters are consistent
  pv = pcell_decl.coerce_parameters(layout, pv)

  # now update, step-by-step, the pv with the given parameters and 
  # call coerce_parameters for each change to remain consistent
  params.each do |k, v|
    raise("PCell #{lib_cell_name}: Encountered unsupported parameter #{k}") if !pv_indices.include? k
    pv[pv_indices[k]] = v
    pv = pcell_decl.coerce_parameters(layout, pv)
  end

  # create a PCell variant cell
  if lib.nil?
    pcell_var = layout.add_pcell_variant(pcell_decl.id, pv)
  else
    pcell_var = layout.add_pcell_variant(lib, pcell_decl.id, pv)
  end
end

class CellInstantiatingPCell < RBA::PCellDeclarationHelper
  def initialize
    super
    param(:child_cell_index, TypeInt, "Index of child cell", default: -1)
  end
  def coerce_parameters_impl
    puts "Executing coerce_parameters_impl: #{@param_values}"
  end      
  def produce_impl
    begin
      puts "#{self}: Executing produce_impl with child_cell_index = #{child_cell_index}"
      trans = RBA::Trans::new(0, 0)
      inst = cell.insert(RBA::CellInstArray.new(child_cell_index, trans))
      layer_idx = layout.layer(RBA::LayerInfo.new(1, 0))
      puts "PCell layout: #{layout}"
      cell.shapes(layer_idx).insert(inst.bbox.dup)
    rescue
      abort("#{$!}\n#{$!.backtrace.join("\n")}")
    end
  end
end

class MyPCellLib < RBA::Library
  def initialize(pcell_classes)
    pcell_classes.each do |pcell_class|
      pcell_obj = pcell_class.new
      layout.register_pcell(pcell_class.name.to_str.split(':')[-1], pcell_obj)
    end
    register(self.class.name.split(':')[-1])
  end
end

MyPCellLib.new([CellInstantiatingPCell])

# create a top cell
ly = RBA::Application.instance.main_window.create_layout(1).layout
puts "Top layout: #{ly}"
top_idx = ly.add_cell("top")

# create a child cell with some content
child = ly.cell(ly.add_cell("child"))
layer = RBA::LayerInfo.new(2, 0)
layer_idx = ly.layer(layer)
triangle = [[0, 50], [-20, 0], [20, 0]].collect{|pt| RBA::Point.new(pt[0], pt[1])}
child.shapes(layer_idx).insert(RBA::Polygon.new(triangle))

# create a PCell variant which should instantiate the child 
# and draw a box on top of it
cci = child.cell_index
pcv = pcell_variant('MyPCellLib.CellInstantiatingPCell', {child_cell_index: cci}, ly)
trans = RBA::Trans::new(200, 100)
ly.cell(top_idx).insert(RBA::CellInstArray::new(pcv, trans))

lv = RBA::Application.instance.main_window.current_view
lv.select_cell(top_idx, 0)
lv.add_missing_layers
lv.zoom_fit

Is there a way around it such as moving the "child cell" into another library, or is this a more fundamental limitation of the current PCell architecture? While looking through the PCell API, I was wondering why there is a "guiding shapes" interface but nothing similar for "guiding cells" or "guiding instances". The above now seems to be an explanation for this.

Regards, Chris

Comments

  • edited November -1

    Huuuu ... I'm scared!

    You like to explore every dark corner of the system, do you? :-)

    Not that I don't like being challenged, but I feel a bit like having opened Pandora's box :-)

    Basically there is a limitation. Remember that PCell's in libraries are instantiated in the library's local layout. When a layout makes use of a PCell in a library, it will reference that local version through a library proxy, but the PCell is still generated in the library's context. So a cell index is not sufficient to convey the information about the instance. Plus, it is likely to change in a save/load loop, so it's not safe to use cell indexes as parameter values.

    A "guiding instance" is not a bad idea. It's surely not easy to implement, but it might be possible.

    But it should be possible to script a utility feature that takes the selected instance and wraps a guard ring around it. This ring is just not tied to the cell so whenever the cell is moved, the ring has to be moved with the cell or deleted and generated again.

    Matthias

  • edited June 2015

    You like to explore every dark corner of the system, do you? :-)

    The project I am working on aims at extending KLayout so it could replace some of our company's existing layout generation tools. Therefore I am investigating what can be achieved with the existing API, which leads to experiments such as the one above.

    So a cell index is not sufficient to convey the information about the instance. Plus, it is likely to change in a save/load loop, so it's not safe to use cell indexes as parameter values.

    I am aware of that and would not use cell indices anyway - I just wanted to create an example as simple as possible to explain what I was trying to do. In the project itself, we are actually using cell names instead... hoping that they will be more consistent. (Although I am a little bit worried when it comes to PCell variants, as they could also change their names if their parameter values are changed.)

    But it should be possible to script a utility feature that takes the selected instance and wraps a guard ring around it.

    That is what we are using currently. Actually what we have so far is somewhat similar to KLayout's PCells, which made me wonder about using PCells for this in the first place. But until a "guiding cell/instance" will make it into KLayout, we'll stick with what we have.

    A "guiding instance" is not a bad idea. It's surely not easy to implement, but it might be possible.

    Just out of curiosity, and only if you have thought about this at all: How would you overcome the problem of the cell/instance not being part of the PCell libraries' Layout object?

    Best regards, Chris

  • edited November -1

    Hi Chris,

    Well, I hope I didn't promise too much already :-)

    When implementation "guiding instances" I'd not provide a fully functional instance but only some attributes representative for it. For example, a cell bounding box, a transformation (so it's possible to make the PCell act depending on the orientation). So seen from the PCell instantation code a guiding instance won't be a real instance but rather a black box or abstract.

    Maybe I'd (optionally) provide the shapes of one (or more) selectable layers too, so you could have access to pins of a macro or an outline layer which is then not just a rectangle. But that scheme will surely be limited to few shapes only.

    And you're right: it's not possible to transfer a complete instance into the library's layout. But transferring a cell abstract is.

    But as I said, that's surely not easy to implement, so it's not a low-hanging fruit.

    Matthias

Sign In or Register to comment.