Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: atait/lymask
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.1.3
Choose a base ref
...
head repository: atait/lymask
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Mar 9, 2022

  1. Copy the full SHA
    31398b2 View commit details
  2. list specification of union

    atait committed Mar 9, 2022
    Copy the full SHA
    00aefaf View commit details
  3. version bump 1.4

    atait committed Mar 9, 2022
    Copy the full SHA
    9fea7a7 View commit details

Commits on Mar 10, 2022

  1. Copy the full SHA
    b1e9f0b View commit details
  2. order-insensitive testing

    atait committed Mar 10, 2022
    Copy the full SHA
    b70527d View commit details
  3. Euclidian compatibility

    atait committed Mar 10, 2022
    Copy the full SHA
    18adc05 View commit details
  4. Copy the full SHA
    eee7d1f View commit details
  5. version bump 1.5

    atait committed Mar 10, 2022
    Copy the full SHA
    9e63ffc View commit details

Commits on Mar 11, 2022

  1. flatten step

    atait committed Mar 11, 2022
    Copy the full SHA
    ba6289c View commit details

Commits on Mar 12, 2022

  1. bump 1.6

    atait committed Mar 12, 2022
    Copy the full SHA
    8d5de9d View commit details
  2. Copy the full SHA
    991eed3 View commit details
  3. path to polygon

    atait committed Mar 12, 2022
    Copy the full SHA
    f6d47ce View commit details
  4. bump 1.8

    atait committed Mar 12, 2022
    Copy the full SHA
    159bf1c View commit details
  5. bugfixes v1.9

    atait committed Mar 12, 2022
    Copy the full SHA
    720fe41 View commit details

Commits on Aug 5, 2022

  1. Cleaner resolve tech name

    atait committed Aug 5, 2022
    Copy the full SHA
    524300c View commit details
  2. Loading DRC into objview

    atait committed Aug 5, 2022
    Copy the full SHA
    948ad28 View commit details
  3. bugfix Technology inheritance

    atait committed Aug 5, 2022
    Copy the full SHA
    b43c37f View commit details
  4. version bump 1.10

    atait committed Aug 5, 2022
    Copy the full SHA
    4d86f6a View commit details

Commits on Jan 28, 2024

  1. Copy the full SHA
    6281fba View commit details
  2. Copy the full SHA
    2e0dbfe View commit details
  3. Copy the full SHA
    51c64da View commit details
  4. Copy the full SHA
    35493be View commit details
  5. Bugfix: missing import

    atait committed Jan 28, 2024
    Copy the full SHA
    35840ba View commit details
  6. Copy the full SHA
    36b715e View commit details
  7. version bump 11

    atait committed Jan 28, 2024
    Copy the full SHA
    6e451c4 View commit details
Showing with 336 additions and 90 deletions.
  1. +2 −2 lymask/__init__.py
  2. +41 −1 lymask/dataprep_steps.py
  3. +28 −15 lymask/drc_steps.py
  4. +17 −9 lymask/invocation.py
  5. +74 −16 lymask/library.py
  6. +32 −3 lymask/menu.py
  7. +100 −38 lymask/utilities.py
  8. +2 −1 setup.py
  9. +2 −3 tests/tech/lymask_example_tech/drc/default.yml
  10. +10 −0 tests/tech/lymask_example_tech/drc/multithreaded.yml
  11. +28 −2 tests/test_drc.py
4 changes: 2 additions & 2 deletions lymask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
__version__ = '0.1.3'
__version__ = '0.1.11'
__lygadget_link__ = ['lygadgets', 'yaml']

from lymask.utilities import active_technology, set_active_technology
from lymask.invocation import batch_main, batch_drc_main

from lymask.utilities import get_design_rules
import lymask.dataprep_steps
42 changes: 41 additions & 1 deletion lymask/dataprep_steps.py
Original file line number Diff line number Diff line change
@@ -5,8 +5,9 @@

from lygadgets import pya, isGUI, message, message_loud
from lygadgets import anyCell_to_anyCell
from lygadgets.gui_objects import gui_view

from lymask.utilities import lys, LayerSet, gui_view, active_technology, func_info_to_func_and_kwargs
from lymask.utilities import lys, LayerSet, active_technology, func_info_to_func_and_kwargs
from lymask.library import dbu, as_region, fast_sized, fast_smoothed, set_threads


@@ -20,6 +21,30 @@ def dpStep(step_fun):
return step_fun


