Instancing one GDS file as a cell in the open file

edited April 2014 in Ruby Scripting

I wrote a script to do the following:

Given an already-open layout, choose a GDS file and instance it (the whole GDS file, assuming one top cell) as a cell in the current layout. In other words, the point is to pull in another layout as a cell within the current layout.

But there are a few strange behaviours.

First, here is the code. Here is a helper script first, that I call 'Misc tools.rb'

class MiscTools
  # To use this, add this line to your file: load File.expand_path('Misc/Misc tools.rb', $PHTOOLS)

  # Originally from: http://klayout.de/forum/comments.php?DiscussionID=103
  def self.copy_cells(lsrc, ltarget, lmap)
    citarget = nil
    # a map for the cell indices
    cmap = {}
    lsrc.each_cell_bottom_up do |cisrc|
      # create a new cell in the target layout and add to the cell index map
      csrc = lsrc.cell(cisrc)
      citarget = ltarget.add_cell(lsrc.cell_name(cisrc))
      ctarget = ltarget.cell(citarget)
      cmap[cisrc] = citarget
      # copy the shapes
      lsrc.layer_indices.each do |lisrc|
        shtarget = ctarget.shapes(lmap[lisrc])
        csrc.shapes(lisrc).each do |shape|
          newshape = shtarget.insert(shape) 
          shtarget.replace_prop_id(newshape, 0) # clear properties
        end
      end
      # translate and copy the instances
      csrc.each_inst do |inst|
        # get the instance object and create a new one with the new cell index
        i = inst.cell_inst
        trans = i.is_complex? ? i.cplx_trans : i.trans
        cinew = cmap[i.cell_index]
        if i.is_regular_array?
          ctarget.insert(RBA::CellInstArray.new(cinew, trans, i.a, i.b, i.na, i.nb))
        else
          ctarget.insert(RBA::CellInstArray.new(cinew, trans))
        end
      end
    end
    # Return the last cell's index which (because we iterated bottom up) is a top cell
    return citarget
  end

 end

And here is the actual code. Note you will need to replace the "load" line to refer to the script above.

    # Instances a gds file as a new cell in the current layout.
    # The name of the new cell is the name of the gds file, minus the ".gds"

    include RBA

    load File.expand_path('Misc/Misc tools.rb', $PHTOOLS) # Contains the helper method "copy_cells"

    layout_view = RBA::Application.instance.main_window.current_view
    if layout_view == nil
      raise "No view selected"
    end

    layout_view.transaction("Bring in external file")
    begin

      layout = Application.instance.main_window.current_view.active_cellview.layout

      file = MiscTools.choose_file()
      unless file.nil?
        load File.expand_path(file)

        layout_to_grab = Layout.new
        layout_to_grab.read(file) unless file.nil?

        lmap = {}
        linfo_to_index = {}

        layout_to_grab.layer_indices.each { |l|
          linfo = layout_to_grab.get_info(l)
          layer = layout.insert_layer(linfo)
          lmap[l] = layer;
          linfo_to_index[linfo.to_s] = layer
        }

        layout_to_grab_top = MiscTools.copy_cells(layout_to_grab, layout, lmap)
        #layout.cell(top).insert(CellInstArray.new(layout_to_grab_top, Trans.new)) #Do this line if you want to instantiate the cell in the "top" cell
      end

    ensure 
      layout_view.commit
    end

Now, here are the issues.

1) If cell "A" already exists in the open layout, and the layout we're bringing in also has cell "A", I'd like them to merge (ie assume cell "A" is identical in both cases and just use the original "A". Instead, it names the new one "A$1" to differentiate it from the old one.

2) The cell I've just brought in doesn't show up in the window (it appears blank when you press "*", although the cell is truly there when you press "-" to see the black outline). However if I then save the whole thing as GDS and re-open it, it shows up just fine now.

3) In the layout I'm bringing in, I have some PCells with guiding shapes. These are converted to non-PCells (polygons) with obviously no guiding shapes. Is there a way to do this and preserve the PCells and guiding shapes?

Note, if this is not the best way to do this, let me know!! I tried playing around with KLayout's functions to get multiple layouts into one tab ("@1", @2", ...) but couldn't for instance figure out how to "flatten" @1 and @2 so that I could instance @2 within @1. So I gave up on that.

Thanks,
David

