#!/usr/bin/python3
# Scale the final GDS by a factor of 4
# This is called by a tech hook that should be inserted post write_design
import sys, glob, os, re
from typing import List
# All functions in this file will take in [gds_tool, cell_list, gds_file]
# Args are [stdcells, gds_file, gds_function]
[docs]
def main(argv: List[str]):
stdcells = argv[0]
gds_file = argv[1]
gds_function = argv[2]
try:
# Prioritize gdstk
gds_tool = __import__('gdstk')
except ImportError:
try:
print("gdstk not found, falling back to gdspy...")
gds_tool = __import__('gdspy')
except ImportError:
print('Check your gdspy installation!')
sys.exit()
print(f"Using {gds_tool.__name__} to get area results from {gds_file}...")
cell_list = [line.rstrip() for line in open(stdcells, 'r')]
fn_to_call = getattr(sys.modules[__name__], gds_function, None)
if callable(fn_to_call):
ret_val = fn_to_call(gds_tool, cell_list, gds_file)
return ret_val
[docs]
def get_area(gds_tool, cell_list, gds_file):
# TODO implement with gdspy as well
if gds_tool.__name__ == 'gdstk':
total_area = 0.0
# load gds
gds_lib = gds_tool.read_gds(infile=gds_file)
top_cell = None
for cell in gds_lib.cells:
if cell.name == "TOPCELL":
top_cell = cell
break
for e in top_cell.polygons:
total_area += e.area()
return total_area
else:
raise NotImplementedError("gdspy not supported yet")
[docs]
def asap7_scale_gds(gds_tool, cell_list, gds_file):
print('Scaling down {gds} using {tool}...'.format(gds=gds_file, tool=gds_tool.__name__))
if gds_tool.__name__ == 'gdstk':
# load original_gds
gds_lib = gds_tool.read_gds(infile=gds_file)
# Non STDCELL cells
non_stdcells = list(filter(lambda c: c.name not in cell_list, gds_lib.cells))
# Iterate through cells that aren't part of standard cell library and scale
for cell in list(filter(lambda c: c.name not in cell_list, gds_lib.cells)):
print('Scaling down ' + cell.name)
# Need to remove 'blk' layer from any macros, else LVS rule deck interprets it as a polygon
# This has a layer datatype of 4
# Then scale down the polygon
non_stdcell_pgs_uniq_layers = set([pg.layer for pg in cell.polygons])
blk_layer_dtype = 4
for layer in non_stdcell_pgs_uniq_layers:
# For all polygon layers that exist in all cells, filter out the polygons with datatype 4
cell.filter(spec=[(layer, blk_layer_dtype)], remove=True)
for poly in cell.polygons:
poly.scale(0.25)
# Scale paths
for path in cell.paths:
path.scale(0.25)
# Scale and move labels
for label in cell.labels:
# Bug fix for some EDA tools that didn't set MAG field in gds file
# Maybe this is expected behavior in ASAP7 PDK
label.magnification = 0.25
label.origin = tuple(i * 0.25 for i in label.origin)
# Move references (which are scaled if needed)
for ref in cell.references:
ref.origin = tuple(i * 0.25 for i in ref.origin)
# We can also write an SVG of the top cell with gdstk
gds_lib.top_level()[0].write_svg(os.path.splitext(gds_file)[0] + '.svg')
elif gds_tool.__name__ == 'gdspy':
# load original_gds
gds_lib = gds_tool.GdsLibrary().read_gds(infile=gds_file, units='import')
# Iterate through cells that aren't part of standard cell library and scale
for k,v in gds_lib.cell_dict.items():
if not any(cell in k for cell in cell_list):
print('Scaling down ' + k)
# Need to remove 'blk' layer from any macros, else LVS rule deck interprets it as a polygon
# This has a layer datatype of 4
# Then scale down the polygon
v.polygons = [poly.scale(0.25) for poly in v.polygons if not 4 in poly.datatypes]
# Scale paths
for path in v.paths:
path.scale(0.25)
# gdspy v1.4 bug: we also need to scale custom path extensions
# Fixed by gdspy/pull#101 in next release
if gds_tool.__version__ == '1.4':
for i, end in enumerate(path.ends):
if isinstance(end, tuple):
path.ends[i] = tuple([e*0.25 for e in end])
# Scale and move labels
for label in v.labels:
# Bug fix for some EDA tools that didn't set MAG field in gds file
# Maybe this is expected behavior in ASAP7 PDK
# In gdspy/__init__.py: `kwargs['magnification'] = record[1][0]`
label.magnification = 0.25
label.translate(-label.position[0]*0.75, -label.position[1]*0.75)
# Move references (which are scaled if needed)
for ref in v.references:
ref.translate(-ref.origin[0]*0.75, -ref.origin[1]*0.75)
# Overwrite original GDS file
gds_lib.write_gds(gds_file)
# For Calibre DRC, we need to rename the top cell to 'TOPCELL'
gds_lib.top_level()[0].name = 'TOPCELL'
gds_lib.write_gds(os.path.splitext(gds_file)[0] + '_drc.gds')
if __name__ == '__main__':
main(sys.argv[1:])