def delete_dollar_duplicates(cell):
''' pya sometimes adds a second top cell or duplicates of other cells.
Other layout programs and lithography tools cannot parse this easily.
These all end with '$1', so they can be removed recursively
'''
layout = cell.layout()
if cell.name.endswith('$1'):
try:
cell.delete()
except RuntimeError as err:
if 'mp_v->is_used (m_n)' in err.args[0]:
pass
elif 'Not a valid cell index' not in err.args[0]:
print('BAD CELL INDEX', chop_cell.name)
pass
else:
raise
return
to_delete = set()
for child_id in cell.each_child_cell():
child_cell = layout.cell(child_id)
delete_dollar_duplicates(child_cell)


def dpStep_phidl(step_fun):
''' phidl version, where the mutable object is a phidl.Device not a pya.Cell
Each step must accept one argument that is Device, plus optionals, and not return
@@ -34,6 +59,7 @@ def wrapper(cell, *args, **kwargs):
anyCell_to_anyCell(cell, phidl_device)
step_fun(phidl_device, *args, **kwargs)
anyCell_to_anyCell(phidl_device, cell)
delete_dollar_duplicates(cell)
all_dpfunc_dict[step_fun.__name__] = wrapper
return wrapper

@@ -79,11 +105,25 @@ def check_floorplan(cell, fp_safe=50):
cell.shapes(lys.FLOORPLAN).insert(fp_box)


__warned_about_flattening = False
@dpStep
def flatten(cell):
global __warned_about_flattening
if isGUI() and not __warned_about_flattening:
message_loud('Warning: The flattening step modifies the layout, so be careful about saving.')
__warned_about_flattening = True
cell.flatten(True)


@dpStep
def paths_to_polys(cell):
for layname in lys.keys():
lay = lys[layname]
for shape in cell.each_shape(lay):
if shape.is_path():
shape.polygon = shape.simple_polygon


@dpStep
def erase_text_and_other_junk(cell):
for layname in lys.keys():
43 changes: 28 additions & 15 deletions lymask/drc_steps.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from __future__ import division, print_function, absolute_import
from functools import wraps
from lygadgets import pya, isGUI, message, message_loud
from lygadgets.gui_objects import gui_view

from lymask.utilities import lys, LayerSet, gui_view
from lymask.library import dbu, as_region, fast_sized, fast_smoothed, set_threads, rdb_create, fast_space, turbo
from lymask.utilities import lys, LayerSet
from lymask.library import dbu, as_region, fast_sized, fast_smoothed, set_threads, rdb_create, fast_width, fast_space, fast_separation, turbo, Euclidian


all_drcfunc_dict = {}
@@ -16,6 +17,15 @@ def drcStep(step_fun):
return step_fun


__warned_about_flattening = False
@drcStep
def flatten(cell, rdb):
global __warned_about_flattening
if isGUI() and not __warned_about_flattening:
message_loud('Warning: The flattening step modifies the layout, so be careful about saving.')
__warned_about_flattening = True
cell.flatten(True)

@drcStep
def make_rdbcells(cell, rdb):
rdb.topcell = cell.name
@@ -49,30 +59,31 @@ def drcX(cell, rdb, on_input=[], on_output=[], none=None):


@drcStep
def width(cell, rdb, layer, value, angle=90):
def width(cell, rdb, layer, value, angle=90, min_projection=0):
rdb_category = rdb.create_category('{}_Width'.format(layer))
rdb_category.description = '{} [{:1.3f} um] - Minimum feature width violation'.format(layer, value)
# message_loud('lymask doing {}'.format(rdb_category.name()))

# do it
polys = as_region(cell, layer)
# violations = polys.width_check(value / dbu, False, pya.Region.Euclidian, angle, None, None)
violations = turbo(polys, 'width_check', [value / dbu, False, pya.Region.Euclidian, angle, None, None],
tile_border=1.1*value, job_name='{}_Width'.format(layer))
violations = fast_width(polys, value / dbu, angle, min_projection / dbu)
# violations = polys.width_check(value / dbu, False, Euclidian, angle, None, None)
# violations = turbo(polys, 'width_check', [value / dbu, False, Euclidian, angle, None, None],
# tile_border=1.1*value, job_name='{}_Width'.format(layer))
rdb_create(rdb, cell, rdb_category, violations)


@drcStep
def space(cell, rdb, layer, value, angle=90):
def space(cell, rdb, layer, value, angle=90, min_projection=0):
rdb_category = rdb.create_category('{}_Space'.format(layer))
rdb_category.description = '{} [{:1.3f} um] - Minimum feature spacing violation'.format(layer, value)
# message_loud('lymask doing {}'.format(rdb_category.name()))

# do it
polys = as_region(cell, layer)
# violations = fast_space(polys, value / dbu, angle)
violations = turbo(polys, 'space_check', [value / dbu, False, pya.Region.Euclidian, angle, None, None],
tile_border=1.1*value, job_name='{}_Space'.format(layer))
violations = fast_space(polys, value / dbu, angle, min_projection / dbu)
# violations = turbo(polys, 'space_check', [value / dbu, False, Euclidian, angle, None, None],
# tile_border=1.1*value, job_name='{}_Space'.format(layer))
rdb_create(rdb, cell, rdb_category, violations)


@@ -84,10 +95,9 @@ def inclusion(cell, rdb, inner, outer, include):
# do it
rin = as_region(cell, inner)
rout = as_region(cell, outer)
# violations = rin.sized(include / dbu) - rout
big_rin = turbo(rin, 'sized', include / dbu,
tile_border=1.1*include, job_name='{} in {}'.format(inner, outer))
violations = big_rin - rout
small_rout = fast_sized(rout, -include / dbu)
# Note: this could be parallelized, but it is easier I think than sizing
violations = rin - small_rout

rdb_create(rdb, cell, rdb_category, violations)

@@ -100,8 +110,11 @@ def exclusion(cell, rdb, lay1, lay2, exclude):
# do it
r1 = as_region(cell, lay1)
r2 = as_region(cell, lay2)
# r1.separation_check(r2, exclude / dbu)
too_close = fast_separation(r1, r2, exclude / dbu)
# This could be parallelized
overlaps = r1 & r2
too_close = r1.separation_check(r2, exclude / dbu)

rdb_create(rdb, cell, rdb_category, overlaps)
rdb_create(rdb, cell, rdb_category, too_close)

26 changes: 17 additions & 9 deletions lymask/invocation.py
Original file line number Diff line number Diff line change
@@ -4,10 +4,10 @@
from __future__ import division, print_function, absolute_import
import os
import yaml
from lygadgets import pya, message, Technology
from lygadgets import pya, message, message_loud, Technology

from lymask.utilities import gui_view, gui_active_layout, gui_window, gui_active_technology, \
active_technology, set_active_technology, \
from lygadgets.gui_objects import gui_view, gui_active_layout, gui_window, gui_active_technology
from lymask.utilities import active_technology, set_active_technology, \
tech_layer_properties, \
lys, reload_lys, func_info_to_func_and_kwargs
from lymask.dataprep_steps import all_dpfunc_dict, assert_valid_dataprep_steps
@@ -21,19 +21,23 @@ def _main(layout, ymlfile, tech_obj=None):
assert_valid_dataprep_steps(step_list)
for func_info in step_list:
func_name, kwargs = func_info_to_func_and_kwargs(func_info)
message('lymask doing {}'.format(func_name))
message('lymask doing {}: {}'.format(func_name, kwargs))
func = all_dpfunc_dict[func_name]
for TOP_ind in layout.each_top_cell():
# call it
func(layout.cell(TOP_ind), **kwargs)
try:
func(layout.cell(TOP_ind), **kwargs)
except Exception as err:
message_loud(str(err))
raise
return layout


def _drc_main(layout, ymlfile, tech_obj=None):
with open(ymlfile) as fx:
step_list = yaml.load(fx, Loader=yaml.FullLoader)
if func_info_to_func_and_kwargs(step_list[0])[0] != 'make_rdbcells':
step_list.insert(0, ['make_rdbcells'])
step_list.insert(0, 'make_rdbcells')
reload_lys(tech_obj, dataprep=True)
# assert_valid_drc_steps(step_list)

@@ -42,10 +46,14 @@ def _drc_main(layout, ymlfile, tech_obj=None):

for func_info in step_list:
func_name, kwargs = func_info_to_func_and_kwargs(func_info)
message('lymask doing {}'.format(func_name))
message('lymask doing {}: {}'.format(func_name, kwargs))
func = all_drcfunc_dict[func_name]
for TOP_ind in layout.each_top_cell():
func(layout.cell(TOP_ind), rdb, **kwargs)
try:
func(layout.cell(TOP_ind), rdb, **kwargs)
except Exception as err:
message_loud(str(err))
raise
return rdb


@@ -81,7 +89,7 @@ def gui_drc_main(ymlfile=None):
def batch_main(infile, ymlspec=None, technology=None, outfile=None):
# covers everything that is not GUI
if outfile is None:
outfile = infile[:-4] + '_proc.gds'
outfile = infile[:-4] + '_proc.oas'
# Load it
layout = pya.Layout()
layout.read(infile)
90 changes: 74 additions & 16 deletions lymask/library.py
Original file line number Diff line number Diff line change
@@ -4,22 +4,40 @@
from __future__ import division, print_function, absolute_import
from functools import wraps
from lymask.utilities import active_technology, lys
from lygadgets import pya, message_loud
from lygadgets import pya, message, message_loud

try:
dbu = active_technology().dbu
except AttributeError:
dbu = .001


# Metrics enum was added in v0.27
try:
Euclidian = pya.Region.Euclidian
Projection = pya.Region.Projection
except AttributeError:
Euclidian = pya.Region.Metrics.Euclidian
Projection = pya.Region.Metrics.Projection


def as_region(cell, layname):
''' Mostly a convenience brevity function.
If a layer isn't in the layer set, return an empty region instead of crashing
If a list, will return the union of the listed layers
'''
try:
return pya.Region(cell.shapes(lys[layname]))
except KeyError:
return pya.Region()
if isinstance(layname, (list, tuple)):
union = pya.Region()
for one_lay in layname:
union += as_region(cell, one_lay)
return union
else:
try:
pya_layer = lys[layname]
except KeyError:
message(f'{layname} not found in layerset.')
return pya.Region()
return pya.Region(cell.shapes(pya_layer))


_thread_count = None
@@ -57,7 +75,8 @@ def fast_smoothed(unfiltered_region, deviation=0.1):
tp.input('in1', temp_region)
tp.output('out1', output_region)
tp.queue("_output(out1, in1.smoothed({}))".format(deviation / dbu))
tp.tile_size(2000., 2000.)
# tp.tile_size(2000., 2000.) # Not sure why this was here
tp.tiles(_tiles, _tiles)
tp.tile_border(5 * deviation, 5 * deviation)
tp.threads = _thread_count
tp.execute('Smoothing job')
@@ -74,34 +93,73 @@ def fast_sized(input_region, xsize):
tp.input('in1', input_region)
tp.output('out1', output_region)
tp.queue("_output(out1, in1.sized({}))".format(xsize))
tp.tile_size(2000., 2000.)
tp.tiles(_tiles, _tiles)
tp.tile_border(2 * xsize, 2 * xsize)
tp.threads = _thread_count
tp.execute('Sizing job')
return output_region


def fast_space(input_region, spacing, angle=90):
def fast_width(input_region, width, angle=90, min_projection=0):
# if something goes wrong, you can fall back to regular here by uncommenting
if _thread_count is None:
return input_region.space_check(spacing)
return input_region.width_check(width, False, Projection, angle)
else:
output_edge_pairs = pya.Region()
output_edge_pairs = pya.EdgePairs()
tp = pya.TilingProcessor()
tp.input('in1', input_region)
tp.output('out1', output_edge_pairs)
if min_projection is None or min_projection == 0:
min_projection = 'nil'
tp.queue("_output(out1, in1.width_check({}, false, Region.Projection, {}, {}, nil))".format(width, angle, min_projection))

border = 1.1 * width
tp.tile_border(border, border)
tp.tiles(_tiles, _tiles)
tp.threads = _thread_count
tp.execute('Width check job')
return output_edge_pairs


def fast_space(input_region, spacing, angle=90, min_projection=0):
# if something goes wrong, you can fall back to regular here by uncommenting
if _thread_count is None:
return input_region.space_check(spacing, False, Projection, angle, min_projection)
else:
output_edge_pairs = pya.EdgePairs()
tp = pya.TilingProcessor()
tp.input('in1', input_region)
tp.output('out1', output_edge_pairs)
# tp.queue("_output(out1, in1.space_check({}))".format(spacing))
tp.queue("_output(out1, in1.space_check({}, false, nil, {}, nil, nil))".format(spacing, angle))
if min_projection is None or min_projection == 0:
min_projection = 'nil'
tp.queue("_output(out1, in1.space_check({}, false, Region.Projection, {}, {}, nil))".format(spacing, angle, min_projection))

border = 1.1 * spacing
tp.tile_border(border, border)
tp.tiles(_tiles, _tiles)
# bbox = input_region.bbox()
# die_wh = [bbox.width(), bbox.height()]
# tile_wh = [dim / _tiles for dim in die_wh]
# border_area = 2 * (tile_wh[0] + tile_wh[1]) * border
tp.threads = _thread_count
tp.execute('Spacing job')
tp.execute('Spacing check job')
return output_edge_pairs


def fast_separation(r1, r2, exclude):
# if something goes wrong, you can fall back to regular here by uncommenting
if _thread_count is None:
return r1.separation_check(r2, exclude)
else:
output_edge_pairs = pya.EdgePairs()
tp = pya.TilingProcessor()
tp.input('in1', r1)
tp.input('in2', r2)
tp.output('out1', output_edge_pairs)
tp.queue("_output(out1, in1.separation_check(in2, {}))".format(exclude))

border = 2 * exclude
tp.tile_border(border, border)
tp.tiles(_tiles, _tiles)
tp.threads = _thread_count
tp.execute('Separation check job')
return output_edge_pairs


Loading