generate dummies for the chip finishing

Here is a DRC script to generate dummies for the chip finishing.

Although dummies are often a box, I have included an example with a polygon (a cross shape).
I have imagined all those dummies and theyr do not target a specific process and they will probably not fit with your target dummies density.

For further options, you can have a look at : https://www.klayout.de/doc-qt5/about/drc_ref_layer.html#k_37
or https://www.klayout.de/forum/discussion/550/using-the-fill-tool

Anyhow, it will generate the dummies cells directly on the toplevel cell.
Does anyone could help to improve my script to generate all those dummies in a separate cell called "DUMMIES_TOP" ? It would help a lot.

Thanks, BRgds,
Laurent

########################
# DUMMIES generation
########################

tstart = Time.now

if $in_gds
  if $topcell
    source($in_gds,$topcell)
  else
    source($in_gds)
  end
end

if $report_file
  report($report_file)
else
  report("DUMMIES generation runset", File.join(File.dirname(RBA::CellView::active.filename), "dumm.lydrc"))
end

require "logger"
log_file(File.join(File.dirname(RBA::CellView::active.filename), "log_DUMM.txt"))

# options
########################


# KLAYOUT setup
########################
# Use a tile size of 1mm
tiles(1000.um)
# Use a tile border of 10 micron:
#tile_borders(10.um)
no_borders

# hierarchical
deep

# Use 8 CPU cores
threads(8)
verbose(true)

# layers definitions
########################
info("Layers definitions")
NWELL = input(1,0)
ACTIVE = input(2,0)
POLY = input(3,0)
METAL1 = input(10,0)
METAL1_dmyblk  = input(10,2)
METAL2 = input(20,0)
METAL2_dmyblk  = input(20,2)
METAL3 = input(30,0)
METAL3_dmyblk  = input(30,2)
METAL4 = input(40,0)
METAL4_dmyblk  = input(40,2)

EXTENT_ALL = source.extent
EXCL_DIFPL = (NWELL + ACTIVE + POLY).sized(2.0)

ACT_fill = fill_pattern("DUMM_ACT")
hull =  [ RBA::DPoint::new(1, 0), RBA::DPoint::new(1, 1), RBA::DPoint::new(0, 1), RBA::DPoint::new(0, 2),
          RBA::DPoint::new(1, 2), RBA::DPoint::new(1, 3), RBA::DPoint::new(2, 3),  RBA::DPoint::new(2, 2),
          RBA::DPoint::new(3, 2), RBA::DPoint::new(3, 1), RBA::DPoint::new(2, 1),  RBA::DPoint::new(2, 0)]
ACT_fill.shape(2, 1, polygon(hull))
ACT_fill.origin(-1, -1)
(EXTENT_ALL - EXCL_DIFPL).fill(ACT_fill, hstep(4.0), vstep(4.0))

POLY_fill = fill_pattern("DUMM_POLY")
POLY_fill.shape(3, 1, box(0, 0, 3, 3))
POLY_fill.origin(-3, -3)
(EXTENT_ALL - EXCL_DIFPL).fill(POLY_fill, hstep(4.0), vstep(4.0))

M1_fill = fill_pattern("DUMM_METAL1")
M1_fill.shape(10, 1, box(0, 0, 3, 1))
M1_fill.origin(-1.0, -1.0)
(EXTENT_ALL - (METAL1.sized(3.0)) - METAL1_dmyblk).fill(M1_fill, hstep(4.0), vstep(2.0))

M2_fill = fill_pattern("DUMM_METAL2")
M2_fill.shape(20, 1, box(0, 0, 1, 3))
M2_fill.origin(-1.0, -1.0)
(EXTENT_ALL - (METAL2.sized(3.0)) - METAL2_dmyblk).fill(M2_fill, hstep(2.0), vstep(4.0))

M3_fill = fill_pattern("DUMM_METAL3")
M3_fill.shape(30, 1, box(0, 0, 3, 1))
M3_fill.origin(-1.0, -1.0)
(EXTENT_ALL - (METAL3.sized(3.0)) - METAL3_dmyblk).fill(M3_fill, hstep(4.0), vstep(2.0))

