Scripting serial numbers for identical devices

I am working to fabricate a few thousand identical devices on a wafer and add a text PCELL to each device cell which will denote its unique serial number (e.g., from 0001 to 9999). Having a unique serial number is useful for diagnosing issues post fabrication.

So far, I drew up a device cell and I filled a 6 inch wafer layer with it using the Fill Tool. But, I am having trouble figuring out how to add the serial number text PCELL. Since there are many devices, I cannot do this by hand.

I have two ideas for how to script these serial numbers with python:

  1. Write a script that iterates through each device cell instance and adds a text PCELL with the serial number at the correct relative x,y position on the cell.
  2. Add a text PCELL as a child cell for the device cell. After the device cell is filled across the wafer, edit the text content for each device instance across the wafer to be the desired serial number.

Which method is better, and how would I implement it in a script? For reference, I am attaching an example GDS file showing how far I have gotten.

Side note: This problem has come up in this topic about 6 years ago. The conclusion there was a rather ugly and inconvenient solution of using excel files. I would like to instead do everything through a python script, since Klayout's python scripting has come a long way since 2015. Moreover, my actual device has 3 different subparts which each need their own serial number, so this excel solution is not robust enough.

Comments

  • edited June 2021

    Hi @fruitspunchsamurai

    The second method will not work as you cannot change a PCell per instance. You can change it only once which will make all device instances use the same number.

    So the first method is the only way to do that. The procedure is this:

    1. You'll need some empty area inside your chip. You can define the coordinates of this area by looking at the device cell and measuring the lower left corner of this area. In you sample that was x,y = -5,4 (micron). I'll assume you want to put your serial number there.
    2. You'll also need to determine the magnification value for the font so the field will fit into the empty area. In your case you used 2. I'll assume this value for the script.
    3. You'll also need a way to determine the number from the location of the chip. I'll assume that sorting the device coordinates by y first and then x can be used. This will render a numbering from left to right and bottom to top. By changing the sort function you can modify the numbering scheme.

    With these assumptions here is a script which generates the serial number PCells:


    ly = RBA::CellView.active.layout wafer = ly.top_cell # collect chips chip_coordinates = [] wafer.each_inst do |inst| if inst.cell.name == "Chip" # TODO: change for other names inst.cell_inst.each_trans do |array_trans| chip_coordinates << array_trans.disp end end end # do the sorting chip_coordinates = chip_coordinates.sort do |a,b| # This is the sorting function which will sort by y first, then by x cmp_y = a.y <=> b.y if cmp_y != 0 cmp_y else a.x <=> b.x end end # create the serial number # position of the S/N field inside the device sn_x = -5.0 sn_y = 4.0 # title field mag mag = 2.0 # layer where to put the serial number layer = RBA::LayerInfo::new(1, 0) # serial number format (A123) sn_format = "A%03d" sn = 1 chip_coordinates.each do |pt| trans = RBA::Trans::new(RBA::Vector::new(pt.x + sn_x / ly.dbu, pt.y + sn_y / ly.dbu)) sn_param = { "layer" => layer, "text" => sn_format % sn, "mag" => mag } sn_cell = ly.create_cell("TEXT", "Basic", sn_param) wafer.insert(RBA::CellInstArray::new(sn_cell.cell_index, trans)) sn += 1 end

    Matthias

    P.S: after posting this script I noticed you wanted to have Python. This script is Ruby. It's straightforward to translate except the sorting function for which no equally elegant solutions exists in Python AFAIK.

  • edited June 2021

    Thank you Matthias! Your script worked very nicely, I have converted it to Python below for others to benefit from and put it also in the attached zip file.

    PS There is a very short one liner to do the sorting in python, which IMHO is more elegant than in Ruby :)

    import pya
    
    # Instances of devices to be labeled are named "Chip"
    # and are children of the "Wafer" cell
    ly = pya.CellView().active().layout()
    wafer = ly.cell("Wafer")
    
    # Gather coordinates of each chip instance   
    chip_coordinates = []
    for inst in wafer.each_inst():
        if inst.cell.name == "Chip":
            for array_trans in inst.cell_inst.each_trans():
                    chip_coordinates.append(array_trans.disp)
    
    # Sort chip coordinates so they are ordered left-to-right and top-to-bottom (like a page in a book)
    chip_coordinates = sorted(chip_coordinates, key= lambda x: (x.y,-1*x.x), reverse=True)
    
    # Set relative position of the Serial Number on chip
    sn_x = -5
    sn_y = 4
    # Text magnification (i.e. relative font size)
    mag = 2.0
    # layer where to put the serial number
    layer = pya.LayerInfo.new(1, 0)
    # make cell to contain the serial numbers
    serial_numbers = ly.create_cell("SerialNumbers")
    sn_format = "A%03d"
    
    #Apply serial numbers for each Chipset
    sn = 1
    for pt in chip_coordinates:
        X = pt.x + rel.x  + sn_x / ly.dbu
        Y = pt.y + rel.y  + sn_y / ly.dbu
        trans = pya.Trans.new(pya.Vector.new(X,Y))
        text =  sn_format % (sn)
        sn_param = {"layer" : layer, "text" :text, "mag" : mag}
        sn_cell = ly.create_cell("TEXT", "Basic", sn_param)
        serial_numbers.insert(pya.CellInstArray.new(sn_cell.cell_index(), trans))
        sn = sn+1
    
    
    #Add the serial number text cell instance to the wafer cell
    trans = pya.Trans.new(pya.Vector.new(0,0))
    wafer.insert(pya.CellInstArray.new(serial_numbers.cell_index(), trans))
    
  • @fruitspunchsamurai Thank you for the conversion.

    But I disagree in one respect: Python's sort key feature is not a compare function, but just a key selector. I found that quite limited. If you insist on a challenge, here is the short Ruby version :)

    chip_coordinates.sort! { |a,b| [ a.y, -a.x ] <=> [ b.y, -b.x ] }
    

    With a bit of monkey patching you can even sort by Points directly:

    module RBA
      class Point
        def <=>(o)
         return self == o ? 0 : (self < o ? -1 : 1)
        end
      end
    end
    ...
    chip_coordinates.sort!
    

    I actually thought that the starship operator is provided automatically by Ruby but that's not the fact. I'll need to check that.

    Best regards,

    Matthias

  • @Matthias First off thank you for the great solution above, I was able to get it to function if the Cell(Chip) is only one level below the top layer in the hierarchy. If the cell is more than one layer lower I cannot get it to function. Please bear with me as this is my first time using macros in Klayout. I assume I need to make a change at wafer=ly.top_cell but I cant figure out how to change it. Any advice you can give here would be greatly appreciated.

  • Update: by changing line 2 to wafer = ly.cell("cell name") that allows me to move down in the hierarchy. However I still cannot get it to identify cells nested inside of other cells. Any advice on how to achieve that?

  • Hi @JWK,

    You mean, you like to have the label inside the chip cell? That is not possible as every chip has to get it's own label and a single chip cell is the same everywhere. Except if you turn every chip instance into a separate cell.

    With the original code above, a single overlay cell is generated with the IDs that covers the whole wafer. This cell has to be on top level to sit in the same coordinate system as the wafer.

    To continue this discussion, a sample and more detailed explanation of what you want to achieve was helpful.

    Matthias

  • Let me suggest that generating the "numbering" might be
    better off as a separate exercise divorced from the hierarchy.
    Like, determine the "placement grid" for texts, make a routine
    that indexes content along with setpping coords, and place
    the instance, and on to the next.

    Then place that "reticle-scale label" over the whole "real
    stuff" layout.

    I don't think you even need PCells if you have the 0 - 9
    character layouts as "0 X" data and just rack up the sub-origins
    per grid-step (maybe even easier if "0" is "000" and "1" is "001",
    no need to even figure out justification - just pick digits and
    place relative to main step grid?

Sign In or Register to comment.