1054 lines
47 KiB
Python
1054 lines
47 KiB
Python
import nazca as nd
|
|
import numpy as np
|
|
|
|
import nazca.trace as trace
|
|
import nazca.cfg as cfg
|
|
from nazca.netlist import interconnect_logger
|
|
import math as m
|
|
import nazca.cp as cp
|
|
|
|
|
|
|
|
from ..components.geometry import *
|
|
from ..components.geometry import _my_polygon
|
|
|
|
# from .dev_passive import *
|
|
|
|
def ic_exception(msg=''):
|
|
"""Raise interconnect exception if conditions apply."""
|
|
if cfg.interconnect_raise:
|
|
if cfg.interconnect_msgnum is None\
|
|
or cfg.interconnect_msgcnt == cfg.interconnect_msgnum:
|
|
raise Exception(msg)
|
|
|
|
'''
|
|
Maxwell's lib of routing for both electrics and photonics
|
|
Based on Nazca.Interconnect
|
|
'''
|
|
class Route(nd.interconnects.Interconnect):
|
|
def __init__(self, radius=None, width=None, angle=90, Ltp_mm = 10, width2_mm=1.0, MM_route = False,
|
|
xs=None, layer=None, adapt_width=False, adapt_xs=False,
|
|
instantiate=False, pinstyle=None, offset=None, varname=None, doc='', PCB=False, modes=None,sharp_patch=True):
|
|
self.PCB= PCB
|
|
self.sharp_patch = sharp_patch
|
|
self.Ltp_mm = Ltp_mm
|
|
|
|
if (width2_mm is None):
|
|
self.width2_mm = width
|
|
else :
|
|
self.width2_mm = width2_mm
|
|
|
|
self.MM_route = MM_route
|
|
|
|
if (self.PCB==True):
|
|
self.MM_route = False ## Override the multimode routing
|
|
|
|
super().__init__(radius=radius, width=width, angle=angle,
|
|
xs=xs, layer=layer, adapt_width=adapt_width, adapt_xs=adapt_xs,
|
|
instantiate=instantiate, pinstyle=pinstyle, offset=offset, varname=varname, doc=doc, PCB=PCB, modes=modes)
|
|
|
|
def connPatch(self,):
|
|
|
|
|
|
pass
|
|
|
|
def rt_bend (self,width=3,xs='strip',angle=90,layer=None,pin=None):
|
|
trace.trace_start()
|
|
pin = self._getpinout(pin)
|
|
with nd.Cell(instantiate=False) as ICcell:
|
|
if (layer==None):
|
|
for layers,growx,growy,acc in nd.layeriter(xs=xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
nd.strt(length=width*(a1-a2)+(b1-b2),width=width*(a1-a2)+(b1-b2),layer=layers).put(-(width*(a1-a2)+(b1-b2))/2,0,0)
|
|
nd.Pin('a0',io=1,xs=xs,width=pin.width).put(0,0,0)
|
|
nd.Pin('b0',io=0,xs=xs,width=pin.width).put(0,0,angle)
|
|
|
|
else :
|
|
nd.strt(length=width,width=width,layer=layer).put(-width/2,0,0)
|
|
nd.Pin('a0',io=1,xs=xs,width=pin.width).put(0)
|
|
nd.Pin('b0',io=0,xs=xs,width=pin.width).put(angle)
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
cfg.cp=pin
|
|
return ICcell
|
|
|
|
|
|
|
|
def strt_mm(self,pin=None,width2=None,Ltp=None,width1=None,Lstart=0.5,length=None,xs=None,arrow=False):
|
|
pin = self._getpinout(pin)
|
|
xs = self._getxs(pin, xs)
|
|
width1 = self._getwidth(pin, width1, xs)
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
|
|
if (Ltp==None):
|
|
Ltp = self.Ltp_mm
|
|
if (width2==None):
|
|
width2 = self.width2_mm
|
|
|
|
if pin is None:
|
|
pin = cp.here()
|
|
if (width2==None):
|
|
width2 = self._getwidth(pin, width2, xs)
|
|
|
|
if (length>(Ltp*2+Lstart*2)):
|
|
Lmm = length-Ltp*2-Lstart*2
|
|
else :
|
|
Lmm = 0
|
|
trace.trace_start()
|
|
with nd.Cell(instantiate=False) as ICcell:
|
|
|
|
if (Lmm==0):
|
|
tr = self.strt(length=length,width=width1).put(0)
|
|
|
|
nd.Pin('a0', io=0, width=width1, xs=xs).put(tr.pin['a0'])
|
|
nd.Pin('b0', io=1, width=width1, xs=xs).put(tr.pin['b0'])
|
|
|
|
else:
|
|
tr = self.strt(length=Lstart,width=width1).put(0)
|
|
self.taper(width1=width1,length=Ltp,width2=width2).put()
|
|
if (Lmm==None):
|
|
Lmm=0
|
|
self.strt(length=Lmm,width=width2).put()
|
|
self.taper(width1=width2,length=Ltp,width2=width1).put()
|
|
tl = self.strt(length=Lstart,width=width1).put()
|
|
|
|
patch = self.strt(length=0.1,width=width1).put(tr.pin['b0'].move(-0.05,0,0))
|
|
patch = self.strt(length=0.1,width=width1).put(tl.pin['a0'].move(-0.05,0,0))
|
|
|
|
nd.Pin('a0', io=0, width=width1, xs=xs).put(tr.pin['a0'])
|
|
nd.Pin('b0', io=1, width=width1, xs=xs).put(tl.pin['b0'])
|
|
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'], flip=pinflip)
|
|
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
cfg.cp=pin
|
|
|
|
return ICcell
|
|
|
|
|
|
""" Multimode swithing """
|
|
def line_mm(self,length=None,width=None,xs=None,width_mm=None,Ltp=None,):
|
|
if (self.MM_route):
|
|
return self.strt_mm(length=length,width1=width,xs=xs,width2=width_mm,Ltp=Ltp)
|
|
else:
|
|
return self.line(length,width,xs)
|
|
|
|
|
|
""" Multimode waveguide inside strt_p2p """
|
|
def strt_mm_p2p(self,pin1=None, pin2=None,width2=None,Ltp=None,width1=None,Lstart=0.5,length=None,xs=None,arrow=False,name=None):
|
|
parse, (length, b) = self._strt_p2p_solve(pin1=pin1, pin2=pin2,
|
|
width=width1, xs=xs)
|
|
pin1, pin2, xs, width, _, radius1, radius2 = parse
|
|
|
|
if (Ltp==None):
|
|
Ltp = self.Ltp_mm
|
|
if (width2==None):
|
|
width2 = self.width2_mm
|
|
|
|
if (Ltp*2 + Lstart*2 >= length):
|
|
return self.strt_p2p(pin1=pin1,pin2=pin2,width=width,xs=xs,name=None,arrow=arrow)
|
|
else :
|
|
if name is None:
|
|
name = 'ic_strt_p2p'
|
|
with nd.Cell('{}_{}'.format(name, xs), instantiate=self.instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
s1 = self.line(length=Lstart, width=width, xs=xs).put(0,0,b)
|
|
p1 = nd.Pin('a0', io=0, width=width, xs=xs).put(s1.pin['a0'].rot(-b))
|
|
|
|
|
|
s1 = self._taper(length=Ltp,width1=width,width2=width2, xs=xs).put()
|
|
|
|
e1 = self.line(length=length-Ltp*2-Lstart*2, width=width2, xs=xs).put()
|
|
|
|
d1 = self._taper(length=Ltp,width1=width2,width2=width, xs=xs).put()
|
|
|
|
d1 = self.line(length=Lstart, width=width, xs=xs).put()
|
|
|
|
p2 = nd.Pin('b0', io=1, width=width, xs=xs).put(d1.pin['b0'].rot(-b-pin1.a+pin2.a+180))
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'])
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
ICcell.pin2 = pin2
|
|
# connect_opt needed because the extra rot in p1 and p2 does hide the line optical connection:
|
|
nd.connect_path(p1, p2, trace.trace_length())
|
|
cfg.cp = pin1
|
|
return ICcell
|
|
|
|
|
|
|
|
def bend_p2p(self,pin1=None, pin2=None, radius=None, width=None, xs=None, name=None, arrow=False, width_mm=None,Ltp=None,sharp_path=True):
|
|
'''
|
|
Another package of IC.strt_bend_strt_p2p, which gives the possiblity to attach pins with right angle
|
|
'''
|
|
return self.strt_bend_strt_p2p_mine(pin1=pin1, pin2=pin2, radius=radius, width=width, xs=xs, name=name, arrow=arrow,
|
|
width_mm=width_mm,Ltp=Ltp)
|
|
|
|
|
|
|
|
def ubend_route(self, pin=None, offset=20.0, radius=None, width=None, xs=None,
|
|
length=0, name=None, arrow=False, balance=0, end_angle=False,
|
|
width_mm=None,Ltp=None):
|
|
|
|
if pin is None:
|
|
pin = cp.here()
|
|
pin2 = pin.move(0, offset, 0)
|
|
return self.ubend_p2p(pin1=pin, pin2=pin2, radius=radius, width=width, xs=xs,
|
|
length=length, name=name, arrow=arrow, balance=balance, end_angle=end_angle,width_mm=width_mm,Ltp=Ltp)
|
|
|
|
|
|
|
|
def sbend_route(self, radius=None, width=None, pin=None, xs=None, offset=20,
|
|
Ltot=None, length1=0, length2=0, name=None, arrow=False, Amax=90.0,original_function=False):
|
|
|
|
pin = self._getpinout(pin)
|
|
xs = self._getxs(pin, xs)
|
|
width = self._getwidth(pin, width, xs)
|
|
radius = self._getradius(pin, radius, xs)
|
|
|
|
if pin is None:
|
|
pin = cp.here()
|
|
|
|
## Revised bt HGL 2023.1.4
|
|
if offset < 2*radius :
|
|
offset_length = 2*np.sqrt(np.power(radius,2)-np.power(radius-np.abs(offset/2),2))
|
|
else :
|
|
offset_length = 2*radius
|
|
|
|
pin2 = pin.move(length1+length2+offset_length+1, offset, 180) ## revise in 2022.12.11
|
|
|
|
return self.sbend_p2p(pin1=pin, pin2=pin2, radius=radius, width=width, xs=xs,
|
|
Lstart=length1, name=name, arrow=arrow, BendEndFlag=1,original_function=True)
|
|
|
|
|
|
|
|
def bend_strt_bend_p2p_mine(self, pin1=None, pin2=None, radius=None,
|
|
radius1=None, radius2=None, width=None, xs=None,
|
|
length1=0, length2=0,
|
|
ictype='shortest', name=None, arrow=False):
|
|
"""Generate a point-to-point bend-straight-bend interconnect.
|
|
|
|
Args:
|
|
pin1 (Node | Instance | tuple(x, y, a)): start pin (default=cp)
|
|
pin2 (Node | Instance | tuple(x, y, a)): end pin
|
|
radius1 (float): optional first bend radius in um
|
|
radius2 (float): optional second bend radius im um
|
|
width (float): optional waveguide width in um
|
|
xs (str): optional xsection
|
|
ictype (str): interconnection type (default='shortest')
|
|
options: 'shortest', 'll', 'lr', 'rl', rr', 'all'
|
|
name (str): optional new name for the component
|
|
arrow (bool): draw connection arrows (default=True)
|
|
|
|
Returns:
|
|
Cell: bend_strt_bend element
|
|
|
|
Example:
|
|
Create and place a bend-straight-bend guide to connect two specific points::
|
|
|
|
import nazca as nd
|
|
from nazca.interconnects import Interconnect
|
|
ic = Interconnect(width=2.0, radius=10.0)
|
|
|
|
guide = ic.bend_strt_bend_p2p(pin1=(0, 0, 0), pin2=(40, 20, 90))
|
|
guide.put()
|
|
nd.export_plt()
|
|
"""
|
|
if radius is not None:
|
|
if radius1 is None:
|
|
radius1 = radius
|
|
if radius2 is None:
|
|
radius2 = radius
|
|
parse, curves = self._bend_strt_bend_p2p_solve(pin1, pin2,
|
|
xs=xs, width=width, radius1=radius1, radius2=radius2,
|
|
length1=length1, length2=length2, ictype=ictype)
|
|
pin1, pin2, xs, width, _, radius1, radius2 = parse
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
|
|
instantiate = self.instantiate
|
|
if ictype == 'all':
|
|
instantiate = True
|
|
variations = curves['variations']
|
|
else:
|
|
variations = [curves]
|
|
|
|
cells = []
|
|
if not curves['found']:
|
|
interconnect_logger(curves['message'], 'error')
|
|
return self.strt_p2p(pin1, pin2, xs='error')
|
|
|
|
else:
|
|
for curve in variations:
|
|
par = curve['solution']
|
|
shape = curve['type']
|
|
param = curve['solution']
|
|
Ltot = param['Ltot']
|
|
L = param['L']
|
|
b = param['angle1']
|
|
e = param['angle2']
|
|
if name is None:
|
|
name = 'ic_bend_strt_bend_p2p'
|
|
with nd.Cell("{}_{}".format(name, shape),
|
|
instantiate=instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
if length1 > 0:
|
|
# if (self.MM_route):
|
|
# e1 = self.strt_mm(length=length1, width1=width, xs=xs).put()
|
|
# else:
|
|
# e1 = self.line(length1, width, xs=xs).put()
|
|
|
|
e1 = self.line_mm(length=length1, width1=width, xs=xs).put()
|
|
self._circle_(radius1, angle=m.degrees(b), width=width, xs=xs,sharp_patch=self.sharp_patch).put()
|
|
# print(m.degrees())
|
|
else:
|
|
e1 = self._circle_(radius1, angle=m.degrees(b), width=width, xs=xs,sharp_patch=self.sharp_patch).put()
|
|
|
|
# if (self.MM_route):
|
|
# self.strt_mm(length=L, width1=width, xs=xs).put()
|
|
# else :
|
|
# self.line(L, width, xs).put()
|
|
self.line_mm(length=L, width=width, xs=xs).put()
|
|
|
|
self._circle_(radius2, angle=m.degrees(e), width=width, xs=xs,sharp_patch=self.sharp_patch).put()
|
|
if length2 > 0:
|
|
# if (self.MM_route):
|
|
# self.strt_mm(length=length2, width1=width, xs=xs).put()
|
|
# else:
|
|
# self.line(length2, width, xs).put()
|
|
self.line_mm(length=length2, width=width, xs=xs).put()
|
|
|
|
nd.Pin('a0', io=0, width=width, xs=xs).put(e1.pin['a0'])
|
|
nd.Pin('b0', io=1, width=width, xs=xs).put()
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'], flip=pinflip)
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
ICcell.pin2 = pin2
|
|
cfg.cp = pin1
|
|
cells.append(ICcell)
|
|
|
|
if not cells:
|
|
msg = "No solution in bend_strt_bend_p2p."
|
|
interconnect_logger(msg, 'warning')
|
|
ic_exception(msg)
|
|
|
|
elif len(cells) == 1:
|
|
return cells[0]
|
|
elif len(cells) > 1:
|
|
with nd.Cell('{}_{}'.format(name, xs), cnt=True) as ICgroup:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
for cell in cells:
|
|
cell.put(180)
|
|
trace.trace_stop()
|
|
ICgroup.length_geo = trace.trace_length()
|
|
cfg.cp = pin1
|
|
return ICgroup
|
|
|
|
|
|
|
|
def bend_route(self, radius=None, angle=None, width=None, pin=None, xs=None,
|
|
length1=0.1, length2=0.1, name=None, arrow=False, offset=None,sharp_patch=True,original_function=False):
|
|
|
|
if (original_function or self.PCB):
|
|
return self.bend(radius=radius, angle=angle, width=width, pin=pin, xs=xs,
|
|
length1=length1, length2=length2, name=name, arrow=arrow, offset=offset)
|
|
else:
|
|
return self.bend_mine(radius=radius, angle=angle, width=width, pin=pin, xs=xs,
|
|
length1=length1, length2=length2, name=name, arrow=arrow, offset=offset)
|
|
|
|
|
|
|
|
def bend_route_p2p(self, pin1=None, pin2=None, radius=None, width=None,
|
|
xs=None, name=None, arrow=False,original_function=False):
|
|
|
|
if (original_function or self.PCB):
|
|
return self.strt_bend_strt_p2p(pin1=pin1, pin2=pin2, radius=radius, width=width,
|
|
xs=xs, name=name, arrow=arrow)
|
|
else:
|
|
return self.strt_bend_strt_p2p_mine(pin1=pin1, pin2=pin2, radius=radius, width=width,
|
|
xs=xs, name=name, arrow=arrow)
|
|
|
|
def _circle_(self,radius = 10, width = 0.45, angle=360, n_points = 65,xs=None,layer=None,sharp_patch=True, theta_start=None,theta_stop=None):
|
|
with nd.Cell(instantiate=False) as C:
|
|
|
|
if (angle is not None):
|
|
theta_start = 0
|
|
theta_stop = angle
|
|
else :
|
|
angle = theta_stop - theta_start
|
|
|
|
""" """
|
|
bend_dir = np.sign(angle)
|
|
|
|
for layers,growx,growy,acc in nd.layeriter(xs=xs):
|
|
(a1,b1), (a2,b2),c1,c2 = growx
|
|
|
|
theta = np.linspace(theta_start,theta_stop,n_points)
|
|
theta = theta/180*np.pi
|
|
vtx_outer_x = np.sin(bend_dir*theta)*(radius+width*a1 + b1)
|
|
vtx_outer_y =-bend_dir*np.cos(theta)*(radius+width*a1 + b1)
|
|
vtx_outer = np.c_[vtx_outer_x,vtx_outer_y]
|
|
|
|
if (radius+width*a2+b1>0.0000001 or np.abs(angle-0)<360):
|
|
vtx_inner_x = np.sin(bend_dir*theta)*(radius+width*a2 + b2)
|
|
vtx_inner_y = -bend_dir*np.cos(theta)*(radius+width*a2 + b2)
|
|
vtx_inner = np.c_[np.flip(vtx_inner_x),np.flip(vtx_inner_y)]
|
|
|
|
vtx = np.r_[vtx_outer,vtx_inner]
|
|
else :
|
|
vtx = vtx_outer
|
|
|
|
|
|
if (sharp_patch==True and b2!=0 and b1!=0):
|
|
""" Creating rectangle for sharp-patching """
|
|
L_patch = max([max(vtx_outer_x),max(vtx_inner_x)])-min([min(vtx_outer_x),min(vtx_inner_x)])
|
|
X_patch = 1/2*(max([max(vtx_outer_x),max(vtx_inner_x)])+min([min(vtx_outer_x),min(vtx_inner_x)]))
|
|
W_patch = (max([max(vtx_outer_y),max(vtx_inner_y)])-min([min(vtx_outer_y),min(vtx_inner_y)]))
|
|
Y_patch = 1/2*(max([max(vtx_outer_y),max(vtx_inner_y)])+min([min(vtx_outer_y),min(vtx_inner_y)]))
|
|
|
|
nd.strt(length=L_patch,width=W_patch,layer=layers).put(X_patch-L_patch/2,Y_patch,0)
|
|
|
|
|
|
_my_polygon(layer_wg=layers,vtx=vtx).put(0,0,0)
|
|
|
|
nd.Pin(name='a0',width=width,xs=xs).put(radius*np.sin(bend_dir*theta_start/180*np.pi),-bend_dir*radius*np.cos(theta_start/180*np.pi),theta_start+180)
|
|
nd.Pin(name='b0',width=width,xs=xs).put(radius*np.sin(bend_dir*theta_stop/180*np.pi) ,-bend_dir*radius*np.cos(theta_stop/180*np.pi) ,theta_stop)
|
|
|
|
# if (angle>0):
|
|
# nd.Pin(name='a0',width=width,xs=xs).put(radius*np.cos(0/180*np.pi),radius*np.sin(0/180*np.pi),0-90)
|
|
# nd.Pin(name='b0',width=width,xs=xs).put(radius*np.cos(angle/180*np.pi),radius*np.sin(angle/180*np.pi),angle+90)
|
|
# else :
|
|
# nd.Pin(name='a0',width=width,xs=xs).put(radius*np.cos(0/180*np.pi),radius*np.sin(0/180*np.pi), 90)
|
|
# nd.Pin(name='b0',width=width,xs=xs).put(radius*np.cos(angle/180*np.pi),radius*np.sin(angle/180*np.pi),270+angle)
|
|
|
|
return C
|
|
|
|
def bend_mine(self, radius=None, angle=None, width=None, pin=None, xs=None,
|
|
length1=0, length2=0, name=None, arrow=False, offset=None,sharp_patch=True):
|
|
|
|
pin = self._getpinout(pin)
|
|
xs = self._getxs(pin, xs)
|
|
width = self._getwidth(pin, width, xs)
|
|
radius = self._getradius(pin, radius, xs)
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
|
|
if offset is None:
|
|
offset = self.offset
|
|
|
|
if angle is None:
|
|
angle = self.angle
|
|
if name is None:
|
|
name = 'ic_bend'
|
|
with nd.Cell('{}_{}'.format(name, xs), instantiate=self.instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
if length1 > 0:
|
|
# if (self.MM_route):
|
|
# e1 = self.strt_mm(length=length1, width1=width, xs=xs).put(0)
|
|
# else:
|
|
# e1 = self.line(length=length1, width=width, xs=xs).put(0)
|
|
|
|
e1 = self.line_mm(length=length1, width=width, xs=xs).put(0)
|
|
|
|
e2 = self._circle_(radius=radius, width=width, angle=angle, xs=xs,sharp_patch=self.sharp_patch).put()
|
|
else:
|
|
e1 =self._circle_(radius=radius, width=width, angle=angle, xs=xs,sharp_patch=self.sharp_patch).put()
|
|
e2 = e1
|
|
|
|
if length2 > 0:
|
|
# if (self.MM_route):
|
|
# e2 = self.strt_mm(length=length2, width1=width, xs=xs).put()
|
|
# else:
|
|
# e2 = self.line(length=length2, width=width, xs=xs).put()
|
|
e2 = self.line_mm(length=length2, width=width, xs=xs).put()
|
|
|
|
nd.Pin('b0', io=1, width=width, xs=xs, pin=e2.pin['b0']).put()
|
|
nd.Pin('a0', io=0, width=width, xs=xs, pin=e1.pin['a0']).put()
|
|
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'], flip=pinflip)
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
|
|
if pin is not None:
|
|
cfg.cp = pin
|
|
return ICcell
|
|
|
|
def tube_mine(self, geo, showpins=False, name=None, xs=None, arrow=False,Ltp=None,width_mm=None,sharp_patch=None):
|
|
"""draw interconnect based on symbols.
|
|
|
|
Returns:
|
|
Cell: Tube element based on the provided elements
|
|
"""
|
|
|
|
if (sharp_patch is None):
|
|
sharp_patch = self.sharp_patch
|
|
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
if name is None:
|
|
name = 'ic_tube'
|
|
with nd.Cell(name, instantiate=self.instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
el_list=[]
|
|
new_geo=[]
|
|
for i, tube in enumerate(geo):
|
|
if tube[0] == 's':
|
|
if abs(tube[1][0])<1.e-5:
|
|
continue
|
|
if (self.MM_route):
|
|
el_list.append(self.strt_mm(length=tube[1][0], width1=tube[1][1], arrow=False,Ltp=Ltp,width2=width_mm).put())
|
|
else:
|
|
el_list.append(self.strt(length=tube[1][0], width=tube[1][1], arrow=False).put())
|
|
|
|
new_geo.append(tube)
|
|
elif tube[0] == 'b':
|
|
if abs(tube[1][0])<1.e-5:
|
|
continue
|
|
if (len(el_list)>0):
|
|
angle_pre = el_list[-1].pin['b0'].a
|
|
else:
|
|
angle_pre = 0
|
|
el_list.append(self._circle_(theta_start=angle_pre,theta_stop=angle_pre + tube[1][0], angle=None,
|
|
radius=tube[1][1], width=tube[1][2],xs=xs,sharp_patch=sharp_patch).put())
|
|
|
|
# el_list.append(self.bend(angle=tube[1][0],radius=tube[1][1], width=tube[1][2]).put())
|
|
|
|
# el_list.append(self._circle_(theta_start=0,theta_stop=tube[1][0], angle=None,
|
|
# radius=tube[1][1], width=tube[1][2],xs=xs,sharp_patch=self.sharp_patch).put())
|
|
# el_list.append(self._circle_(angle=tube[1][0], radius=tube[1][1], width=tube[1][2],xs=xs,sharp_patch=self.sharp_patch).put())
|
|
|
|
new_geo.append(tube)
|
|
else:
|
|
cfg.interconnect_errcnt += 1
|
|
msg = "IC-{} Tube element not recognized {} in cell '{}'".\
|
|
format(cfg.interconnect_errcnt, tube, cfg.cells[-1].cell_name)
|
|
interconnect_logger(msg, 'error')
|
|
ic_exception(msg)
|
|
if el_list:
|
|
nd.Pin('a0', io=0, width=el_list[0].pin['a0'].width, xs=xs).put(el_list[0].pin['a0'])
|
|
nd.Pin('b0', io=1, width=el_list[-1].pin['b0'].width, xs=xs).put(el_list[-1].pin['b0'])
|
|
else: # nothing to place. TODO: Handle in cell.put(), where empty cells can be skipped, e.g. with cell.void = True
|
|
nd.Pin('a0', io=0, width=None, xs=xs).put(0, 0, 180)
|
|
nd.Pin('b0', io=1, width=None, xs=xs).put(0, 0, 0)
|
|
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'], flip=pinflip)
|
|
return ICcell
|
|
|
|
def strt_bend_strt_p2p_mine(self, pin1=None, pin2=None, radius=None,
|
|
radius1=None, radius2=None, width=None, xs=None,
|
|
length1=0, length2=0,
|
|
ictype='shortest', name=None, arrow=False,
|
|
width_mm=None,Ltp =None):
|
|
|
|
parse, params = self._strt_bend_strt_p2p_solve(pin1=pin1, pin2=pin2,
|
|
xs=xs, width=width, radius=radius)
|
|
pin1, pin2, xs, width, _, radius1, radius2 = parse
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
|
|
if params['found']:
|
|
solution = params['solution']
|
|
L1 = solution['length1']
|
|
L2 = solution['length2']
|
|
da = solution['da']
|
|
|
|
if name is None:
|
|
name = 'ic_strt_bend_strt_p2p'
|
|
cfg.cp = pin1
|
|
if (self.PCB==True):
|
|
C = self.tube(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow)
|
|
else:
|
|
C = self.tube_mine(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow,width_mm=width_mm,Ltp=Ltp)
|
|
|
|
C.pin2 = pin2
|
|
return C
|
|
|
|
else:
|
|
interconnect_logger(params['message'], 'warning')
|
|
ic_exception(params['message'])
|
|
|
|
# goto bend_strt_bend:
|
|
return self.bend_strt_bend_p2p_mine(pin1, pin2, radius1=radius,
|
|
radius2=radius, width=width, xs=xs, arrow=arrow, name=name)
|
|
|
|
## HGL's sbend ###############################################################################################
|
|
|
|
def sbend_p2p_mine(self, pin1=None, pin2=None, width=None, radius=None, Lstart=0, doFirst=1, arrow=False,
|
|
width_mm=None,Ltp=None,sharp_patch=False):
|
|
"""
|
|
Create pin-to-pin s-bend interconnect. This code is made by myself to avoid the possible gap between two bend
|
|
|
|
The direction of the end pin is ignored and is assumed to be parallel with pin1.
|
|
|
|
Args:
|
|
pin1 (Node | Instance | tuple(x, y, a)): start pin (default=cp)
|
|
pin2 (Node | Instance | tuple(x, y, a)): end pin
|
|
width (float): width of the interconnect in um
|
|
radius (float): bend radius of the interconnect in um
|
|
xs (str): optional xsection of sbend
|
|
doFirst (int): (default=1)
|
|
Lstart (float): straight waveguide length at beginning (positive
|
|
value) or end (negative value) of sbend
|
|
arrow (bool): draw connection arrows (default=True)
|
|
|
|
Returns:
|
|
Cell: sbend element
|
|
|
|
Example:
|
|
Create and place a sbend to connect two specific points::
|
|
|
|
import nazca as nd
|
|
from nazca.interconnects import Interconnect
|
|
ic = Interconnect(width=2.0, radius=10.0)
|
|
|
|
guide = ic.sbend_p2p(pin1=(0), pin2=(40, 20))
|
|
guide.put()
|
|
nd.export_plt()
|
|
"""
|
|
# if pin1.width != pin1.width:
|
|
# print("--Be carefull that width of two ends of this sbend does not equal to each other.--")
|
|
res = 0.2
|
|
parse = self._p2p_parse(pin1, pin2)
|
|
pin1, pin2, _, _, _, _, _ = parse
|
|
|
|
if width is None:
|
|
width = self.width
|
|
if radius is None:
|
|
radius = self.radius
|
|
## Add code to solve the problem when (pin1.a-pin2.a)!=180
|
|
angle_offset = 180-abs(pin1.a-pin2.a)
|
|
if abs(angle_offset)>0.01:
|
|
bend = self.bend(radius=radius,width=width,angle=-angle_offset*np.sign(pin1.a-pin2.a),xs=self.xs).put(pin2)
|
|
pin2 = bend.pin['b0']
|
|
## Do sbend connecting
|
|
a = pin1.a
|
|
a = a*np.pi/180
|
|
lateral_length = (pin2.x-pin1.x)*np.cos(a) + (pin2.y-pin1.y)*np.sin(a)
|
|
offset_length = -(pin2.x-pin1.x)*np.sin(a) + (pin2.y-pin1.y)*np.cos(a)
|
|
cp.goto_pin(pin1)
|
|
with nd.Cell(name="my_sbend", instantiate=False, cnt=True) as ICcell:
|
|
## Add Lstart strt
|
|
strt = nd.strt(length=Lstart, width=width, xs=self.xs).put(0, 0)
|
|
lateral_length = lateral_length - Lstart
|
|
## Add sbend
|
|
if abs(offset_length) >= 2*radius:
|
|
bend1 = nd.bend(radius=radius, width=width, xs=self.xs, angle=90*np.sign(offset_length)).put()
|
|
nd.strt(length=abs(offset_length)-2*radius, width=width, xs=self.xs).put()
|
|
bend2 = nd.bend(radius=radius, width=width, xs=self.xs, angle=-90*np.sign(offset_length)).put()
|
|
nd.strt(length=lateral_length-2*radius, width=width, xs=self.xs).put()
|
|
# Add patch
|
|
for lay, grow, acc, polyline in nd.layeriter(xs=self.xs):
|
|
(a1, b1), (a2, b2), c1, c2 = grow
|
|
if b1!=0 and b2!=0:
|
|
if offset_length>0:
|
|
patch_poly = [
|
|
(0,width*a2+b2),
|
|
(abs(radius)-(width*a2+b2),width*a2+b2),
|
|
(abs(radius)-(width*a2+b2),abs(radius)),
|
|
(0,abs(radius))
|
|
]
|
|
nd.Polygon(points=patch_poly,layer=lay).put(bend1.pin['a0'].move(0,0,180))
|
|
patch_poly = [
|
|
(0,-(width*a1+b1)),
|
|
(abs(radius)+(width*a1+b1),-(width*a1+b1)),
|
|
(abs(radius)+(width*a1+b1),abs(radius)),
|
|
(0,abs(radius))
|
|
]
|
|
nd.Polygon(points=patch_poly,layer=lay).put(bend2.pin['b0'].move(0,0,180))
|
|
elif offset_length<0:
|
|
patch_poly = [
|
|
(0,(width*a1+b1)),
|
|
(abs(radius)+(width*a1+b1),(width*a1+b1)),
|
|
(abs(radius)+(width*a1+b1),-abs(radius)),
|
|
(0,-abs(radius))
|
|
]
|
|
nd.Polygon(points=patch_poly,layer=lay).put(bend1.pin['a0'].move(0,0,180))
|
|
patch_poly = [
|
|
(0,-(width*a2+b2)),
|
|
(abs(radius)-(width*a2+b2),-(width*a2+b2)),
|
|
(abs(radius)-(width*a2+b2),-abs(radius)),
|
|
(0,-abs(radius))
|
|
]
|
|
nd.Polygon(points=patch_poly,layer=lay).put(bend2.pin['b0'].move(0,0,180))
|
|
if lateral_length-2*radius < 0:
|
|
print("--Be carefull that the length of last strt in strt-sbend-strt is negative.--")
|
|
elif offset_length == 0:
|
|
nd.strt(length=lateral_length, width=width, xs=self.xs).put()
|
|
elif abs(offset_length) < 2*radius:
|
|
# Sbend parameter
|
|
sbend_length = 2 * np.sqrt(np.power(radius,2) - np.power(radius-abs(offset_length)/2,2))
|
|
theta_range = np.arccos((radius-abs(offset_length)/2) / radius)
|
|
theta_step = res / radius
|
|
theta_list = np.arange(0, theta_range, theta_step)
|
|
# Center of two bend
|
|
center1 = {"x":0, "y":radius*np.sign(offset_length)}
|
|
center2 = {"x":sbend_length, "y":offset_length-radius*np.sign(offset_length)}
|
|
for lay, grow, acc, polyline in nd.layeriter(xs=self.xs):
|
|
(a1, b1), (a2, b2), c1, c2 = grow
|
|
Ro = radius + width * a1 + b1
|
|
Ri = radius + width * a2 + b2
|
|
p1 = [(center1["x"] + Ri*np.sin(theta),
|
|
center1["y"] - np.sign(offset_length)*Ri*np.cos(theta))
|
|
for theta in theta_list]
|
|
p2 = [(center2['x'] - Ro*np.sin(theta),
|
|
center2['y'] + np.sign(offset_length)*Ro*np.cos(theta))
|
|
for theta in reversed(theta_list)]
|
|
p3 = [(center2['x'] - Ri*np.sin(theta),
|
|
center2['y'] + np.sign(offset_length)*Ri*np.cos(theta))
|
|
for theta in theta_list]
|
|
p4 = [(center1["x"] + Ro*np.sin(theta),
|
|
center1["y"] - np.sign(offset_length)*Ro*np.cos(theta))
|
|
for theta in reversed(theta_list)]
|
|
sbend_poly = p1 + p2 + p3 + p4
|
|
nd.Polygon(points=sbend_poly, layer=lay).put()
|
|
if b1!=0 and b2!=0:
|
|
if offset_length>0:
|
|
patch_poly = [
|
|
(0,width*a2+b2), (sbend_length,width*a2+b2),
|
|
(sbend_length,width*a1+b1+offset_length),
|
|
(0,width*a1+b1+offset_length)
|
|
]
|
|
nd.Polygon(points=patch_poly,layer=lay).put(strt.pin['b0'])
|
|
elif offset_length<0:
|
|
patch_poly = [
|
|
(0,width*a1+b1), (sbend_length,width*a1+b1),
|
|
(sbend_length,width*a2+b2+offset_length),
|
|
(0,width*a2+b2+offset_length)
|
|
]
|
|
nd.Polygon(points=patch_poly,layer=lay).put(strt.pin['b0'])
|
|
nd.strt(length=lateral_length-sbend_length, width=width, xs=self.xs).put(Lstart+sbend_length, offset_length)
|
|
nd.Pin(name='a0').put(0, 0, 180)
|
|
nd.Pin(name='b0').put(lateral_length+Lstart, offset_length, 0)
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'])
|
|
|
|
return ICcell
|
|
|
|
def sbend_p2p(self, pin1=None, pin2=None, width=None, radius=None, Amax=90,
|
|
xs=None, doStrFirst=1, Lstart=0, BendEndFlag=1, ref=None,
|
|
name=None, arrow=False, bsb=True, sharp_patch=None,
|
|
width_mm=None,Ltp=None):
|
|
|
|
# if (original_function or self.PCB==True or self.sharp_patch==False):
|
|
if (sharp_patch is None):
|
|
sharp_patch = self.sharp_patch
|
|
|
|
""" Note : This part is directly copy from sbend_p2p in nazca.Interconnect """
|
|
parse, params = self._sbend_p2p_solve(
|
|
pin1=pin1, pin2=pin2, width=width, radius=radius, length1=Lstart,
|
|
doStrFirst=doStrFirst, ref=ref, Amax=Amax)
|
|
|
|
pin1, pin2, xs, width, _, radius1, radius2 = parse
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
|
|
if not params['found'] or abs(params['solution']['angle']) < 0*m.pi:
|
|
interconnect_logger(params['message'], 'info')
|
|
#TODO: alterative back up if La is so long that Lb is negative?
|
|
if bsb:
|
|
msg = "replacing sbend with bend_strt_bend."
|
|
interconnect_logger(msg, 'info')
|
|
return self.bend_strt_bend_p2p(
|
|
pin1=pin1, pin2=pin2, radius=radius, width=width, xs=xs,
|
|
name=name, arrow=arrow, width_mm=width_mm, Ltp=Ltp)
|
|
else:
|
|
if (self.MM_route and self.PCB is False):
|
|
return self.strt_mm_p2p(pin1=pin1,pin2=pin2,xs='error',arrow=arrow,width2=width_mm,Ltp=Ltp)
|
|
else:
|
|
return self.strt_p2p(pin1, pin2, xs='error', arrow=arrow)
|
|
|
|
else:
|
|
if name is None:
|
|
name = 'ic_sbend'
|
|
cfg.cp = pin1
|
|
if (self.MM_route==False or self.PCB==True or sharp_patch==False):
|
|
C = self.tube(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow)
|
|
else:
|
|
C = self.tube_mine(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow, width_mm=width_mm, Ltp=Ltp,sharp_patch=sharp_patch)
|
|
|
|
C.pin2 = pin2
|
|
return C
|
|
""" Original Function ends """
|
|
|
|
# else :
|
|
# return self.sbend_p2p_mine(pin1=pin1, pin2=pin2, width=width, radius=radius, Lstart=Lstart, doFirst=doStrFirst, arrow=arrow)
|
|
|
|
def bend_strt_bend_p2p(self, pin1=None, pin2=None, radius=None,
|
|
radius1=None, radius2=None, width=None, xs=None,
|
|
length1=0, length2=0,
|
|
ictype='shortest', name=None, arrow=True,
|
|
width_mm=None,Ltp=None,
|
|
):
|
|
"""Generate a point-to-point bend-straight-bend interconnect.
|
|
|
|
Args:
|
|
pin1 (Node | Instance | tuple(x, y, a)): start pin (default=cp)
|
|
pin2 (Node | Instance | tuple(x, y, a)): end pin
|
|
radius1 (float): optional first bend radius in um
|
|
radius2 (float): optional second bend radius im um
|
|
width (float): optional waveguide width in um
|
|
xs (str): optional xsection
|
|
ictype (str): interconnection type (default='shortest')
|
|
options: 'shortest', 'll', 'lr', 'rl', rr', 'all'
|
|
name (str): optional new name for the component
|
|
arrow (bool): draw connection arrows (default=True)
|
|
|
|
Returns:
|
|
Cell: bend_strt_bend element
|
|
|
|
Example:
|
|
Create and place a bend-straight-bend guide to connect two specific points::
|
|
|
|
import nazca as nd
|
|
from nazca.interconnects import Interconnect
|
|
ic = Interconnect(width=2.0, radius=10.0)
|
|
|
|
guide = ic.bend_strt_bend_p2p(pin1=(0, 0, 0), pin2=(40, 20, 90))
|
|
guide.put()
|
|
nd.export_plt()
|
|
"""
|
|
if radius is not None:
|
|
if radius1 is None:
|
|
radius1 = radius
|
|
if radius2 is None:
|
|
radius2 = radius
|
|
parse, curves = self._bend_strt_bend_p2p_solve(pin1, pin2,
|
|
xs=xs, width=width, radius1=radius1, radius2=radius2,
|
|
length1=length1, length2=length2, ictype=ictype)
|
|
pin1, pin2, xs, width, _, radius1, radius2 = parse
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
|
|
instantiate = self.instantiate
|
|
if ictype == 'all':
|
|
instantiate = True
|
|
variations = curves['variations']
|
|
else:
|
|
variations = [curves]
|
|
|
|
cells = []
|
|
if not curves['found']:
|
|
interconnect_logger(curves['message'], 'error')
|
|
return self.strt_p2p(pin1, pin2, xs='error')
|
|
|
|
else:
|
|
for curve in variations:
|
|
par = curve['solution']
|
|
shape = curve['type']
|
|
param = curve['solution']
|
|
Ltot = param['Ltot']
|
|
L = param['L']
|
|
b = param['angle1']
|
|
e = param['angle2']
|
|
if name is None:
|
|
name = 'ic_bend_strt_bend_p2p'
|
|
with nd.Cell("{}_{}".format(name, shape),
|
|
instantiate=instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
|
|
if length1 > 0:
|
|
e1 = self.line_mm(length=length1, width=width, xs=xs, width_mm=width_mm, Ltp=Ltp).put()
|
|
self._circle_(radius=radius1, angle=m.degrees(b), width=width, xs=xs).put()
|
|
|
|
else:
|
|
e1 = self._circle_(radius=radius1, angle=m.degrees(b), width=width, xs=xs).put()
|
|
|
|
self.line_mm(length=L, width=width, xs=xs, width_mm=width_mm, Ltp=Ltp).put()
|
|
self._circle_(radius=radius2, angle=m.degrees(e), width=width, xs=xs).put()
|
|
|
|
if length2 > 0:
|
|
self.line_mm(length=length2, width=width, xs=xs, width_mm=width_mm, Ltp=Ltp).put()
|
|
nd.Pin('a0', io=0, width=width, xs=xs).put(e1.pin['a0'])
|
|
nd.Pin('b0', io=1, width=width, xs=xs).put()
|
|
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'], flip=pinflip)
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
ICcell.pin2 = pin2
|
|
cfg.cp = pin1
|
|
cells.append(ICcell)
|
|
|
|
if not cells:
|
|
msg = "No solution in bend_strt_bend_p2p."
|
|
interconnect_logger(msg, 'warning')
|
|
ic_exception(msg)
|
|
|
|
elif len(cells) == 1:
|
|
return cells[0]
|
|
elif len(cells) > 1:
|
|
with nd.Cell('{}_{}'.format(name, xs), cnt=True) as ICgroup:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
for cell in cells:
|
|
cell.put(180)
|
|
trace.trace_stop()
|
|
ICgroup.length_geo = trace.trace_length()
|
|
cfg.cp = pin1
|
|
return ICgroup
|
|
|
|
""" Functions Reconstructed with <arrow = False> default """
|
|
def ubend_p2p(self, pin1=None, pin2=None, radius=None, width=None, xs=None,
|
|
length=0.1, name=None, arrow=False, balance=0, end_angle=False,original_function=False,Ltp=None,width_mm=None):
|
|
parse, params = self._ubend_p2p_solve(
|
|
pin1,
|
|
pin2,
|
|
length=length,
|
|
width=width,
|
|
radius=radius,
|
|
balance=balance,
|
|
end_angle=end_angle
|
|
)
|
|
pin1, pin2, xs, width, _, radius1, radius2 = parse
|
|
|
|
if name is None:
|
|
name = 'ic_ubend_p2p'
|
|
cfg.cp = pin1
|
|
if (original_function or self.PCB):
|
|
C = self.tube(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow)
|
|
C.pin2 = pin2
|
|
else:
|
|
C = self.tube_mine(geo=params['geo'], name='{}_{}'.format(name, xs), xs=xs, arrow=arrow,Ltp=Ltp,width_mm=width_mm)
|
|
C.pin2 = pin2
|
|
return C
|
|
|
|
def strt(self, length=None, width=None, pin=None, xs=None, edge1=None,
|
|
edge2=None, edgepoints=50, name=None, arrow=False, gridpatch=None):
|
|
pin = self._getpinout(pin)
|
|
xs = self._getxs(pin, xs)
|
|
width = self._getwidth(pin, width, xs)
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
if gridpatch is None:
|
|
gridpatch = self.gridpatch
|
|
if length is None:
|
|
length = self.length
|
|
if name is None:
|
|
name = 'ic_strt'
|
|
with nd.Cell('{}_{}'.format(name, xs), instantiate=self.instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
e1 = self.line(length=length, width=width, xs=xs, edge1=edge1,
|
|
edge2=edge2, edgepoints=edgepoints, gridpatch=gridpatch).put(0)
|
|
nd.Pin('a0', io=0, width=width, xs=xs).put(e1.pin['a0'])
|
|
nd.Pin('b0', io=1, width=width, xs=xs).put()
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'], flip=pinflip)
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
|
|
if pin is not None:
|
|
cfg.cp = pin
|
|
return ICcell
|
|
|
|
def taper(self, length=None, width1=None, width2=None, shift=0, xs=None, pin=None,
|
|
name=None, patch=False, arrow=False):
|
|
pin = self._getpinout(pin)
|
|
xs = self._getxs(pin, xs)
|
|
width1 = self._getwidth(pin, width1, xs, adapt=False)
|
|
width2 = self._getwidth(pin, width2, xs, adapt=False)
|
|
pinflip = not nd.get_xsection(xs).symmetry
|
|
|
|
if length is None:
|
|
length = self.length
|
|
|
|
with nd.Cell('taper_{}'.format(xs), instantiate=self.instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
e1 = self._taper(length=length, width1=width1, width2=width2,
|
|
shift=shift, xs=xs).put(0)
|
|
nd.Pin('a0', io=0, width=width1, xs=xs).put(e1.pin['a0'])
|
|
nd.Pin('b0', io=1, width=width2, xs=xs).put()
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'], flip=pinflip)
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
if patch :
|
|
width_max = np.max(np.array([width1,width2]))
|
|
for lay, grow, acc, polyline in nd.layeriter(xs=self.xs):
|
|
if "CLD" in lay :
|
|
(a1, b1), (a2, b2), c1, c2 = grow
|
|
nd.strt(length=length,width=width_max*(a1-a2)+b1-b2,layer=lay).put(0,0,0)
|
|
|
|
if pin is not None:
|
|
cfg.cp = pin
|
|
return ICcell
|
|
|
|
def taper_p2p(self, pin1=None, pin2=None, width1=None, width2=None, xs=None, name=None, arrow=False ):
|
|
save_adapt = self.adapt_width
|
|
self.adapt_width = True # Adapt taper width to what it connects to.
|
|
parse = self._p2p_parse(
|
|
pin1=pin1,
|
|
pin2=pin2,
|
|
width1=width1,
|
|
width2=width2,
|
|
xs=xs
|
|
)
|
|
pin1, pin2, xs, width1, width2, radius1, radius2 = parse
|
|
length, shift, _ = nd.diff(pin1, pin2)
|
|
if name is None:
|
|
name = 'ic_taper_p2p'
|
|
with nd.Cell(f'{name}_{xs}', instantiate=self.instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
e1 = self._taper(
|
|
length=length,
|
|
width1=width1,
|
|
width2=width2,
|
|
shift=shift,
|
|
xs=xs
|
|
).put(0, 0, pin1.xya()[2])
|
|
p1 = nd.Pin('a0', io=0, width=width1, xs=xs).put(e1.pin['a0'])
|
|
p2 = nd.Pin('b0', io=1, width=width2, xs=xs).put(e1.pin['b0'])
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'])
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
ICcell.pin2 = pin2
|
|
# connect_opt needed because the extra rot in p1 and p2 does hide the line
|
|
# optical connection:
|
|
nd.connect_path(p1, p2, trace.trace_length())
|
|
cfg.cp = pin1
|
|
self.adapt_width = save_adapt # restore previous value
|
|
return ICcell
|
|
|
|
def strt_p2p(self, pin1=None, pin2=None, width=None, xs=None, name=None, arrow=False ):
|
|
parse, (length, b) = self._strt_p2p_solve(pin1=pin1, pin2=pin2,
|
|
width=width, xs=xs)
|
|
pin1, pin2, xs, width, _, radius1, radius2 = parse
|
|
if name is None:
|
|
name = 'ic_strt_p2p'
|
|
with nd.Cell('{}_{}'.format(name, xs), instantiate=self.instantiate, cnt=True) as ICcell:
|
|
ICcell.group = 'interconnect'
|
|
trace.trace_start()
|
|
e1 = self.line(length=length, width=width, xs=xs).put(0, 0, b)
|
|
p1 = nd.Pin('a0', io=0, width=width, xs=xs).put(e1.pin['a0'].rot(-b))
|
|
p2 = nd.Pin('b0', io=1, width=width, xs=xs).put(e1.pin['b0'].rot(-b-pin1.a+pin2.a+180))
|
|
if arrow:
|
|
self.arrow.put(ICcell.pin['a0'])
|
|
self.arrow.put(ICcell.pin['b0'])
|
|
trace.trace_stop()
|
|
ICcell.length_geo = trace.trace_length()
|
|
ICcell.pin2 = pin2
|
|
# connect_opt needed because the extra rot in p1 and p2 does hide the line optical connection:
|
|
nd.connect_path(p1, p2, trace.trace_length())
|
|
cfg.cp = pin1
|
|
return ICcell
|
|
|