Note
Go to the end to download the full example code
Dummy Filling during Layout design
This sample illustrates how to perform dummy filling on a layout. Dummy filling is crucial for controlling the density of certain layers, which helps prevent issues during fabrication steps.
In IPKISS, dummy filling can be performed directly on your layout by using ipkiss3.all.dummy_fill_layout, or on a gds file
by using ipkiss3.all.dummy_fill_gdsii.
This example demonstrates how to:
Define a test structure and a design containing a routed splitter tree.
Add a frame to simulate a realistic design boundary.
Apply dummy filling to the layout using a specified Filler cell, while ignoring certain layers (e.g., the documentation layer).
Disclaimer: Dummy filling here is done on the silicon layer only, since no metal was used in this device.
import pteam_library_si_fab.all as lib
import si_fab.all as pdk
import ipkiss3.all as i3
DESIGN_SIZE = (1000, 1000)
class TestStructure(i3.PCell):
total_length = i3.PositiveNumberProperty(
default=DESIGN_SIZE[0] - 100,
doc="Total length of the test structure",
)
class Layout(i3.LayoutView):
def generate(self, layout):
gc = pdk.GratingCoupler()
layout = i3.place_and_route(
layout=layout,
specs=[
i3.Inst("gc_in", gc),
i3.Inst("gc_out", gc),
i3.Place("gc_in@CW", (0, 0)),
i3.Place("gc_out@CE", (self.total_length, 0), angle=180),
i3.ConnectManhattan("gc_in:out", "gc_out:out"),
],
)
return layout
class Design(i3.PCell):
class Layout(i3.LayoutView):
def generate(self, layout):
# We instantiate the splitter tree
splitter_tree = lib.SplitterTree(levels=5)
# We pass the splitter tree to the routed splitter tree
routed_splitter_tree = lib.RoutedSplitterTree(splitter_tree=splitter_tree, length_of_out_wg=120)
length = routed_splitter_tree.Layout().size_info().get_width()
layout = i3.place_and_route(
layout=layout,
specs=[
i3.Inst("DUT", routed_splitter_tree),
i3.Inst("test_structure", TestStructure(total_length=length)),
i3.Place.X("test_structure", x=50),
i3.Place.X("DUT@W", x=50),
i3.Place.Y("test_structure@S", y=50),
i3.Place.Y("DUT@S", y=50, relative_to="test_structure@N"),
],
)
# add a frame, such it replicates a realistic design
layout += i3.Box(
layer=pdk.TECH.PPLAYER.DOC, box_size=DESIGN_SIZE, center=(DESIGN_SIZE[0] / 2, DESIGN_SIZE[1] / 2)
)
# Now we want to perform dummy filling on the layout directly -> this makes it easier to iterate
# For this example, we don't have metal layers to fill, so we fill the silicon layers to control density
# and prevent problems during litho and wet etch.
# We create a square using i3.Box to serve as the filler,
# specify a 1um spacing between the fillers,
# and instruct to ignore the DOC layer that indicates the frame of the design.
fill_spec = i3.Filler(
cell=i3.Box(layer=pdk.TECH.PPLAYER.SI, box_size=(4, 4)),
tile_buffer=1.0,
ignore_layers=[pdk.TECH.PPLAYER.DOC],
)
# Now we dummy fill our current layout
dummy_elements = i3.dummy_fill_layout(layout, filling=[fill_spec])
# if you want more control over the dummy filling you can specify more of the parameters
# fill_spec = i3.Filler(
# cell=i3.Box(layer=pdk.TECH.PPLAYER.SI, box_size=(4, 4)),
# density=0.75,
# ignore_layers=[pdk.TECH.PPLAYER.DOC],
# design_buffer=5.0,
# edge_buffer=20.0,
# )
# dummy_elements = i3.dummy_fill_layout(layout, filling=[fill_spec])
layout += dummy_elements
return layout
# Design().Layout().visualize()
Design().Layout().write_gdsii("test_plot.gds")
# if you want to do dummy filling on a gds, that is also possible using the other function
# i3.dummy_fill_gdsii(filename, fill_cell, ...)