Skip to content

Commit

Permalink
opt_in_ & opt_ support
Browse files Browse the repository at this point in the history
allow for verification with either opt_in_ or opt_ format.
The opt_ format was adopted by SiEPIClab for electrical-optical testing.
  • Loading branch information
lukasc-ubc committed Feb 13, 2023
1 parent 270a50f commit 331ae3a
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 97 deletions.
166 changes: 84 additions & 82 deletions klayout_dot_config/python/SiEPIC/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2561,93 +2561,95 @@ def layout_check(cell=None, verbose=False):

# opt_in labels
for ti1 in range(0, len(opt_in)):
t = opt_in[ti1]['Text']
box_s = 1000
box = pya.Box(t.x - box_s, t.y - box_s, t.x + box_s, t.y + box_s)
# opt_in labels check for unique
for ti2 in range(ti1 + 1, len(opt_in)):
if opt_in[ti1]['opt_in'] == opt_in[ti2]['opt_in']:
if verbose:
print(" - Found DFT error, non unique text labels: %s, %s, %s" %
(t.string, t.x, t.y))
rdb_item = rdb.create_item(rdb_cell.rdb_id(), rdb_cat_id_optin_unique.rdb_id())
rdb_item.add_value(pya.RdbItemValue(t.string))
rdb_item.add_value(pya.RdbItemValue(pya.Polygon(box).to_dtype(dbu)))

# opt_in format check:
if not opt_in[ti1]['wavelength'] in DFT_wavelengths:
if verbose:
print(" - DFT error: wavelength")
rdb_item = rdb.create_item(rdb_cell.rdb_id(), rdb_cat_id_optin_wavelength.rdb_id())
rdb_item.add_value(pya.RdbItemValue(pya.Polygon(box).to_dtype(dbu)))

if not (opt_in[ti1]['pol'] in DFT_polarizations):
if verbose:
print(" - DFT error: polarization")
rdb_item = rdb.create_item(
rdb_cell.rdb_id(), rdb_cat_id_optin_polarization.rdb_id())
rdb_item.add_value(pya.RdbItemValue(pya.Polygon(box).to_dtype(dbu)))

# find the GC closest to the opt_in label.
from ._globals import KLAYOUT_VERSION
components_sorted = sorted([c for c in components if [p for p in c.pins if p.type == _globals.PIN_TYPES.OPTICALIO]],
key=lambda x: x.trans.disp.to_p().distance(pya.Point(t.x, t.y).to_dtype(1)))
# GC too far check:
if components_sorted:
dist_optin_c = components_sorted[0].trans.disp.to_p(
).distance(pya.Point(t.x, t.y).to_dtype(1))
if verbose:
print(" - Found opt_in: %s, nearest GC: %s. Locations: %s, %s. distance: %s" % (opt_in[ti1][
'Text'], components_sorted[0].instance, components_sorted[0].center, pya.Point(t.x, t.y), dist_optin_c * dbu))
if dist_optin_c > float(DFT['design-for-test']['opt_in']['max-distance-to-grating-coupler']) * 1000:
if 'opt_in' in opt_in[ti1]:
t = opt_in[ti1]['Text']
box_s = 1000
box = pya.Box(t.x - box_s, t.y - box_s, t.x + box_s, t.y + box_s)
# opt_in labels check for unique
for ti2 in range(ti1 + 1, len(opt_in)):
if 'opt_in' in opt_in[ti2]:
if opt_in[ti1]['opt_in'] == opt_in[ti2]['opt_in']:
if verbose:
print(" - Found DFT error, non unique text labels: %s, %s, %s" %
(t.string, t.x, t.y))
rdb_item = rdb.create_item(rdb_cell.rdb_id(), rdb_cat_id_optin_unique.rdb_id())
rdb_item.add_value(pya.RdbItemValue(t.string))
rdb_item.add_value(pya.RdbItemValue(pya.Polygon(box).to_dtype(dbu)))

