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 16

    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 18

    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

Sign In or Register to comment.