Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace cell #229

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
Loading