# opt_in format check:
if not opt_in[ti1]['wavelength'] in DFT_wavelengths:
if verbose:
print(" - opt_in label too far from the nearest grating coupler: %s, %s" %
(components_sorted[0].instance, opt_in[ti1]['opt_in']))
rdb_item = rdb.create_item(rdb_cell.rdb_id(), rdb_cat_id_optin_toofar.rdb_id())
print(" - DFT error: wavelength")
rdb_item = rdb.create_item(rdb_cell.rdb_id(), rdb_cat_id_optin_wavelength.rdb_id())
rdb_item.add_value(pya.RdbItemValue(pya.Polygon(box).to_dtype(dbu)))

# starting with each opt_in label, identify the sub-circuit, then GCs, and
# check for GC spacing
trimmed_nets, trimmed_components = trim_netlist(
nets, components, components_sorted[0])
components_connected_opt_in = components_connected_opt_in + trimmed_components
detector_GCs = [c for c in trimmed_components if [p for p in c.pins if p.type == _globals.PIN_TYPES.OPTICALIO] if (
c.trans.disp - components_sorted[0].trans.disp).to_p() != pya.DPoint(0, 0)]
if verbose:
print(" N=%s, detector GCs: %s" %
(len(detector_GCs), [c.display() for c in detector_GCs]))
vect_optin_GCs = [(c.trans.disp - components_sorted[0].trans.disp).to_p()
for c in detector_GCs]
# for vi in range(0,len(detector_GCs)):
# if round(angle_vector(vect_optin_GCs[vi])%180)!=int(DFT['design-for-test']['grating-couplers']['gc-array-orientation']):
# if verbose:
# print( " - DFT GC pitch or angle error: angle %s, %s" % (round(angle_vector(vect_optin_GCs[vi])%180), opt_in[ti1]['opt_in']) )
# rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_GCpitch.rdb_id())
# rdb_item.add_value(pya.RdbItemValue( detector_GCs[vi].polygon.to_dtype(dbu) ) )

# find the GCs in the circuit that don't match the testing configuration
import numpy as np
array_angle = float(DFT['design-for-test']['grating-couplers']['gc-array-orientation'])
pitch = float(DFT['design-for-test']['grating-couplers']['gc-pitch'])
sx = np.round(np.cos(array_angle/180*np.pi))
sy = np.round(np.sin(array_angle/180*np.pi))

for d in list(range(int(DFT['design-for-test']['grating-couplers']['detectors-above-laser']) + 0, 0, -1)) + list(range(-1, -int(DFT['design-for-test']['grating-couplers']['detectors-below-laser']) - 1, -1)):
if pya.DPoint(d * sx* pitch * 1000, d *sy* pitch * 1000) in vect_optin_GCs:
del_index = vect_optin_GCs.index(pya.DPoint(
d * sx* pitch * 1000, d *sy* pitch * 1000))
del vect_optin_GCs[del_index]
del detector_GCs[del_index]
for vi in range(0, len(vect_optin_GCs)):

if not (opt_in[ti1]['pol'] in DFT_polarizations):
if verbose:
print(" - DFT GC array config error: %s, %s" %
(components_sorted[0].instance, opt_in[ti1]['opt_in']))
print(" - DFT error: polarization")
rdb_item = rdb.create_item(
rdb_cell.rdb_id(), rdb_cat_id_GCarrayconfig.rdb_id())
rdb_item.add_value(pya.RdbItemValue(
"The label having the error is: " + opt_in[ti1]['opt_in']))
rdb_item.add_value(pya.RdbItemValue(detector_GCs[vi].polygon.to_dtype(dbu)))
rdb_cell.rdb_id(), rdb_cat_id_optin_polarization.rdb_id())
rdb_item.add_value(pya.RdbItemValue(pya.Polygon(box).to_dtype(dbu)))

