Defining DBU of newly generated layout

edited August 2023 in Ruby Scripting

I am using MATLAB to generate a series of DXF files that I am importing into KLayout, converting each file to GDS, placing a TEXT PCell of the filename within the layout, then exporting the final GDS.

My problem is that I have generated many of these DXF files in the past and the dimensions in some cases are very small - around 20nm in diameter. When I use Ruby to script the above task, I cannot figure out a way to define the DBU of a newly-generated layout before importing the DXF file. As a result, there is snapping of the geometry to grid points.

This doesn't seem to be an issue if I import these files manually after changing the DXF reader options DBU to 0.0001, but I cannot figure out how to do it (if possible) with scripting. I have tried creating a blank layout and exporting with DBU = 0.0001, then reopening that blank layout and subsequently importing the DXF, but this does not solve the problem. I could solve this by going back to MATLAB and re-exporting the DXFs with scaled dimensions, then scaling back down using Ruby, but that would take a ton of care and time. I pasted my code below.

begin

include RBA


  # INPUTS:
  out_filepath ='E:'  
  opt = SaveLayoutOptions::new
  opt.dbu = 0.0001
  dbu = opt.dbu
  um = opt.dbu*10000
  opt.gds2_libname = "LIBNAME"
  opt.format = "GDS2"

    # Get relevant handles
  app = Application.instance
  mw = app.main_window

  # 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

    # Loop through each file and instantiate it under the parent cell
  files.each_with_index { |f,i|
    p "Reading #{f}"

     ly = RBA::Layout::new()
     ly.read(f) 
     top = ly.top_cell.name


        # find the library that has the TEXT pcell
        lib = RBA::Library.library_by_name("Basic")
        lib || raise("Unknown lib 'Basic'")

        # find the pcell named TEXT
        pcell_decl = lib.layout.pcell_declaration("TEXT")
        pcell_decl || raise("Unknown PCell 'TEXT'")

        #gets the filename and shortens it to get rid of the directory, file extension, and the type of unit cell
        filename = "#{f}"
        strCut = filename.index('_')
        filename = filename[strCut+1,filename.length-(strCut+1)-4]
        filenameExt = filename + '.gds'
        ly.write(File.expand_path(out_filepath + filenameExt), opt)

        ly2 = RBA::Layout::new()
        ly2.read(File.expand_path(out_filepath + filenameExt))
        top2 = ly2.top_cell.name

        # set some sample parameters (uses layer 0)
        param = { 
          "text" => filename, 
          "layer" => RBA::LayerInfo::new(0, 0), 
          "mag" => 10
        }

        # build a param array using the param hash as a source
        pv = pcell_decl.get_parameters.collect do |p|
        param[p.name] || p.default
        end

        # create a PCell variant cell
        pcell_var = ly2.add_pcell_variant(lib, pcell_decl.id, pv)

        # instantiate that cell and move it to the top-center of the array
        t = RBA::Trans::new(RBA::Trans::R0, 0, 0)
        pcell_inst = ly2.cell(top2).insert(RBA::CellInstArray::new(pcell_var, t))
        shiftX = -dbu*pcell_inst.bbox.width/2
        shiftY = dbu*ly2.cell(top2).bbox.height/2
        padding = 2*um
        shiftY = shiftY+padding
        trans = RBA::Trans::new(RBA::Trans::R0, shiftX, shiftY)
        ly2.cell(top2).transform(pcell_inst,trans)

    ly2.write(File.expand_path(out_filepath + filenameExt), opt)

  }


end

Comments

  • IME the DBU comes from what your mask shop (or other lithography)
    can deliver. I'm used to older nodes and 0.001um (1nm) has never
    been close to a problem. But you might find that 20nm circles are a
    little raggedy (ortho structures, less so).

    Usually this choice would be nailed down before you get any or much
    layout done. Here, seems like you'd have to go to "least common
    denominator" if you want to assert a DBU that puts everything on grid.

    An alternative might be searching for "off grid" and translating
    vertices / PCell points to snap-to. But that might get messy, left
    unattended.

    You may have some manual work ahead of you. Might suggest returning
    to the generating scripts and "bake in" gridding to that process, as a
    "least incremental effort" approach (esp. if many objects come from
    one script, that's leverage).

  • Hi eherrm,

    The dbu can be modified thuough setting attribute of a Layout object

    ly = RBA::Layout::new()
    ly.dbu =  opt.dbu #add this to modify dbu
    

    https://www.klayout.de/doc-qt5/code/class_Layout.html#method54

  • Hi dick_freebird, thanks for taking the time! Yes, 20nm circles are horribly drawn with the above code. I'm working with an electron beam lithography system with a spot size of 5nm, so those raggedy edges of small circles have an effect on the proximity-effect correction and the small circles end up not getting a high enough dosage. I can load these old DXF files manually into KLayout if I make sure to set the DXF reader options correctly, but doing this for all of these generated geometries would be a nightmare.

    RawrRanger, thanks to you also for taking the time. Although your solution is one that I have tested and found doesn't work as we wanted, I took a deeper look into the link you provided and found the LoadLayoutOptions class. Turns out all I need to do is use this class to change the DXF reader options from within the script, then include these options in the initial DXF read() call. This is done in the same way the writer options are defined. The new lines of code are below. Thanks again!

      loadOpt = LoadLayoutOptions::new
      loadOpt.dxf_dbu = 0.0001
      loadOpt.dxf_unit = 1
      ...
      ly.read(f,loadOpt) 
    
  • Very good. That's exactly the way to solve this problem.

    Please note that the database uses 32 bit integers unless you compile with the 64bit coordinate option. That limits the total available area to -2e9 ... 2e9 DBUs and with smaller DBU this area gets smaller too. In reality the usable range is smaller as differences also are represented in 32bit integers. So -1e9 to 1e9 DBUs is safe which still is 200x200mm in your case. But not enough to represent a 300mm wafer if you like to go in that direction.

    Matthias

Sign In or Register to comment.