M4_fill = fill_pattern("DUMM_METAL4")
M4_fill.shape(40, 1, box(0, 0, 1, 3))
M4_fill.origin(-1.0, -1.0)
(EXTENT_ALL - (METAL4.sized(3.0)) - METAL4_dmyblk).fill(M4_fill, hstep(2.0), vstep(4.0))


time = Time.now
hours = ((time - tstart)/3600).to_i
minutes = ((time - tstart)/60 - hours * 60).to_i
seconds = ((time - tstart) - (minutes * 60 + hours * 3600)).to_i
info("DRC finished at : #{time.hour}:#{time.min}:#{time.sec}  -  DRC duration =  #{hours} hrs. #{minutes} min. #{seconds} sec.\n")
$stdout.write "DUMMIES generation finished at : #{time.hour}:#{time.min}:#{time.sec}  -  DRC duration =  #{hours} hrs. #{minutes} min. #{seconds} sec.\n"

Comments

  • Hi Laurent,

    as a sample how this can be done, here is my Metal1 fill implementation for the IHP sg13g2 PDK. It puts the Metal1 fill in the cell "METAL1_FILL":

    verbose
    
    ncpu = 4
    
    chip = input(189, 4)
    chip.output(189, 4)
    
    # Prepare a hierarchy below the top cell to hold the fill pattern later
    top_cell = source.cell_obj
    
    # for metal1
    metal1_fill_top = source.layout.create_cell("METAL1_FILL")
    top_cell.insert(RBA::CellInstArray::new(metal1_fill_top, RBA::Trans::new))
    
    # Find the sealring so we can exclude everything outside
    
    # NOTE: this must not happen in tiled mode as the sealring
    # is only visible as a whole in flat mode
    sealring = source.cell("sealring")
    
    metal1_seal = sealring.input(8, 0)
    metal1_seal_inner = metal1_seal.holes
    # NOTE: metal1_seal_outer is empty if there is no sealring ->
    # the full chip will be filled
    metal1_seal_outer = chip.interacting(metal1_seal_inner) - metal1_seal_inner
    
    # Everything else can be done in tiled mode
    
    tiles(500)
    tile_borders(0.0)
    threads(ncpu)
    
    fill_origin = origin(0, 0)
    
    # switch to the output cell for the metal1 fill
    output_cell(metal1_fill_top.name)
    
    metal1 = input(8, 0)
    metal1_fill = input(8, 22)
    metal1_nofill = input(8, 23) + metal1_seal_outer
    
    metal1_dist = 0.42
    metal1_min_space_to_fill = 1.0
    
    # close small gaps we can't use for fill at all
    metal1_cleaned = metal1.raw.sized(0.5*metal1_min_space_to_fill).sized(-0.5*metal1_min_space_to_fill)
    
    # compute the metal1 fill area
    to_fill = chip - metal1_nofill - metal1_cleaned
    
    pattern = fill_pattern("METAL1_FILL1")
    pattern.shape(8, 22, box(0.0, 0.0, 5.0, 5.0))
    pattern.dim(5.0, 5.0)
    pattern.margin(metal1_dist, metal1_dist)
    
    to_fill = to_fill.fill_with_left(pattern, hstep(7.0, 0), vstep(1.5, 7.0), fill_origin)
    
    pattern = fill_pattern("METAL1_FILL2")
    pattern.shape(8, 22, box(0.0, 0.0, 2.0, 2.0))
    pattern.dim(2.0, 2.0)
    pattern.margin(metal1_dist, metal1_dist)
    
    to_fill = to_fill.fill_with_left(pattern, hstep(2.42, 0), vstep(0.65, 2.42), fill_origin)
    
    pattern = fill_pattern("METAL1_FILL3")
    pattern.shape(8, 22, box(0.0, 0.0, 1.2, 1.2))
    pattern.dim(1.2, 1.2)
    pattern.margin(metal1_dist, metal1_dist)
    
    to_fill = to_fill.fill_with_left(pattern, hstep(1.62, 0), vstep(0.3, 1.62), fill_origin)
    

    This will need version 0.30.3. Note however, that the fill feature has a small bug in 0.30.3 that may degrade performance and produce too many fill instances (https://github.com/KLayout/klayout/issues/2112). That is going to be fixed in 0.30.4.

    Matthias

Sign In or Register to comment.