# find the GC closest to the opt_in label.
from ._globals import KLAYOUT_VERSION
components_sorted = sorted([c for c in components if [p for p in c.pins if p.type == _globals.PIN_TYPES.OPTICALIO]],
key=lambda x: x.trans.disp.to_p().distance(pya.Point(t.x, t.y).to_dtype(1)))
# GC too far check:
if components_sorted:
dist_optin_c = components_sorted[0].trans.disp.to_p(
).distance(pya.Point(t.x, t.y).to_dtype(1))
if verbose:
print(" - Found opt_in: %s, nearest GC: %s. Locations: %s, %s. distance: %s" % (opt_in[ti1][
'Text'], components_sorted[0].instance, components_sorted[0].center, pya.Point(t.x, t.y), dist_optin_c * dbu))
if dist_optin_c > float(DFT['design-for-test']['opt_in']['max-distance-to-grating-coupler']) * 1000:
if verbose:
print(" - opt_in label too far from the nearest grating coupler: %s, %s" %
(components_sorted[0].instance, opt_in[ti1]['opt_in']))
rdb_item = rdb.create_item(rdb_cell.rdb_id(), rdb_cat_id_optin_toofar.rdb_id())
rdb_item.add_value(pya.RdbItemValue(pya.Polygon(box).to_dtype(dbu)))

# starting with each opt_in label, identify the sub-circuit, then GCs, and
# check for GC spacing
trimmed_nets, trimmed_components = trim_netlist(
nets, components, components_sorted[0])
components_connected_opt_in = components_connected_opt_in + trimmed_components
detector_GCs = [c for c in trimmed_components if [p for p in c.pins if p.type == _globals.PIN_TYPES.OPTICALIO] if (
c.trans.disp - components_sorted[0].trans.disp).to_p() != pya.DPoint(0, 0)]
if verbose:
print(" N=%s, detector GCs: %s" %
(len(detector_GCs), [c.display() for c in detector_GCs]))
vect_optin_GCs = [(c.trans.disp - components_sorted[0].trans.disp).to_p()
for c in detector_GCs]
# for vi in range(0,len(detector_GCs)):
# if round(angle_vector(vect_optin_GCs[vi])%180)!=int(DFT['design-for-test']['grating-couplers']['gc-array-orientation']):
# if verbose:
# print( " - DFT GC pitch or angle error: angle %s, %s" % (round(angle_vector(vect_optin_GCs[vi])%180), opt_in[ti1]['opt_in']) )
# rdb_item = rdb.create_item(rdb_cell.rdb_id(),rdb_cat_id_GCpitch.rdb_id())
# rdb_item.add_value(pya.RdbItemValue( detector_GCs[vi].polygon.to_dtype(dbu) ) )

# find the GCs in the circuit that don't match the testing configuration
import numpy as np
array_angle = float(DFT['design-for-test']['grating-couplers']['gc-array-orientation'])
pitch = float(DFT['design-for-test']['grating-couplers']['gc-pitch'])
sx = np.round(np.cos(array_angle/180*np.pi))
sy = np.round(np.sin(array_angle/180*np.pi))

for d in list(range(int(DFT['design-for-test']['grating-couplers']['detectors-above-laser']) + 0, 0, -1)) + list(range(-1, -int(DFT['design-for-test']['grating-couplers']['detectors-below-laser']) - 1, -1)):
if pya.DPoint(d * sx* pitch * 1000, d *sy* pitch * 1000) in vect_optin_GCs:
del_index = vect_optin_GCs.index(pya.DPoint(
d * sx* pitch * 1000, d *sy* pitch * 1000))
del vect_optin_GCs[del_index]
del detector_GCs[del_index]
for vi in range(0, len(vect_optin_GCs)):
if verbose:
print(" - DFT GC array config error: %s, %s" %
(components_sorted[0].instance, opt_in[ti1]['opt_in']))
rdb_item = rdb.create_item(
rdb_cell.rdb_id(), rdb_cat_id_GCarrayconfig.rdb_id())
rdb_item.add_value(pya.RdbItemValue(
"The label having the error is: " + opt_in[ti1]['opt_in']))
rdb_item.add_value(pya.RdbItemValue(detector_GCs[vi].polygon.to_dtype(dbu)))
rdb_item.add_value(pya.RdbItemValue(pya.Polygon(box).to_dtype(dbu)))

