Skip to content

Commit

Permalink
Replace cell (#229)
Browse files Browse the repository at this point in the history
* replace update
* Update test_scripts_replace_cell.py
  • Loading branch information
lukasc-ubc authored Nov 4, 2024
1 parent 739a351 commit cce6dd4
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 20 deletions.
85 changes: 65 additions & 20 deletions klayout_dot_config/python/SiEPIC/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -3089,22 +3089,34 @@ def layout_diff(cell1, cell2, tol = 1, verbose=True):
Based on https://github.com/atait/lytest
'''

if not cell1.layout() == cell2.layout():
raise Exception ('SiEPIC.scripts.layout_diff is only implement for cells in the same layout.')
# Get a list of the layers
layers = []
layout1 = cell1.layout()
layout2 = cell2.layout()
if layout1 == layout2:
# cells from the same layout
for li in layout1.layer_indices():
layers.append ( (li,li) )
else:
# cells from different layouts
#raise Exception ('SiEPIC.scripts.layout_diff is only implement for cells in the same layout.')
for ll1 in layout1.layer_indices():
li1 = layout1.get_info(ll1)
ll2 = layout2.find_layer(layout1.get_info(ll1))
if ll2 is None:
raise Exception(
f"Layer {li1} in cell1 is not present in cell2."
)

layers.append((ll1, ll2))

# Count the differences
diff_count = 0

# Get a list of the layers
layers = []
layout = cell1.layout()
for li in layout.layer_indices():
layers.append ( li )

# Do geometry checks on each layer
for li in layers:
r1 = pya.Region(cell1.begin_shapes_rec(li))
r2 = pya.Region(cell2.begin_shapes_rec(li))
for li1,li2 in layers:
r1 = pya.Region(cell1.begin_shapes_rec(li1))
r2 = pya.Region(cell2.begin_shapes_rec(li2))

rxor = r1 ^ r2

Expand All @@ -3115,22 +3127,31 @@ def layout_diff(cell1, cell2, tol = 1, verbose=True):
diff_count += rxor.size()
if verbose:
print(
f" - SiEPIC.scripts.layout_diff: {rxor.size()} differences found in {cell1.name} on layer {layout.get_info(li)}."
f" - SiEPIC.scripts.layout_diff: {rxor.size()} differences found in {cell1.name} on layer {layout1.get_info(li1)}."
)
print(r1)
print(r2)
print(rxor)
return diff_count


def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_library=None, Exact = True, RequiredCharacter = '$', run_layout_diff = True, debug = False):
def replace_cell(layout, cell_x_name = None, cell_y_name=None, cell_y_file=None, cell_y_library=None, cell_ref_bb = None, Exact = True, RequiredCharacter = '$', run_layout_diff = True, debug = False):
'''
SiEPIC-Tools: scripts.replace_cell
Search and replace: cell_x with cell_y
useful for blackbox IP cell replacement
- load layout containing cell_y_name from cell_y_file or cell_y_library
- replace all cell_x_name* instances with cell_y
- Exact = True: the cell name must match exactly
= False: the cell_y_name appears at the beginning of the cells to be replaced
and RequiredCharacter appears directly after, e.g,. '$' as KLayout appends during merging
(exact match is still included)
run_layout_diff = True:
perform an xor with the black box cell in the layout, versus the original (reference) black box
requires cell_ref_bb
cell_ref_bb: the black box cell, which will be compared with the cell_x
check_bbox = True: make sure the bounding box for the two cells are the same
Black box True geometry
Basename_BB, Basename_BB* YES: Basename
Expand All @@ -3144,6 +3165,18 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr
log = ''
log += "- cell replacement for: %s, with cell %s (%s or %s)\n" % (cell_x_name, cell_y_name, cell_y_file, cell_y_library)

# Find the cell name from the cell_ref_bb
if not cell_x_name:
if cell_ref_bb:
cell_x_name = cell_ref_bb.name
else:
raise Exception ('missing replacement cell name')

# Make sure we can run the layout diff check.
if run_layout_diff:
if not cell_ref_bb:
raise Exception ('missing reference black box cell, required for layout diff check')

# Find the cells that need replacement (cell_x)
# find cell name exactly matching cell_x_name
cells_x = [layout.cell(cell_x_name)]
Expand All @@ -3165,7 +3198,7 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr
if debug:
print(" - none found: %s" % cell_x_name)
log += " - none found: %s" % cell_x_name
return
return log, None, False

if Exact:
if debug:
Expand All @@ -3176,6 +3209,12 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr
print(" - non-exact match: %s" % ([c.name for c in cells_x]) )
log += " - non-exact match: %s" % ([c.name for c in cells_x])

# if you don't provide the cell name, get it from the file
if not cell_y_name:
layout1 = pya.Layout()
layout1.read(cell_y_file)
cell_y_name = layout1.top_cell().name

# Load the new cell:
if cell_y_file:
cell_y = layout.cell(cell_y_name)
Expand All @@ -3200,6 +3239,10 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr
if cells_x:
log += " - replacing cells: %s\n" % ([c.name for c in cells_x])


# Perform replacement
count = 0
error = False
for cell_x in cells_x:
if debug:
print(" - replace_cell: found cells to be replaced: %s" % (cell_x.name))
Expand All @@ -3224,24 +3267,26 @@ def replace_cell(layout, cell_x_name, cell_y_name, cell_y_file=None, cell_y_libr
# Check if the BB cells are the same, by doing an XOR operation
# from . import layout_diff
if run_layout_diff:
if layout_diff(inst.cell, cell_x, tol=0):
if debug:
print(" - black box cells are different: %s vs %s" % (inst.cell.name, cell_x.name))
raise Exception (" - black box cells are different: %s vs %s" % (inst.cell.name, cell_x.name))
if layout_diff(cell_ref_bb, cell_x, tol=0, verbose=True):
print(" - ERROR: black box cells are different: %s vs %s" % (inst.cell.name, cell_x.name))
error = True
# raise Exception (" - black box cells are different: %s vs %s" % (inst.cell.name, cell_x.name))
break;
# replace with CELL_Y
if inst.is_regular_array():
if debug:
print(" - checked, and replaced %s in %s, with cell array: %s" % (cell_x.name, cc.name, cell_y.name))
ci = inst.cell_inst
cc.replace(inst, pya.CellInstArray(cell_y.cell_index(),inst.trans, ci.a, ci.b, ci.na, ci.nb))
count += 1
else:
if debug:
print(" - replacing %s in %s, with cell: %s" % (cell_x.name, cc.name, cell_y.name))
cc.replace(inst, pya.CellInstArray(cell_y.cell_index(),inst.trans))
count += 1
inst = next(itr, None)

return log
return log, count, error


def svg_from_cell(verbose=True):
Expand Down Expand Up @@ -3435,7 +3480,7 @@ def instantiate_all_library_cells(topcell, terminator_cells = None, terminator_l
else:
print('Error in: %s' % n)
p.inc()
x, y = xmax, 0
x, y = xmax, 0

# all the fixed cells
for c in li.layout().each_top_cell():
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
"""
Test for SiEPIC.scripts.replace_cell
by Lukas Chrostowski 2024
"""

def test_replace_cell():
'''
'''

import pya

import SiEPIC
from SiEPIC._globals import Python_Env
from SiEPIC.utils.layout import new_layout

import os

if Python_Env == 'Script':
# For external Python mode, when installed using pip install siepic_ebeam_pdk
import GSiP

tech_name = 'GSiP'

from packaging import version
if version.parse(SiEPIC.__version__) < version.parse("0.5.4"):
raise Exception("Errors", "This example requires SiEPIC-Tools version 0.5.4 or greater.")


from SiEPIC.scripts import replace_cell, delete_extra_topcells

# The circuit layout that contains BB cells
path = os.path.dirname(os.path.realpath(__file__))
file_in = os.path.join(path,'example_bb.gds')
layout = pya.Layout()
layout.read(file_in)
top_cell = layout.top_cell()

# the individual BB reference cell
file_bb = os.path.join(path,'ip_library_bb.gds')
ly_bb = pya.Layout()
ly_bb.read(file_bb)
cell_bb = ly_bb.top_cell()

# the individual WB cell
file_wb = os.path.join(path,'ip_library_wb.gds')
'''
ly_wb = pya.Layout()
ly_wb.read(file_wb)
cell_wb = ly_wb.top_cell()
'''
def check_bb_geometries(layout):
'''
check if there are any Black Box layers in the layout
'''
layer_bb = layout.layer(pya.LayerInfo(998,0)) # hard coded for the GSiP PDK
r1 = pya.Region(top_cell.begin_shapes_rec(layer_bb))
diff_count = 0
if not r1.is_empty():
diff_count = r1.size()
print(
f" - SiEPIC.scripts.layout_diff: {r1.size()} Black Box geometry(ies) found in {top_cell.name} on layer {layout.get_info(layer_bb)}."
)
return diff_count


# Check -- exact replacement (without $)
if 1:
text_out, count, error = replace_cell(layout,
cell_ref_bb = cell_bb,
cell_y_file = file_wb,
Exact = True,
run_layout_diff = False,
debug = True,
)
print('replaced %s' %count)
assert count == 1
assert check_bb_geometries(layout) == 1

file_out = os.path.join(path,'example_replaced.gds')
delete_extra_topcells(layout, top_cell.name)
layout.write(file_out)
try:
# Display the layout in KLayout, using KLayout Package "klive", which needs to be installed in the KLayout Application
if Python_Env == 'Script':
from SiEPIC.utils import klive
klive.show(file_out, technology='EBeam')
except:
pass
os.remove(file_out)


# Check -- non-exact replacement (with $)
if 1:
layout = pya.Layout()
layout.read(file_in)
top_cell = layout.top_cell()
text_out, count, error = replace_cell(layout,
cell_ref_bb = cell_bb,
cell_y_file = file_wb,
Exact = False, RequiredCharacter='$',
run_layout_diff = False,
debug = True,
)
print('replaced %s' %count)
assert count == 2
assert check_bb_geometries(layout) == 0

file_out = os.path.join(path,'example_replaced2.gds')
delete_extra_topcells(layout, top_cell.name)
layout.write(file_out)
try:
# Display the layout in KLayout, using KLayout Package "klive", which needs to be installed in the KLayout Application
if Python_Env == 'Script':
from SiEPIC.utils import klive
klive.show(file_out, technology='EBeam')
except:
pass
os.remove(file_out)

# Check -- Run BB reference vs. design layout difference, non-exact replacement (with $)
if 1:
layout = pya.Layout()
layout.read(file_in)
top_cell = layout.top_cell()
text_out, count, error = replace_cell(layout,
cell_y_file = file_wb,
Exact = False, RequiredCharacter='$',
cell_ref_bb = cell_bb,
run_layout_diff = True,
debug = True,
)
print('replaced %s' %count)
assert count == 2
assert check_bb_geometries(layout) == 0
assert error == False

# Check -- Run BB reference (changed) vs. design layout difference, non-exact replacement (with $)
if 1:
layout = pya.Layout()
layout.read(file_in)
top_cell = layout.top_cell()
# the (changed) BB reference cell
file_bb = os.path.join(path,'ip_library_bb2.gds')
ly_bb = pya.Layout()
ly_bb.read(file_bb)
cell_bb = ly_bb.top_cell()
text_out, count, error = replace_cell(layout,
cell_ref_bb = cell_bb,
cell_y_file = file_wb,
Exact = False, RequiredCharacter='$',
run_layout_diff = True,
debug = True,
)
assert error == True

if __name__ == "__main__":
test_replace_cell()

0 comments on commit cce6dd4

Please sign in to comment.