Comments

  • edited November -1

    Ah, so now I found File > Import > Other file into current.

    This solves #2, but anyway #1 and #3 are still the same problem. (Well, #1 isn't a problem, it's a preference...)

  • edited May 2014

    Note, to solve #1, I guess I should use File > Import > Other file into current, and then choose:

    "Merge hierarchy - Merge other cells into current (attempt to identify corresponding cells)"
    

    However this doesn't work as I imagine it...

    Here's a simple use case. Create a top cell "TOP". Create a child cell "A". Instance "A" inside of "TOP". Put a box in child cell "A". Save the whole thing as TOP.gds. Also save the child cell by right-clicking in the cell tree and selecting "Save Child Cell As" > "A.gds".

    Now, when I go through the File > Import > Other file into current > Merge hierarchy, I expect nothing to happen -- it should identify that "A" already exists in the current cell tree and ignore it. However it instances the shape on cell "TOP". So, now you get two boxes in "TOP" -- one is the child cell "A" with a box in, the other is the box itself which has just been imported and plonked down, i.e. not in its own cell.

    I think I'm understanding it wrong though, or maybe the Import function is still early stages. But I imagine it should just see that it has a cell A, and it's importing a cell A with identical contents, and either just ignore it or say "I found this cell with the same name and the same contents. Ignoring."

    Also if you go in and modify A.gds manually (so now [A.gds] > [cell "A"] has different contents to [TOP.gds] > [cell TOP] > [cell "A"]) then try the above procedure, it would be nice to have it say "I found this cell by the same name with different contents. What do you want me to do? Use old cell / Use new cell / Rename old cell / Rename new cell."

    Anyway just some thoughts - let me know if I'm understanding that correctly.

  • edited November -1

    Hi David,

    you love to write text do you ... :-)

    I'll try to answer your points, but first of all, the solution you look for may be as simple as this:

    ly = RBA::Layout::new
    ly.read("file1.gds")
    ly.read("file2.gds")
    

    The second read will basically continue the first read. If it encounters a cell the second time, it will continue to fill this cell. This will also preserve and merge PCell instances of both files. If you don't want to merge all cells, you can rename certain cells between the two read's so there is no name conflict.

    Your code is basically correct, but regarding the guiding shapes, you can add an enhancement: guiding shapes are stored on special layout layers which will appear in the layer list too. You can identify that layer using Layout#guiding_shape_layer and skip that.

    However, preserving PCell instances across layouts is possible, but is a awful lot of code. You can use Cell#is_pcell_instance to identify PCell instances. You can then obtain the PCell parameters and the PCell declaration, observing that a PCell typically lives in a library, obtain a new PCell variant from that library, create a library reference etc ...

    Since version 0.23.x there is better support for copying shapes, instances and even cell trees across layouts. Have a look at the Cell#copy... method family. There are even hierarchical copy functions which allow specification of a cell mapping table. That may also provide a solution for your problem. However, I have not built in support for PCell translation, so that will not solve the PCell preservation issue currently. But I'll file that as a request.

    The display problem may be related to the layers - you are using "insert_layer" which will always create a new layer, even if the requested one already exists. The layout database allows two layers to have the same specification but different identity. In that case, only the first one is shown in the layer view and the new cell appears empty, because it has the shapes on the second layer. I realized that is a problem, so there is a method called Layout#layer since 0.23.x which only creates a layout if it does not exist yet (you see - I try to be one step ahead :-) ).

    The "import layout" functionality is probably too sophisticated. It is designed to preserve the flat layout appearance of the imported layout. Hence, I cannot simply merge cell A of the imported layout into cell A. There are various solutions: merge, if both A's provide the same instances as seen from the top cell or create new cells which represent the original hierarchy of the imported layout. None of the options will admit to modifying the gometrical relations of the cell. I guess that is what caused the confusion in your case.

    Is something missing? I think not, but I'm sure you will follow up :-)

    Best regards,

    Matthias

  • edited November -1

    Heh.. just call me an overachiever.

    I should have known you already had some simple way of doing this! :-)

    However it's almost right but not quite. Instead of just using the first- or last-read cell "A", it reads ALL cell "A"s and puts them all on top of each other in a new cell "A".

    In other words, if "A" has a box on, and you're reading 100 files, each with an identical "A" somewhere in its tree, your resultant cell "A" will look the same but have 100 boxes on top of each other. This is fine for simple layouts but increases the filesize dramatically if "A" is complex.

    Any simple way to not read in any existing cells?

  • edited May 2014

    (And if "A" is not identical between the 100 files, again it is instanced on top of each other, leading to sometimes undesired results. I suggest it might be worth having a LoadLayoutOptions option, where you choose the mode, e.g.: "Ignore cells with identical name to existing layout" / "Overwrite cells with identical name on existing layout" / "Merge cells with identical name" / "Rename new cells found to have same name" / "Rename old cells found to have the same name")

  • edited November -1

    Hi David,

    I'm a overachiever too :-)

    If the "read again" approach is too simplistic, I can offer Cell#copy_tree_shapes. There is a version of that method which takes a CellMapping object which allows to be very specific about what target cell is to be used for which source cell. Once you have two layouts you can basically decide which cell you want to copy where. The CellMapping object allows setting up a mapping using various ways and you can even modify that later or generate a mapping yourself. But there is no option to drop shapes currently - the intention is to provide a lossless transfer (however, that's an interesting extension).

    Alternatively you can code the transfer using Cell#copy_shapes or Cell#copy_shapes_tree for subcells and decide on a per-cell basis how to handle the transfer. I assume you can identify some common cells by their name (i.e som library prefix).

    Matthias

  • sbysby
    edited November -1
    Hey guys,

    I'm a little bit confused. I also want to import some cells from another gds file to my current layout.
    Is there a simple way to achieve this with the layout.read method like Matthias wrote before?

    My first idea was to merge different gds files with this code:

    http://klayout.de/forum/comments.php?DiscussionID=146

    But what I'm looking for is a File->Import->OtherFileIntoCurrent->Instantiate in ruby code.

    Daniel
  • edited November -1

    Try this:

    # Builds a layout from separate files
    # To use: Make a new layout with at least one cell, called "TOP"
    # Modify the INPUTS accordingly.
    # This script will instance other gds/oas files under "TOP".
    
    begin
    
      include RBA
    
      # INPUTS:
      save_output = true # Whether to save the resultant file automatically
      out_filepath = 'C:\filepath\\' # Put two \\'s on the end
      out_filename = 'filename.oas.gz'
      opt = SaveLayoutOptions::new
      opt.gds2_libname = "LIBNAME"
      opt.format = "OASIS"
      top_cell_name = "TOP" # The name of the cell you will instantiate the imported files in to
    
      # Get relevant handles
      app = Application.instance
      mw = app.main_window
      lv = mw.current_view
      if lv == nil; raise "No view selected"; end
      master_layout = lv.active_cellview.layout
      top_cell_idx = lv.active_cellview.cell.cell_index
    
      # Ask the user for the files
      dialog = QFileDialog::new(mw)
      dialog.setDirectory(QDir::homePath())
      dialog.setFileMode(QFileDialog::ExistingFiles)
      if (dialog.exec())
        files = dialog.selectedFiles()
      end
    
      # Create a progress bar
      progress = RelativeProgress::new("Loading...", files.length)
    
      # Loop through each file and instantiate it under the parent cell
      files.each_with_index { |f,i|
        p "Reading #{f}"
        master_layout.read(f) # This reads it in but instantiates it as new top cells rather than child cells under the parent cell.
    
        # Find the just-read-in layout
        top_cells = master_layout.top_cells
        top_cells.each { |cell|
          next if cell.name == top_cell_name # Skip the parent cell
    
          # Figure out where to place each one. You can modify this line.
          trans = Trans.new(Point.new(0,0))
    
          # Instantiate the other cells under the parent cell
          master_layout.cell(top_cell_idx).insert(CellInstArray.new(cell.cell_index, trans))
    
        }
    
        progress.inc
      }
    
      if save_output
        p "Writing output file..."
        master_layout.write(File.expand_path(out_filepath + out_filename), opt)
        p "Done!"
      end
    
    ensure
      progress.destroy
    end
    
  • edited November -1

    Hi all,

    thanks for the code pasted. Basically the method will work. There is a little issue here: if the layout read contains cells with the same name than any cell in the present layout (here: "master_layout"), then the read method will merge the content of both cells rather than creating two different cells. Another issue arises if the database unit of the imported layout is different from the master layout.

    That effect is intended, but it's pretty dangerous. A more elaborate method which avoids these issues is the following:

    • create a new cell in the master layout. Instantiate this. This is the cell "target"
    • create a new layout
    • read the file into this new layout ("newLayout")
    • copy over the shapes and cell tree from that new layout into the master layout using Cell#copy_tree: "target.copy_tree(newLayout.top_cell)"

    Matthias

Sign In or Register to comment.