# subtract components connected to opt_in labels from all components to
# find circuits with missing opt_in
Expand Down
48 changes: 33 additions & 15 deletions klayout_dot_config/python/SiEPIC/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1029,19 +1029,31 @@ def layout_pgtext(cell, layer, x, y, text, mag, inv=False):
cell.insert(pya.CellInstArray(pcell.cell_index(), pya.Trans(pya.Trans.R0, x / dbu, y / dbu)))


'''
return all opt_in labels:
text_out: HTML text
opt_in: a Dictionary
'''


def find_automated_measurement_labels(topcell=None, LayerTextN=None, GUI=False):
# example usage:
# topcell = pya.Application.instance().main_window().current_view().active_cellview().cell
# LayerText = pya.LayerInfo(10, 0)
# LayerTextN = topcell.layout().layer(LayerText)
# find_automated_measurement_labels(topcell, LayerTextN)
"""return all opt_in labels from a cell
requires a layout with Text labels on the layer LayerTextN
the format of the labels is
opt_in_<polarization>_<wavelength>_<type>_<deviceID>_<params>
or
opt_<polarization>_<wavelength>_<type>_<deviceID>_<params>
or
elec_<deviceID>_<params>
for electrical-optical measurements, the deviceID on the electrical contact
needs to match that of the optical input
returns:
text_out: HTML text
opt_in: a Dictionary
example usage:
topcell = pya.Application.instance().main_window().current_view().active_cellview().cell
LayerText = pya.LayerInfo(10, 0)
LayerTextN = topcell.layout().layer(LayerText)
find_automated_measurement_labels(topcell, LayerTextN)
"""

import string
if not LayerTextN:
from . import get_technology, find_paths
Expand Down Expand Up @@ -1078,7 +1090,12 @@ def find_automated_measurement_labels(topcell=None, LayerTextN=None, GUI=False):
i += 1
text2 = iter.shape().text.transformed(iter.itrans())
texts.append(text2)
fields = text.string.split("_")
if 'opt_in' in text.string:
# allow for either opt_ or opt_in_ formats
textlabel = text.string
else:
textlabel = text.string.replace('opt_','opt_in_')
fields = textlabel.split("_")
while len(fields) < 7:
fields.append('comment')
if GUI == True:
Expand All @@ -1098,13 +1115,14 @@ def find_automated_measurement_labels(topcell=None, LayerTextN=None, GUI=False):
duplicate = True
else:
device_ids.add(fields[5])
opt_in.append({'opt_in': text.string, 'x': int(text2.x * dbu), 'y': int(text2.y * dbu), 'pol': fields[
2], 'wavelength': fields[3], 'type': fields[4], 'deviceID': fields[5], 'params': fields[6:], 'Text': text2})
opt_in.append({'opt_in': textlabel, 'x': int(text2.x * dbu), 'y': int(text2.y * dbu), 'pol': fields[2],
'wavelength': fields[3], 'type': fields[4],
'deviceID': fields[5], 'params': fields[6:], 'Text': text2})
params_txt = ''
for f in fields[6:]:
params_txt += ', ' + str(f)
text_out += "%s, %s, %s, %s, %s, %s%s<br>" % (int(text2.x * dbu), int(text2.y * dbu), fields[
2], fields[3], fields[4], fields[5], params_txt)
text_out += "%s, %s, %s, %s, %s, %s%s<br>" % (int(text2.x * dbu), int(text2.y * dbu), fields[2],
fields[3], fields[4], fields[5], params_txt)
iter.next()

text_out += "<br>"
Expand Down

0 comments on commit 331ae3a

Please sign in to comment.