Creating letters

edited March 2014 in Ruby Scripting
Hi Matthias,

I started to use your software and found it awesome. However, I have a problem with creating characters. I could not find the way of converting texts into PCells. So I tried to use another way suggested here; http://klayout.de/forum/comments.php?DiscussionID=230&page=1#Item_0

But, I could not change the size of letters with this way. I want to create text patterns with several points and in a particular size. Could you help me out?

Thanks in advance.

Comments

  • edited November -1

    Hi ayata,

    this should be possible without having to use a script.

    • Create an new instance
    • Choose cell "TEXT" from the "Basic" library.
    • On the PCell tab enter the text, select the layer and choose a magnification (that will control the size)

    And then place the cell where you want to have it. You'll get an instance of a PCell which you can later edit to change the text etc.

    Matthias

  • edited March 2014
    Hi Matthias,

    Thank you for your quick response.

    Actually I want to create text objects by using scripts because I need to write texts at many different place. How do you choose many TEXT cell and convert them into PCell, or do you have any other ways to create letters programmatically?


    Thanks,
    Ayata
  • edited March 2014

    Hi Ayata,

    Well, if your goal is to create texts through scripting, then you might consider coding the text generation individually. You'll need a function that produces a polygon object for each letter or digit and apply that function to generate the texts.

    Using PCells has some advantage if you want to edit the text later and you like the font provided (you can install another font if necessary, but that will complicate deployment of the script). Here is some sample code how to instantiate a text PCell in a freshly created layout:

    ly = RBA::Layout.new
    top = ly.create_cell("TOP")
    
    # find the lib
    lib = RBA::Library.library_by_name("Basic")
    lib || raise("Unknown lib 'Basic'")
    
    # find the pcell
    pcell_decl = lib.layout.pcell_declaration("TEXT")
    pcell_decl || raise("Unknown PCell 'TEXT'")
    
    # set some sample parameters (uses GDS layer 10/0):
    param = { 
      "text" => "KLAYOUT RULES", 
      "layer" => RBA::LayerInfo::new(10, 0), 
      "mag" => 2.5 
    }
    
    # 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 = ly.add_pcell_variant(lib, pcell_decl.id, pv)
    
    # instantiate that cell
    t = RBA::Trans::new(RBA::Trans::r90, 0, 0)
    pcell_inst = top.insert(RBA::CellInstArray::new(pcell_var, t))
    
    # write the output
    ly.write("pcells.gds")
    

    BTW, from the PCellDeclaration object you can obtain a list of parameter names, types, default values etc.:

    type2s = { }
    %w/TypeBoolean TypeDouble TypeInt TypeLayer TypeList TypeNone TypeShape TypeString/.each do |t|
      type2s[RBA::PCellParameterDeclaration::const_get(t)] = t
    end
    pcell_decl.get_parameters.each do |pd|
      if !pd.readonly?
        puts "#{pd.name}, #{type2s[pd.type]}, #{pd.unit}, #{pd.default}, #{pd.description}, #{pd.hidden?}, #{pd.readonly?}"    
      end   
    end
    

    For the TEXT PCell this will give the following

    text, TypeString, , , Text, false, false
    font, TypeInt, , 0, Font, false, false
    layer, TypeLayer, , , Layer, false, false
    mag, TypeDouble, , 1.0, Magnification, false, false
    inverse, TypeBoolean, , false, Inverse, false, false
    bias, TypeDouble, micron, 0.0, Bias, false, false
    cspacing, TypeDouble, micron, 0.0, Additional character spacing, false, false
    lspacing, TypeDouble, micron, 0.0, Additional line spacing, false, false
    

    If you compare thae description strings to the parameters presented in the user interface, you can match manually set parameters to the names you need to use for the parameter hash.

    Matthias

  • edited November -1

    Thanks, Matthias!! Finally I solved the problem, I really appreciate your help.

    Ayata

  • edited November -1
    Hi
    After create a PCell variant cell "pcell_var ", any way we can put the letters right on a already opened layout? (nt intend to save the letters into gds.

    The reason I asked about this is I want to create letters on specific location with different letters on a already opened gds.
  • edited November -1

    Hello,

    yes, surely. The sample is just a demo. "ly" is the layout object and instead of using a fresh one you can use the currently loaded one:

    cv = RBA::CellView::active
    ly = cv.layout 
    top = cv.cell
    ...
    

    Matthias

  • edited November -1
    Thanks, it works.
    However, I got another question.
    I have been using peice of codes I found in the forum to generate test layouts.
    And now the ruby script is became vary compliated.
    I plan to do a 50K pattern split, but....I use ecxel to modify 500K row of the following codes.
    Then copy/paste 50K row codes in to macro development interface. But simply copy/paste takes me 20min.
    While I try to generate "500K" row codes, excel just crashed....
    Any chance you can help me to optimize my method?


    #The whole thing is "one" row of the coding. because
    #=================================================================
    #aear 1
    main_window = RBA::Application::instance.main_window ;
    layout = main_window.create_layout(1).layout ;
    layout_view = main_window.current_view ;
    layout.dbu =0.001;
    layer_index = layout.insert_layer(RBA::LayerInfo::new(999, 0));
    cell_index = layout.add_cell("TOP") ; cell = layout.cell(cell_index) ;
    unit_cell_index = layout.add_cell("1");
    unit_cell = layout.cell(unit_cell_index);
    unit_cell.shapes(layer_index).insert(RBA::Box::new(0, 0, 0, 0));
    nx = 1 ;dx = 1;ny = 1 ;dy = 1;ox = 0;oy = 0;
    trans = RBA::Trans::new(RBA::Point::new(ox, oy));
    ax = RBA::Point::new(dx, 0);ay = RBA::Point::new(0, dy);
    cell.insert(RBA::CellInstArray::new(unit_cell_index, trans, ax, ay, nx, ny));
    layout_view.select_cell(cell_index, 0);layout_view.add_missing_layers;layout_view.zoom_fit;#
    # Actually area 1 didn't generate anything, I add it because if I didn't add it, nothing will be generated.
    #==================================================================
    layout_view = main_window.current_view;layout.dbu =0.001;
    layer_index = layout.insert_layer(RBA::LayerInfo::new(15,99));
    cell = layout.cell(cell_index);
    mw = RBA::Application::instance.main_window;view = mw.current_view || raise("No view open");
    cell = view.active_cellview.cell

    A1 = [ RBA::Point::new(-4500,-4500),
    RBA::Point::new(-4400,-4500),
    RBA::Point::new(-4400,-4420),
    RBA::Point::new(-4300,-4420),
    RBA::Point::new(-4300,-4500),
    RBA::Point::new(-4200,-4500)];
    D1 = [ RBA::Point::new(-4350,-4455),
    RBA::Point::new(-4350,-4515)];
    E1 = [ RBA::Point::new(-4450,-4465),
    RBA::Point::new(-4450,-4405)];
    unit_cell_index = layout.add_cell("1");
    unit_cell = layout.cell(unit_cell_index);
    unit_cell.shapes(layer_index).insert(RBA::Path::new(A1,30));
    unit_cell.shapes(layer_index).insert(RBA::Path::new(D1,30));
    unit_cell.shapes(layer_index).insert(RBA::Path::new(E1,30));
    unit_cell.shapes(layer_index).insert(RBA::Box::new(-4367.5, -4457.5, -4362.5, -4452.5));
    unit_cell.shapes(layer_index).insert(RBA::Box::new(-4337.5, -4457.5, -4332.5, -4452.5));
    unit_cell.shapes(layer_index).insert(RBA::Box::new(-4467.5, -4467.5, -4462.5, -4462.5));
    unit_cell.shapes(layer_index).insert(RBA::Box::new(-4437.5, -4467.5, -4432.5, -4462.5));
    unit_cell.shapes(layer_index).insert(RBA::Box::new(-4385, -4435, -4385, -4435));
    unit_cell.shapes(layer_index).insert(RBA::Box::new(-4315, -4435, -4315, -4435));
    unit_cell.shapes(layer_index).insert(RBA::Box::new(-4415, -4405, -4415, -4405));
    unit_cell.shapes(layer_index).insert(RBA::Box::new(-4285, -4405, -4285, -4405));
    nx = 45;dx = 200;ny = 28.125;dy = 320;ox = 1;oy = 1;
    trans = RBA::Trans::new(RBA::Point::new(ox, oy));
    ax = RBA::Point::new(dx, 0);
    ay = RBA::Point::new(0, dy);
    cell.insert(RBA::CellInstArray::new(unit_cell_index, trans, ax, ay, nx, ny));
    layout_view.select_cell(cell_index, 0);
    layout_view.add_missing_layers;layout_view.zoom_fit;cv = RBA::CellView::active;ly = cv.layout ;top = cv.cell;
    lib = RBA::Library.library_by_name("Basic");lib || raise("Unknown lib 'Basic'");
    pcell_decl = lib.layout.pcell_declaration("TEXT");
    pcell_decl || raise("Unknown PCell 'TEXT'");
    param = { "text" => "P50M30L50T2S20BOX5S0", "layer" => RBA::LayerInfo::new(15,99), "mag" =>1};
    pv = pcell_decl.get_parameters.collect do |p|param[p.name] || p.default end;
    pcell_var = ly.add_pcell_variant(lib, pcell_decl.id, pv);
    t = RBA::Trans::new(RBA::Trans::r0,-5499,-5499);
    pcell_inst = top.insert(RBA::CellInstArray::new(pcell_var, t));#
  • edited November 2014

    Hi,

    I think the main problem is the big chunk of code you will generate in this setup.

    I don't know precisely the variables in your scheme, so I can only guess what the Excel sheet will do. But I'd suggest to put as much computation in the script as possible.

    Instead of generating code which produces layout, you can generalize the layout generation code. Here is a sample showing what I mean:

    class Generator
    
      def initialize
    
        main_window = RBA::Application::instance.main_window
        @layout = main_window.create_layout(1).layout
        @layout.dbu =0.001
        @layout_view = main_window.current_view
    
        @top = @layout.create_cell("TOP")
        @layer_index = @layout.insert_layer(RBA::LayerInfo::new(15,99))
    
        @lib = RBA::Library.library_by_name("Basic")
        @lib || raise("Unknown lib 'Basic'");
        @pcell_decl = @lib.layout.pcell_declaration("TEXT");
        @pcell_decl || raise("Unknown PCell 'TEXT'");
    
      end
    
      def make_pattern(l, w, xpos, ypos)
    
        a1 = [ 
          RBA::Point::new(0, 0),
          RBA::Point::new(100, 0),
          RBA::Point::new(100, l + w),
          RBA::Point::new(200, l + w),
          RBA::Point::new(200, 0),
          RBA::Point::new(300, 0)
        ]
    
        y1 = l + w / 2 - 20
        x1 = 150
        d1 = [ RBA::Point::new(x1, y1), RBA::Point::new(x1, -w / 2) ]
    
        y2 = w / 2 + 20
        x2 = 50
        e1 = [ RBA::Point::new(x2, y2), RBA::Point::new(x2, l + w + w / 2) ]
    
        unit_cell = @layout.create_cell("1")
        unit_cell.shapes(@layer_index).insert(RBA::Path::new(a1, w))
        unit_cell.shapes(@layer_index).insert(RBA::Path::new(d1, w))
        unit_cell.shapes(@layer_index).insert(RBA::Path::new(e1, w))
        unit_cell.shapes(@layer_index).insert(RBA::Box::new(x1 - w / 2 - 2, y1 - 2, x1 - w / 2 + 2, y1 + 2))
        unit_cell.shapes(@layer_index).insert(RBA::Box::new(x1 + w / 2 + 2, y1 - 2, x1 + w / 2 + 2, y1 + 2))
        unit_cell.shapes(@layer_index).insert(RBA::Box::new(x2 - w / 2 - 2, y2 - 2, x2 - w / 2 + 2, y2 + 2))
        unit_cell.shapes(@layer_index).insert(RBA::Box::new(x2 + w / 2 + 2, y2 - 2, x2 + w / 2 + 2, y2 + 2))
    
        nx = 45
        dx = 200
        ny = 28
        dy = 320
        ox = xpos
        oy = ypos
    
        trans = RBA::Trans::new(RBA::Point::new(ox, oy))
        ax = RBA::Point::new(dx, 0)
        ay = RBA::Point::new(0, dy)
        @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans, ax, ay, nx, ny))
    
        param = { 
          "text" => ("P50M30L%02dT2S20BOX5S0" % l), 
          "layer" => RBA::LayerInfo::new(15,99), 
          "mag" => 1
        }
        pv = @pcell_decl.get_parameters.collect do |p| 
          param[p.name] || p.default 
        end
        pcell_var = @layout.add_pcell_variant(@lib, @pcell_decl.id, pv)
        t = RBA::Trans::new(RBA::Trans::r0, -1000 + xpos, -1000 + ypos)
        @top.insert(RBA::CellInstArray::new(pcell_var, t))
    
      end
    
      def done
        @layout_view.select_cell(@top.cell_index, 0)
        @layout_view.add_missing_layers
        @layout_view.zoom_fit
      end
    
    end
    
    # =================================================================
    
    g = Generator::new
    g.make_pattern(50, 30, 0, 0)
    g.make_pattern(70, 30, 15000, 0)
    g.make_pattern(90, 30, 0, 15000)
    g.make_pattern(110, 30, 15000, 15000)
    g.done
    

    The final code will be a number of calls of the "make_pattern" method of the Generator object. In the end the actual generation boils do one line per pattern. You could also read the parameters from a file, so the number of pattern will virtually be unlimited.

    Please note that the formulas above are a wild guess. But they hopefully illustrate the idea.

    Matthias

  • edited November -1
    Thanks Matthias
    Currently running a 500K row of g.make_pattern split on a qual core windows x64 16gb RAM system for more than 24 hours.
    I am so worried that Klayout will stop due to lack of memory. If windows failed, then I will try to use the corporate linux server to see if I can make it.

    https://www.dropbox.com/s/j0vp5t1kna15ejw/Klayout.jpg?dl=0

    Anyway, big thanks to you, Matthias
  • edited November -1

    Hi,

    something is going wrong here. It should not take that long and take that much memory.

    I'll have a look.

    Matthias

  • edited November 2014

    Hi,

    I had a look at that problem and I can confirm the runtimes.

    The reason seems to be the PCell cache which grows due to many PCell instances with different texts. It's not optimized for a huge number of variants and I will try to solve that issue in the next major release.

    Since then there is a little trick we can use. Instead of instantiating a PCell, we can use the PCell code to generate the layout. So we basically fake a PCell without the cache overhead.

    Here is the part of the code which will change:

    param = { 
      "text" => "REPLACE WITH YOUR TEXT!",
      "layer" => RBA::LayerInfo::new(15,99), 
      "mag" => 1
    }
    pv = @pcell_decl.get_parameters.collect do |p| 
      param[p.name] || p.default 
    end
    
    # fake a PCell by creating a normal cell and using the production code
    # of the PCell to generate the layout
    text_cell = @layout.create_cell("1T")
    @pcell_decl.produce(@layout, [ @layer_index ], pv, text_cell)
    
    t = RBA::Trans::new(RBA::Trans::r0, -1000 + xpos, -1000 + ypos)
    @top.insert(RBA::CellInstArray::new(text_cell.cell_index, t))
    

    For generating huge layouts, I'd recommend to use KLayout in batch mode and not do that in the UI. Running a script in the IDE and using a layout attached to a display will always create some overhead.

    You can use a modified version of the script to create, fill and write a layout. Below you find a sample.

    If you save that script to "generate.rb" for example, you can run it in batch mode this way:

    klayout -b -r c:\users\myself\generate.rb

    Since KLayout is not built as a console application on Windows, you won't see any output however. Please also note, that -r option wants to see a full path currently.

    With that setup I was able to produce 500k cells within 5 Minutes on a ancient Core Duo on Ubuntu 12.06. The output was a decent 190MB OASIS file. Memory footprint was about 1.7G on 32bit, on 64bit it's probably somewhat bigger.

    I added the final "exit!" statement at the end of the script - I noticed that Ruby appears to have a hard time doing the final cleanup in the garbage collector. The "exit!" cuts that off and saves the time for waiting for the GC.

    Regards,

    Matthias

    class Generator
    
      def initialize
    
        @layout = RBA::Layout::new
        @layout.dbu =0.001
    
        @top = @layout.create_cell("TOP")
        @layer_index = @layout.insert_layer(RBA::LayerInfo::new(15,99))
    
        @lib = RBA::Library.library_by_name("Basic")
        @lib || raise("Unknown lib 'Basic'");
        @pcell_decl = @lib.layout.pcell_declaration("TEXT");
        @pcell_decl || raise("Unknown PCell 'TEXT'");
    
      end
    
      def make_pattern(l, w, xpos, ypos, num)
    
        a1 = [ 
          RBA::Point::new(0, 0),
          RBA::Point::new(100, 0),
          RBA::Point::new(100, l + w),
          RBA::Point::new(200, l + w),
          RBA::Point::new(200, 0),
          RBA::Point::new(300, 0)
        ]
    
        y1 = l + w / 2 - 20
        x1 = 150
        d1 = [ RBA::Point::new(x1, y1), RBA::Point::new(x1, -w / 2) ]
    
        y2 = w / 2 + 20
        x2 = 50
        e1 = [ RBA::Point::new(x2, y2), RBA::Point::new(x2, l + w + w / 2) ]
    
    
        unit_cell = @layout.create_cell("1")
        unit_cell.shapes(@layer_index).insert(RBA::Path::new(a1, w))
        unit_cell.shapes(@layer_index).insert(RBA::Path::new(d1, w))
        unit_cell.shapes(@layer_index).insert(RBA::Path::new(e1, w))
        unit_cell.shapes(@layer_index).insert(RBA::Box::new(x1 - w / 2 - 2, y1 - 2, x1 - w / 2 + 2, y1 + 2))
        unit_cell.shapes(@layer_index).insert(RBA::Box::new(x1 + w / 2 + 2, y1 - 2, x1 + w / 2 + 2, y1 + 2))
        unit_cell.shapes(@layer_index).insert(RBA::Box::new(x2 - w / 2 - 2, y2 - 2, x2 - w / 2 + 2, y2 + 2))
        unit_cell.shapes(@layer_index).insert(RBA::Box::new(x2 + w / 2 + 2, y2 - 2, x2 + w / 2 + 2, y2 + 2))
    
        nx = 45
        dx = 200
        ny = 28
        dy = 320
        ox = xpos
        oy = ypos
    
        trans = RBA::Trans::new(RBA::Point::new(ox, oy))
        ax = RBA::Point::new(dx, 0)
        ay = RBA::Point::new(0, dy)
        @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans, ax, ay, nx, ny))
    
        param = { 
          "text" => ("%02d-%06d" % [ l, num ]),    # REPLACE WITH YOUR TEXT!
          "layer" => RBA::LayerInfo::new(15,99), 
          "mag" => 1
        }
        pv = @pcell_decl.get_parameters.collect do |p| 
          param[p.name] || p.default 
        end
    
        # fake a PCell by creating a normal cell and using the production code
        # of the PCell to generate the layout
        text_cell = @layout.create_cell("1T")
        @pcell_decl.produce(@layout, [ @layer_index ], pv, text_cell)
    
        t = RBA::Trans::new(RBA::Trans::r0, -1000 + xpos, -1000 + ypos)
        @top.insert(RBA::CellInstArray::new(text_cell.cell_index, t))
    
      end
    
      def done(filename)
        @layout.write(filename)
        puts "Output file written: "+filename
      end
    
    end
    
    # =================================================================
    
    g = Generator::new
    ncolumns = 1000
    nrows = 500
    num = 1
    ncolumns.times do |i|
      puts "Column #{i+1}/#{ncolumns} .."
      nrows.times do |j|
        g.make_pattern(50 + (i + j) % 73, 30, 15000 * i, 15000 * j, num)
        num = num + 1
      end
    end
    g.done("test.oas")
    
    # See comments
    exit!
    
  • edited November -1
    Your new coding is fantastic !!
    Total run time on my windows is less than 10min, the oasis file is around 557MB.
    However, during the 10min processing time, the physical memory still need around 14GB.

    Got another question, i like the way you wrote

    g = Generator::new
    ncolumns = 1000
    nrows = 500
    num = 1
    ncolumns.times do |i|
    puts "Column #{i+1}/#{ncolumns} .."
    nrows.times do |j|
    g.make_pattern(50 + i, 30, 15000 * i, 15000 * j, num)
    num = num + 1
    end
    end

    For the part of 50 + i, it will run like 50, 51, 52....etc
    What if I need 50, 51,53, 57, 60, 100, 200.... (in various spacing between numbers)?


    Again, great software, with a great author Matthias!!
  • edited November 2014

    Thank you :-)

    14G is still a lot, but I think that is what it takes. Maybe it's possible to reduce that - I guess the sample above is not representative, so I'll have to do a little guessing. Here are some thoughts:

    • Cells (not instances) create considerable overhead. If you try to avoid cells, you can probably reduce the memory footprint. For example, you could create one cell per letter (one cell for "A", one for "B" etc.) and digit and create the texts by placing the respective cells one by one, rather than creating one cell for each label. I assume there is no way to avoid the unit cells, unless there is some similarity in the pattern you can exploit.
    • It's possible that Ruby objects pile up while the garbage collector is waiting to be triggered again. You could insert "GC.start" to run the garbage collector explicitly (not too often, maybe every 1000th block) and check whether that has an effect.

    Regarding the question about the sequence: I'd suggest that you create some control file which provides the input. For example, create an Excel table with the following format:

    w      l      xpos    ypos
    50     30     0       0
    51     30     15000   0
    ...
    

    Save that as a CSV file to "control.csv" and let this file control the generation with a main loop like this:

    header_seen = nil
    File.open("c:\\users\\me\\control.csv", "r") do |file|
      file.each_line do |l|
        if header_seen
          items = l.split(",")
          items.size == 4 && g.make_pattern(*items.collect { |i| i.to_f }) 
        end
        header_seen = true
      end
    end
    

    Regards,

    Matthias

  • edited November -1
    If I use the cvs, then I am going back to the stage of 500K row of coding :)

    Here is the coding which need the memory of 14gb
    Again, this is just collection of everything I found in the forum, so thank you and all the other users.

    Another funny issue here is
    " "text" => ("%03d-%03d-%03d-%03d-%03d-%03d" % [ a, b,c,d,e,f,g ]), # REPLACE WITH YOUR TEXT!
    "
    This line of letter is too line for a 10um width pattern, any way to cut them into to line?
    like abcdefg==> abc
    defg


    ==================================================

    class Generator

    def initialize

    @layout = RBA::Layout::new

    @layout.dbu =0.001
    @top = @layout.create_cell("TOP")
    @layer_index = @layout.insert_layer(RBA::LayerInfo::new(15,99))
    @lib = RBA::Library.library_by_name("Basic")
    @lib || raise("Unknown lib 'Basic'");
    @pcell_decl = @lib.layout.pcell_declaration("TEXT");
    @pcell_decl || raise("Unknown PCell 'TEXT'");
    end

    def make_pattern( a , b , c , d , e , f , g , xpos , ypos )
    dx = 4*a+4*b
    dy = 2*c+2*d
    arx = (10000*0.9*0.5/dx).to_i
    ary = (10000*0.8*0.5/dy).to_i
    nx = arx
    ny = ary
    a1 = [
    RBA::Point::new( 0 , 0 ),
    RBA::Point::new( 2*b+1.5*a , 0 ),
    RBA::Point::new( 2*b+1.5*a , a+c ),
    RBA::Point::new( 2*b+1.5*a+2*a+2*b , a+c ),
    RBA::Point::new( 2*b+1.5*a+2*a+2*b , a*-0.5 ),
    ]

    d1 = [
    RBA::Point::new( b+0.5*a , a*0.5+d ),
    RBA::Point::new( b+0.5*a , a*0.5+d+c ),
    ]

    e1 = [
    RBA::Point::new( 3*b+2.5*a , c-d+0.5*a ),
    RBA::Point::new( 3*b+2.5*a , 0.5*a-d-c-d ),
    ]
    b1x = b + e
    b1y = a*0.5+d + f
    b2x = b+a - e
    b2y = a*0.5+d + f
    b3x = 0 + e
    b3y = 0.5*a + f
    b4x = a+2*b - e
    b4y = 0.5*a + f
    b5x = 3*b+2*a + e
    b5y = c-d+0.5*a + f
    b6x = 3*b+3*a - e
    b6y = c-d+0.5*a + f
    b7x = 2*b+2*a + e
    b7y = 0.5*a+c + f
    b8x = 4*b+3*a - e
    b8y = 0.5*a+c + f
    unit_cell = @layout.create_cell("1")
    unit_cell.shapes(@layer_index).insert(RBA::Path::new(a1, a))
    unit_cell.shapes(@layer_index).insert(RBA::Path::new(d1, a))
    unit_cell.shapes(@layer_index).insert(RBA::Path::new(e1, a))
    unit_cell.shapes(@layer_index).insert(RBA::Box::new( b1x-0.5*g , b1y-0.5*g ,
    b1x+0.5*g , b1y+0.5*g ))
    unit_cell.shapes(@layer_index).insert(RBA::Box::new( b2x-0.5*g , b2y-0.5*g ,
    b2x+0.5*g , b2y+0.5*g ))
    unit_cell.shapes(@layer_index).insert(RBA::Box::new( b3x-0.5*g , b3y-0.5*g ,
    b3x+0.5*g , b3y+0.5*g ))
    unit_cell.shapes(@layer_index).insert(RBA::Box::new( b4x-0.5*g , b4y-0.5*g ,
    b4x+0.5*g , b4y+0.5*g ))
    unit_cell.shapes(@layer_index).insert(RBA::Box::new( b5x-0.5*g , b5y-0.5*g ,
    b5x+0.5*g , b5y+0.5*g ))
    unit_cell.shapes(@layer_index).insert(RBA::Box::new( b6x-0.5*g , b6y-0.5*g ,
    b6x+0.5*g , b6y+0.5*g ))
    unit_cell.shapes(@layer_index).insert(RBA::Box::new( b7x-0.5*g , b7y-0.5*g ,
    b7x+0.5*g , b7y+0.5*g ))
    unit_cell.shapes(@layer_index).insert(RBA::Box::new( b8x-0.5*g , b8y-0.5*g ,
    b8x+0.5*g , b8y+0.5*g ))
    ox = xpos
    oy = ypos
    trans1 = RBA::Trans::new(0, true, RBA::Point::new(ox, oy+c+a,))
    trans2 = RBA::Trans::new(0, false, RBA::Point::new(ox, oy-c-a,))
    ax = RBA::Point::new(dx, 0)
    ay = RBA::Point::new(0, dy*2)

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, ax, ay, nx, ny/2))

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, ax, ay, nx, ny/2))

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, -ax, -ay, nx, ny/2))

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, -ax, -ay, nx, ny/2))

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, -ax, ay, nx, ny/2))

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, -ax, ay, nx, ny/2))

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, ax, -ay, nx, ny/2))

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, ax, -ay, nx, ny/2))

    param = {
    "text" => ("%03d-%03d-%03d-%03d-%03d-%03d" % [ a, b,c,d,e,f,g ]), # REPLACE WITH YOUR TEXT!
    "layer" => RBA::LayerInfo::new(15,99),
    "mag" => 0.1
    }
    pv = @pcell_decl.get_parameters.collect do |p|
    param[p.name] || p.default
    end

    # fake a PCell by creating a normal cell and using the production code
    # of the PCell to generate the layout
    text_cell = @layout.create_cell("1T")
    @pcell_decl.produce(@layout, [ @layer_index ], pv, text_cell)

    t = RBA::Trans::new(RBA::Trans::r0, -4000 + xpos, -4000 + ypos)
    @top.insert(RBA::CellInstArray::new(text_cell.cell_index, t))

    end

    def done(filename)
    @layout.write(filename)
    puts "Output file written: "+filename
    end

    end

    # =================================================================
    g = Generator::new


    #====================

    ncolumns =3

    nrows = 3

    num1 = 1

    ncolumns.times do |nc|

    puts "Column #{nc+1}/#{ncolumns} .."


    nrows.times do |nr|

    #====================

    kcolumns = 3

    krows = 3

    num2 = 1

    kcolumns.times do |kc|

    puts "Column #{kc+1}/#{kcolumns} .."


    krows.times do |kr|

    #====================

    lcolumns =3

    lrows = 3

    num3 = 1

    lcolumns.times do |lc|

    puts "Column #{lc+1}/#{lcolumns} .."


    lrows.times do |lr|

    #====================

    g.make_pattern( 30+nc*4 , 40+nr*2 , 120 , 30+kr*2 , -8+lc*4 , -8+lr*4 ,5+kc*5, 10000*nc+10000*(ncolumns+1)*kc+10000*(ncolumns+1)*(kcolumns+1)*lc , 10000*nr+10000*(nrows+1)*kr+10000*(nrows+1)*(krows+1)*lr )


    #====================

    num1 = num1 + 1
    num2 = num2 + 1
    num3 = num3 + 1

    end

    end

    end

    end
    end

    end

    g.done("60.oas")
  • edited November -1
    Forgot to mention,
    The actual numbers are these. And I just realize....the combination is 1 million...


    ncolumns =20
    nrows = 20

    kcolumns = 5

    krows = 20

    lcolumns =5

    lrows = 5
  • edited November 2014

    Hi mattseng,

    I conducted some experiments on your code and my findings are that the text cells are a major contribution.

    You can bring that down by using the letter-by-letter approach. Specifically, in the initialization code you create cells with a single letter:

    def initialize
    
      @layout = RBA::Layout::new
    
      @layout.dbu =0.001
    
      @top = @layout.create_cell("TOP")
      @layer_index = @layout.insert_layer(RBA::LayerInfo::new(15,99))
    
      @lib = RBA::Library.library_by_name("Basic")
      @lib || raise("Unknown lib 'Basic'");
    
      @pcell_decl = @lib.layout.pcell_declaration("TEXT");
      @pcell_decl || raise("Unknown PCell 'TEXT'");
    
      @chars = {}
    
      # create one cell for "-" and the digits
      "0123456789-".split("").each do |c|
    
        param = {
        "text" => c,
        "layer" => RBA::LayerInfo::new(15,99),
        "mag" => 0.1
        }
        pv = @pcell_decl.get_parameters.collect do |p|
        param[p.name] || p.default
        end
    
        # fake a PCell by creating a normal cell and using the production code
        # of the PCell to generate the layout
        char_cell = @layout.create_cell("C#{c}")
        @pcell_decl.produce(@layout, [ @layer_index ], pv, char_cell)
    
        @chars[c] = char_cell
    
      end
    
    end
    

    then the text production code becomes (note the \n which is a line break):

    text = ("%03d-%03d-%03d\n%03d-%03d-%03d" % [ a, b, c, d, e, f, g ])
    
    xt0 = xt = -4000 + xpos
    yt = -4000 + ypos
    
    text.split("").each do |tc|
      if tc == "\n"
        xt = xt0
        yt -= 90
      else
        char_cell = @chars[tc]
        if char_cell
          @top.insert(RBA::CellInstArray::new(char_cell.cell_index, RBA::Trans::new(RBA::Trans::r0, xt, yt)))
          xt += 60
        end
      end
    end
    

    I ran this scheme on 10% of your full setup and brought the memory down to a little more than half of the original size. So my guess is that this scheme will take 8G of memory instead of 14G. Runtimes will be comparable and output file size should be a little less.

    Regards,

    Matthias

  • edited November -1
    Fantastic!! This way really reduce the memory needs.


    btw, I am being dumb again. Please help me.
    I tried to document every pattern's variables and coordinates....
    But it seems f.puts really just put the charters in the "myfile.txt" , not series of numbers of variables.



    #====================

    g.make_pattern( 10 , 10 , 10 , 10, 10, 5000*nc+5000*ncolumns*kc+5000*ncolumns*kcolumns*lc+5000*ncolumns*kcolumns*lcolumns*mc , 5000*nr+5000*nrows*kr+5000*nrows*krows*lr+5000*nrows*krows*lrows*mr )

    open('myfile.txt', 'w') { |f|
    f.puts "10 , 10 , 10 , 10, 10, 5000*nc+5000*ncolumns*kc+5000*ncolumns*kcolumns*lc+5000*ncolumns*kcolumns*lcolumns*mc , 5000*nr+5000*nrows*kr+5000*nrows*krows*lr+5000*nrows*krows*lrows*mr "
    }
    #====================
  • edited November -1
    umm....I found a way to make it print out the numbers of variables.
    But it only prints out the last set . /_\
    #====================

    g.make_pattern( 10 , 10 , 10 , 10, 10, 5000*nc+5000*ncolumns*kc+5000*ncolumns*kcolumns*lc+5000*ncolumns*kcolumns*lcolumns*mc , 5000*nr+5000*nrows*kr+5000*nrows*krows*lr+5000*nrows*krows*lrows*mr )

    open('myfile.txt', 'w') { |f|
    f.puts nc , nr , lc+kr , lr, kc, 5000*nc+5000*ncolumns*kc+5000*ncolumns*kcolumns*lc+5000*ncolumns*kcolumns*lcolumns*mc , 5000*nr+5000*nrows*kr+5000*nrows*krows*lr+5000*nrows*krows*lrows*mr
    }
    #====================
  • edited November -1

    Hi Mattseng,

    You have to open the file before the loop and put the loop into the block after the open :-)

    To write the values, there are many ways to do that in Ruby. Here's some "Rubyish" way:

    values = [ nc, nr, lc+kr, lr, kc, 5000*nc+5000*ncolumns*kc+... ]
    f.puts(values.collect { |v| v.to_s }.join(","))
    

    Regarding your code, I'd try not to duplicate the formulas for easy maintenance of the script:

    xpos = 5000*nc+5000*ncolumns*kc+5000*ncolumns*kcolumns*lc+5000*ncolumns*kcolumns*lcolumns*mc
    ypos = 5000*nr+5000*nrows*kr+5000*nrows*krows*lr+5000*nrows*krows*lrows*mr
    g.make_pattern(..., xpos, ypos)
    values = [ ..., xpos, ypos ]
    f.puts(values.collect { |v| v.to_s }.join(","))
    

    Matthias

  • edited November -1
    The script is working well :)
    However, I want to make it better, so I hope you can advice me how to modify it.

    1. the mirror part:
    In order to make the pattern is located at the center of each macro regardless of the pitch (to help who needs to measure these patterns)
    I kind find a way to mirror the pattern, but I think it is not the best way to do so.
    #====================== where I did mirror
    trans1 = RBA::Trans::new(0, true, RBA::Point::new(ox, oy+c+a,))
    trans2 = RBA::Trans::new(0, false, RBA::Point::new(ox, oy-c-a,))
    ax = RBA::Point::new(dx, 0)
    ay = RBA::Point::new(0, dy*2)

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, ax, ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, ax, ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, -ax, -ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, -ax, -ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, -ax, ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, -ax, ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, ax, -ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, ax, -ay, nx, ny/2))


    2. I try to insert a pattern that doesn't need to be arrayed. Like a big cover shape.
    However, anywhere I insert "cell.shapes(@layer_index).insert(RBA::Box::new(0, 0, 10000, 10000)) shows error....
    Maybe I did something wrong.







    #==============whole set of the code===
    class Generator
    def initialize
    @layout = RBA::Layout::new
    @layout.dbu =0.001
    @top = @layout.create_cell("TOP")
    @layer_index = @layout.insert_layer(RBA::LayerInfo::new(15,99))
    @lib = RBA::Library.library_by_name("Basic")
    @lib || raise("Unknown lib 'Basic'");
    @pcell_decl = @lib.layout.pcell_declaration("TEXT");
    @pcell_decl || raise("Unknown PCell 'TEXT'");
    @chars = {}
    # create one cell for "-" and the digits
    "0123456789-".split("").each do |c|

    param = {
    "text" => c,
    "layer" => RBA::LayerInfo::new(15,99),
    "mag" => 1
    }
    pv = @pcell_decl.get_parameters.collect do |p|
    param[p.name] || p.default
    end

    # fake a PCell by creating a normal cell and using the production code
    # of the PCell to generate the layout
    char_cell = @layout.create_cell("C#{c}")
    @pcell_decl.produce(@layout, [ @layer_index ], pv, char_cell)

    @chars[c] = char_cell

    end

    end

    def make_pattern( a , b , c , d , xpos , ypos )
    dx = 4*a+4*b
    dy = 2*c+2*d+2*a
    arx = (10000*0.9*0.5/dx).to_i
    ary = (10000*0.8*0.5/dy).to_i+1
    nx = arx
    ny = ary
    a1 = [
    RBA::Point::new( 0 , 0 ),
    RBA::Point::new( 2*b+1.5*a , 0 ),
    RBA::Point::new( 2*b+1.5*a , a+c ),
    RBA::Point::new( 2*b+1.5*a+2*a+2*b , a+c ),
    RBA::Point::new( 2*b+1.5*a+2*a+2*b , a*-0.5 ),
    ]
    d1 = [
    RBA::Point::new( b+0.5*a , c+0.5*a ),
    RBA::Point::new( b+0.5*a , c+0.5*a+a ),
    ]
    e1 = [
    RBA::Point::new( 3*b+2.5*a , c-d+0.5*a ),
    RBA::Point::new( 3*b+2.5*a , 0.5*a-d ),
    ]
    unit_cell = @layout.create_cell("1")
    unit_cell.shapes(@layer_index).insert(RBA::Path::new(a1, a))
    unit_cell.shapes(@layer_index).insert(RBA::Path::new(d1, a))
    unit_cell.shapes(@layer_index).insert(RBA::Path::new(e1, a))
    ox = xpos
    oy = ypos

    #====================== where I did mirror
    trans1 = RBA::Trans::new(0, true, RBA::Point::new(ox, oy+c+a,))
    trans2 = RBA::Trans::new(0, false, RBA::Point::new(ox, oy-c-a,))
    ax = RBA::Point::new(dx, 0)
    ay = RBA::Point::new(0, dy*2)

    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, ax, ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, ax, ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, -ax, -ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, -ax, -ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, -ax, ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, -ax, ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans1, ax, -ay, nx, ny/2))
    @top.insert(RBA::CellInstArray::new(unit_cell.cell_index, trans2, ax, -ay, nx, ny/2))

    text = ("A%03dB%03d\nC%04dD%03d" % [ a,b,c,d ])

    xt0 = xt = -4000 + xpos
    yt = -4800 + ypos

    text.split("").each do |tc|
    if tc == "\n"
    xt = xt0
    yt -= 900
    else
    char_cell = @chars[tc]
    if char_cell
    @top.insert(RBA::CellInstArray::new(char_cell.cell_index, RBA::Trans::new(RBA::Trans::r0, xt, yt)))
    xt += 600
    end
    end
    end

    def done(filename)
    @layout.write(filename)
    puts "Output file written: "+filename
    end

    end

    end
    # =================================================================

    g = Generator::new

    File.open('10K_F4.txt', 'w') do |f|

    #====================

    ncolumns = 2

    nrows = 2

    num1 = 1

    ncolumns.times do |nc|

    puts "Column #{nc+1}/#{ncolumns} .."


    nrows.times do |nr|

    #====================

    kcolumns = 2

    krows = 2

    num2 = 1

    kcolumns.times do |kc|

    puts "Column #{kc+1}/#{kcolumns} .."


    krows.times do |kr|

    #====================

    lcolumns = 2

    lrows = 2

    num3 = 1

    lcolumns.times do |lc|

    puts "Column #{lc+1}/#{lcolumns} .."


    lrows.times do |lr|

    #====================
    #====================

    # mcolumns =4

    # mrows = 10

    # num4 = 1

    #mcolumns.times do |mc|

    # puts "Column #{mc+1}/#{mcolumns} .."


    # mrows.times do |mr|

    #====================

    #g.make_pattern( 20+nr*2+nx*10 , 20+lc*2+lr*10 , 10+kc*20 , 30+kr*2 , 10000*nc+10000*ncolumns*kc+10000*ncolumns*kcolumns*lc+10000*ncolumns*kcolumns*lcolumns*mc , 10000*nr+10000*nrows*kr+10000*nrows*krows*lr+10000*nrows*krows*lrows*mr )
    g.make_pattern( 120+nr*2+nc*10 , 120+lr*2+lc*10 , 20+kr*100 , 20+kc*4 , 10000*nc+10000*ncolumns*kc+10000*ncolumns*kcolumns*lc, 10000*nr+10000*nrows*kr+10000*nrows*krows*lr )
    values = [120+nr*2+nc*10 , 120+lr*2+lc*10 , 20+kr*100 , 20+kc*4 , 10000*nc+10000*ncolumns*kc+10000*ncolumns*kcolumns*lc, 10000*nr+10000*nrows*kr+10000*nrows*krows*lr ]
    f.puts(values.collect { |v| v.to_s }.join(","))
    #g.make_pattern( 20+nc*2+nr*10 , 20+lc*2+lr*10 , 10+kc*200 , 30+kr*2 , 10000*ncolumns*0.01+10000*ncolumns*kcolumns*0.01+10000*ncolumns*kcolumns*lcolumns+10000*nc+10000*ncolumns*kc+10000*ncolumns*kcolumns*lc, 10000*nr+10000*nrows*kr+10000*nrows*krows*lr )
    #====================

    num1 = num1 + 1
    num2 = num2 + 1
    num3 = num3 + 1
    #num4 = num4 + 1
    end

    end

    end

    end
    end

    #end
    # end

    end
    end
    g.done("10K_F4.oas")
  • edited November -1

    Hi Mattseng,

    both pieces of code look fine to me.

    Some tools impose restrictions on the way arrays are created, hence using "-ax" for the array pitch may create some issues when using GDS2. But those tools are rare now and I don't think there will be trouble with that. Specifically since you are using OASIS which does not have the interpretation ambiguities of GDS2.

    Regarding the second piece of code: this looks fine to me too. What is the error message you get?

    Matthias

  • edited November -1
    Hi Matthias
    the error looks like this.


    https://www.dropbox.com/s/lahcj415mrwib9z/Case%201.png?dl=0

    Just trying to add another box before writing the oasis.


    end
    cell.shapes(@layer_index).insert(RBA::Box::new(0, 0, 10000, 10000))
    g.done("10K_F4.oas")
  • edited November -1

    That's pretty simple - "cell" is unknown.

    You'll have to put the code into the "done" method before the "write". There you can use "@top" member variable instead of "cell" to address the top cell.

    Matthias

  • I do not have a problem to report - however, I did want to take time to note how extremely helpful this thread was. I am doing some layout and creating labels for each instant by hand has been extremely painful. This automation is superb and really helps improve efficiency of label creation! Thanks all!!

    Vikas

Sign In or Register to comment.