def draw(self): name = self.name o = self.origin pad_x = self.size.x if pad_x > self.pitch * 9 / 10: pad_x = self.pitch * 9 / 10 warnings.warn("Pad size too large, capped to pitch*9/10") pad_cell=pg.rectangle(size=(pad_x,self.size.y),\ layer=self.layer) pad_cell.move(origin=(0,0),\ destination=o.coord) cell = Device(self.name) dp = Point(self.pitch, 0) pad_gnd_sx = cell << pad_cell pad_sig = cell << pad_cell pad_sig.move(origin=o.coord,\ destination=(o+dp).coord) pad_gnd_dx = cell << pad_cell pad_gnd_dx.move(origin=o.coord,\ destination=(o+dp*2).coord) cell.add_port(Port(name='sig',\ midpoint=(o+Point(pad_x/2+self.pitch,self.size.y)).coord,\ width=pad_x,\ orientation=90)) cell.add_port(Port(name='gnd_left',\ midpoint=(o+Point(pad_x/2,self.size.y)).coord,\ width=pad_x,\ orientation=90)) cell.add_port(Port(name='gnd_right',\ midpoint=(o+Point(pad_x/2+2*self.pitch,self.size.y)).coord,\ width=pad_x,\ orientation=90)) return cell
def draw(self): supercell=cls.draw(self) cell=pt.Device(self.name) master_ref=cell.add_ref(supercell,alias='Device') add_passivation(cell, self.passivation_margin, self.passivation_scale, self.passivation_layer, self.passivation_absolute_mode) r=st.get_corners(cell) if issubclass(cls,pc.SMD): bottom_port=Port( name='S_1', width=self.size.x, orientation=270, midpoint=r.s.coord) top_port=Port( name='N_2', width=self.size.x, orientation=90, midpoint=r.n.coord) cell.add_port(top_port) cell.add_port(bottom_port) cell.add(pr.route_quad( bottom_port, master_ref.ports['S_1'], layer=self.layer)) cell.add(pr.route_quad( top_port, master_ref.ports['N_2'], layer=self.layer)) else: ppt.copy_ports(supercell,cell) return cell
def get_centroid_ports(*ports): '''Calculate port with matching orientation and with as input ports, and midpoint at the centroid of the input ports midpoint. Attributes: ---------- *ports : dl.Port Returns: ------- dl.Port. ''' if not ports: raise ValueError(f"st.get_centroid_ports(): no ports passed") if isinstance(ports, Port): return ports else: ports_centroid = get_centroid(*[Point(x.midpoint) for x in ports]) ports_width = np.average([x.width for x in ports]) ports_orientation = np.average([x.orientation for x in ports]) return Port(orientation=ports_orientation, width=ports_width, midpoint=ports_centroid.coord)
def draw(self): if self.shape == 'square': cell=pg.rectangle(size=(self.size,self.size),\ layer=self.layer) elif self.shape == 'circle': cell=pg.circle(radius=self.size/2,\ layer=self.layer) else: raise ValueError("Via shape can be \'square\' or \'circle\'") cell.move(origin=(0,0),\ destination=self.origin.coord) cell.add_port(Port(name='conn',\ midpoint=cell.center,\ width=cell.xmax-cell.xmin,\ orientation=90)) return cell
def draw(self): ''' Generates layout cell based on current parameters. 'top' and 'bottom' ports are included in the cell. Returns ------- cell : phidl.Device. ''' unitcell = self._draw_unit_cell() cell = Device(self.name) cell.name = self.name cell.add_array(unitcell,columns=self.n,rows=1,\ spacing=(self.pitch*2,0)) cell.flatten() totx = self.pitch * (self.n * 2 + 1) - self.pitch * (1 - self.coverage) midx = totx / 2 finger_dist=Point(self.pitch*1,\ self.length+self.y_offset) cell = join(cell) cell.name = self.name cell.add_port(Port(name='bottom',\ midpoint=(self.origin+\ Point(midx,0)).coord,\ width=totx, orientation=-90)) cell.add_port(Port(name='top',\ midpoint=(self.origin+\ Point(midx,self.length+self.y_offset)).coord,\ width=totx, orientation=90)) del unitcell return cell
def draw(self): ''' Generates layout cell based on current parameters. 'top' and 'bottom' ports is included in the cell. Returns ------- cell : phidl.Device. ''' o = self.origin b_main = Bus() b_main.origin = o b_main.layer = self.layer b_main.size = Point(self.x, self.active_area.y) b_main.distance = Point(self.active_area.x + self.x, 0) main_etch = b_main.draw() etch = Device(self.name) etch.absorb(etch << main_etch) port_up=Port('top',\ midpoint=(o+Point(self.x+self.active_area.x/2,self.active_area.y)).coord,\ width=self.active_area.x,\ orientation=-90) port_down=Port('bottom',\ midpoint=(o+Point(self.x+self.active_area.x/2,0)).coord,\ width=self.active_area.x,\ orientation=90) etch.add_port(port_up) etch.add_port(port_down) del main_etch return etch
def draw(self): supercell = cls.draw(self) cell = pt.Device(self.name) master_ref = cell.add_ref(supercell, alias='Device') add_passivation(cell, self.passivation_margin, self.passivation_scale, self.passivation_layer) if issubclass(cls, pc.SMD): bottom_port = Port(name='S_1', width=self.size.x, orientation=270, midpoint=s.coord) top_port = Port(name='N_2', width=self.size.x, orientation=90, midpoint=n.coord) cell.add_port(top_port) cell.add_port(bottom_port) cell.add( pt._make_poly_connection(bottom_port, master_ref.ports['S_1'], layer=self.layer)) cell.add( pt._make_poly_connection(top_port, master_ref.ports['N_2'], layer=self.layer)) else: pt._copy_ports(supercell, cell) return cell
def _get_centroid_ports(ports): if not ports: raise ValueError( f"pt._get_centroid_ports(): no ports with tag {tag} in device {cell.name}" ) ports_centroid = _get_centroid(*[Point(x.midpoint) for x in ports]) ports_width = np.average([x.width for x in ports]) ports_orientation = np.average([x.orientation for x in ports]) return Port(orientation=ports_orientation, width=ports_width, midpoint=ports_centroid.coord)
def draw(self): cell=Device(name=self.name) super_ref=cell.add_ref(cls.draw(self)) nvias_x,nvias_y=self.n_vias unit_cell=self._draw_padded_via() viacell=join(CellArray(unit_cell,\ columns=nvias_x,rows=nvias_y,\ spacing=(unit_cell.xsize,unit_cell.ysize))) viacell.add_port(Port(name='conn',\ midpoint=(viacell.x,viacell.ymax),\ width=viacell.xsize,\ orientation=90)) for sides in side: for p_name in super_ref.ports.keys(): if re.search(sides,p_name): p=super_ref.ports[p_name] pad=pg.compass(size=(p.width,self.via_distance),layer=self.pad_layers[0]) if sides=='top': self._attach_instance(cell, pad, pad.ports['S'], viacell,p) if sides=='bottom': self._attach_instance(cell, pad, pad.ports['N'], viacell,p) for p_name,p_value in super_ref.ports.items(): cell.add_port(p_value) return cell
def shift_port(port, dist): '''Returns new port with translated midpoint. Attributes: ---------- port : dl.Port dist : float Returns: dl.Port. ''' port_mid_norm = Point(port.normal[1]) - Point(port.normal[0]) midpoint_projected = Point(port.midpoint) + port_mid_norm * dist new_port = Port(name=port.name + 'shifted', orientation=port.orientation, width=port.width, midpoint=midpoint_projected.coord) return new_port
def draw_array(cell: Device, x: int, y: int, row_spacing: float = 0, column_spacing: float = 0) -> Device: ''' returns a spaced matrix of identical cells, copying ports in the original cells. Parameters ---------- cell : phidl.Device x : int columns of copies y : int rows of copies row_spacing: float column_spacing: float Returns ------- cell : phidl.Device. ''' new_cell = pg.Device(cell.name + "array") cell_size = Point(cell.size) + Point(column_spacing, row_spacing) cellmat = [] ports = [] for j in range(y): cellvec = [] for i in range(x): cellvec.append( new_cell.add_ref(cell, alias=cell.name + str(i) + str(j))) cellvec[i].move(destination=(Point(cell_size.x * i, cell_size.y * j)).coord) for p in cellvec[i].ports.values(): ports.append(Port(name=p.name+str(i),\ midpoint=p.midpoint,\ width=p.width,\ orientation=p.orientation)) cellmat.append(cellvec) for p in ports: new_cell.add_port(p) del cellmat return new_cell
class LayoutDefault: '''container of pirel constants.''' origin = Point(0, 0) layerSi = 0 layerBottom = 1 layerTop = 2 layerPad = 3 layerEtch = 4 layerVias = 5 layerPartialEtch = 6 layerBackSide = 7 layerMask = 99 #text TextParams={'font':"BebasNeue-Regular.otf",'size':125.0,'location':'top',\ 'distance':Point(0,100),'label':"default",'layer':layerTop} #IDT IDT_y = 200.0 IDTpitch = 20.0 IDTcoverage = 0.7 IDTy_offset = 10.0 IDTlayer = layerTop IDTn = 4 #Bus Bussize = Point(IDTpitch * 2 * IDTn, IDTpitch * 2) Busdistance = Point(0, IDT_y + IDTy_offset) #EtchPit EtchPit_x = Bussize.x / 4 EtchPitactive_area=Point(Bussize.x,\ IDT_y+IDTy_offset+2*Bussize.y) EtchPitlayer = layerEtch #Anchor Anchorsize=Point(IDTpitch*IDTn/4,\ 2*Bussize.y) Anchor_metalized = Point(Anchorsize.x - 4, Anchorsize.y + 4) Anchoretch_x = EtchPit_x Anchorx_offset = 0.0 Anchorlayer = IDTlayer Anchoretch_layer = EtchPitlayer Anchoretch_choice = True #LFERes LFEResactive_area_margin = 0.5 #FBERes FBEResplatelayer = layerBottom #GSProbe GSProbepitch = 150.0 GSProbepad_size = Point(80, 80) GSProbelayer = layerTop GSProberouting = True GSProbespacing = Point(20, 30) #Via Vialayer = layerVias Viashape = 'circle' Viasize = 20 #GSGProbe GSGProbelayer = layerTop GSGProbepitch = 200.0 GSGProbesize = Point(100, 100) GSProbelayer = GSGProbelayer GSProbepitch = GSGProbepitch GSProbesize = GSGProbesize #TFERes TFEResbottomlayer = layerBottom #Routing Routingtrace_width = 80.0 Routingclearance = ((0, 250), (300, 550)) Routinglayer = layerTop Routingports=(Port(name='1',midpoint=(450,0),\ width=50,orientation=90),\ Port(name='2',midpoint=(100,550),\ width=50,orientation=90),) Routingside = 'auto' #MultiRouting MultiRoutingsources = (Routingports[0], ) MultiRoutingdestinations=(Port(name='2',midpoint=(100,550),\ width=50,orientation=90),\ Port(name='3',midpoint=(200,80),\ width=50,orientation=-90)) #GSGProbe_LargePad GSGProbe_LargePadground_size = 200.0 #ParametricArray Arrayx_spacing = 50.0 Arrayx_param = {"IDTPitch": [_ for _ in range(1, 4)]} Arraylabels_top = ["IDTP" + str(x) for x in range(1, 4)] Arraylabels_bottom = [str(x) for x in range(1, 4)] #ParametricMatrix Matrixy_param = {"IDTLength": [_ for _ in range(100, 400, 100)]} Matrixy_spacing = Arrayx_spacing Matrixlabels_top=[ ["IDTP"+str(x)+y for x in range(1,4)]\ for y in ["L"+str(z) for z in range(100,400,100)]] Matrixlabels_bottom=[ [str(x)+str(y) for x in range(1,4)] \ for y in range(100,400,100)] #Stack Stackn = 4 #Pad Padsize = 80.0 Padlayer = IDTlayer Paddistance = 30.0 Padport=Port(name='top',midpoint=(50,50),width=100,\ orientation=-90) #array arraybusextlength = 30.0 #addVia addVia_over_via = 2.0 addVia_via_area = Point(100, 100) addVia_via_distance = 40
class LayoutDefault: '''container of pirel constants.''' origin = Point(0, 0) layerSi = 0 layerBottom = 1 layerTop = 2 layerPad = 3 layerEtch = 4 layerVias = 5 layerPartialEtch = 6 layerBackSide = 7 layerPassivation = layerPartialEtch layerMask = 99 #text TextParams={'font':"BebasNeue-Regular.otf",'size':125.0,'location':'top',\ 'distance':Point(0,100),'label':"default",'layer':layerTop} #IDT IDT_y = 200.0 IDTpitch = 20.0 IDTcoverage = 0.7 IDTy_offset = 10.0 IDTlayer = layerTop IDTn = 4 #Bus Bussize = Point(IDTpitch * 2 * IDTn, IDTpitch * 2) Busdistance = Point(0, IDT_y + IDTy_offset) #EtchPit EtchPit_x = Bussize.x / 4 EtchPitactive_area=Point(Bussize.x,\ IDT_y+IDTy_offset+2*Bussize.y) EtchPitlayer = layerEtch #Anchor Anchorsize=Point(IDTpitch*IDTn/4,\ 2*Bussize.y) Anchor_metalized = Point(Anchorsize.x - 4, Anchorsize.y + 4) Anchoretch_x = EtchPit_x Anchorx_offset = 0.0 Anchorlayer = IDTlayer Anchoretch_layer = EtchPitlayer Anchoretch_choice = True #MultiAnchor MultiAnchorn = 1 #LFERes LFEResactive_area_margin = 0.5 #FBERes FBEResplatelayer = layerBottom #Via Vialayer = layerVias Viashape = 'square' Viasize = 20 #Probe Probesiglayer = layerTop Probegroundlayer = {layerTop, layerBottom} Probepitch = 200.0 Probesize = Point(100, 100) LargePadground_size = 250 #TFERes TFEResbottomlayer = layerBottom #Routing Routingtrace_width = None Routingclearance = ((0, 0), (0, 0)) Routinglayer = layerTop Routingports=(Port(name='1',midpoint=(450,0),\ width=50,orientation=90),\ Port(name='2',midpoint=(100,550),\ width=50,orientation=90),) Routingtype = 'manhattan' Routingoverhang = 20 Routingside = 'auto' #MultiRouting MultiRoutingsources = (Routingports[0], ) MultiRoutinglayer = {layerBottom, layerTop} MultiRoutingdestinations=(Port(name='2',midpoint=(100,550),\ width=50,orientation=90),\ Port(name='3',midpoint=(200,80),\ width=50,orientation=-90)) #Probe_LargePad Probe_LargePadground_size = 200.0 #ParametricArray Arrayx_spacing = 50.0 Arrayx_param = {"IDTPitch": [_ for _ in range(1, 4)]} Arraylabels_top = ["IDTP" + str(x) for x in range(1, 4)] Arraylabels_bottom = [str(x) for x in range(1, 4)] #ParametricMatrix Matrixy_param = {"IDTLength": [_ for _ in range(100, 400, 100)]} Matrixy_spacing = Arrayx_spacing Matrixlabels_top=[ ["IDTP"+str(x)+y for x in range(1,4)]\ for y in ["L"+str(z) for z in range(100,400,100)]] Matrixlabels_bottom=[ [str(x)+str(y) for x in range(1,4)] \ for y in range(100,400,100)] #Stack Stackn = 4 #Pad Padsize = 80.0 Padlayer = IDTlayer Paddistance = 30.0 Padport=Port(name='top',midpoint=(50,50),width=100,\ orientation=-90) #TwoPortProbe TwoPortProbeoffset = Point(0, 200) #Text TextSize = 100 TextLabel = 'Default' TextLayer = layerTop TextFont = str( pathlib.Path(__file__).parent / 'addOns' / 'BebasNeue-Regular.otf') #Npath NPathCommLength = 100 NPathSpacing = Point(0, 0) #SMD SMDDistance = Point(0, 500) SMDSize = Point(200, 300) SMDLayer = layerTop #Passivation PassivationMargin = Point(1.5, 1.2) PassivationScale = Point(1.8, 1.5) PassivationLayer = layerPassivation
def _draw_device_grounds(self, device_cell, probe_cell, label): output_cell = Device() lx_device_ports = [] rx_device_ports = [] gnd_ports = pt._find_ports(device_cell, 'Ground', depth=0) if not gnd_ports: return output_cell for p in gnd_ports: p_adj = Port(name=p.name, midpoint=p.midpoint, width=probe_cell['Port1'].size[1], orientation=p.orientation) if "LX" in p.name: lx_device_ports.append(p_adj) elif "RX" in p.name: rx_device_ports.append(p_adj) r = pc.MultiRouting() r.layer = self.gndlefttrace.layer r.clearance = pt._bbox_to_tuple(self._bbox_mod(device_cell.bbox)) r.trace_width = self.gnd_routing_width ## left connections if label == 'side': r.source = tuple( pt._find_ports(probe_cell, 'GroundLXW', depth=0)) elif label == 'straight': r.source = (probe_cell.ports['GroundLXN_1'], ) r.destination = tuple(lx_device_ports) r.side = 'left' output_cell.absorb(output_cell << r.draw()) ## right connections if label == 'side': r.source = tuple( pt._find_ports(probe_cell, 'GroundRXE', depth=0)) elif label == 'straight': r.source = (probe_cell.ports['GroundRXN_1'], ) r.destination = tuple(rx_device_ports) r.side = 'right' output_cell.absorb(output_cell << r.draw()) return output_cell
def draw(self): cell=pt.Device(name=self.name) oldprobe=cell<<probe.draw(self) cell.absorb(oldprobe) groundpad=pg.compass(size=(self.ground_size,self.ground_size),\ layer=self.layer) [_,_,ul,ur]=get_corners(groundpad) for name,p in oldprobe.ports.items(): name=p.name if 'gnd' in name: groundref=cell<<groundpad if 'left' in name: groundref.move(origin=ur.coord,\ destination=p.endpoints[1]) left_port=groundref.ports['N'] elif 'right' in name: groundref.move(origin=ul.coord,\ destination=p.endpoints[0]) right_port=groundref.ports['N'] cell.absorb(groundref) else : cell.add_port(p) for name,port in oldprobe.ports.items(): if 'gnd' in name: if 'left' in name: if self.pad_position=='side': left_port=Port(name=name,\ midpoint=(left_port.midpoint[0]+self.ground_size/2,\ left_port.midpoint[1]-self.ground_size/2),\ orientation=180,\ width=self.ground_size) elif self.pad_position=='top': left_port=Port(name=name,\ midpoint=(left_port.midpoint[0],\ left_port.midpoint[1]),\ orientation=90,\ width=self.ground_size) else : raise ValueError(f"New pad position is {self.pad_position} : not acceptable") cell.add_port(left_port) elif 'right' in name: if self.pad_position=='side': right_port=Port(name=name,\ midpoint=(right_port.midpoint[0]-self.ground_size/2,\ right_port.midpoint[1]-self.ground_size/2),\ orientation=0,\ width=self.ground_size) elif self.pad_position=='top': right_port=Port(name=name,\ midpoint=(right_port.midpoint[0],\ right_port.midpoint[1]),\ orientation=90,\ width=self.ground_size) else : raise ValueError(f"New pad position is {self.pad_position} : not acceptable") cell.add_port(right_port) return cell
from phidl import set_quickplot_options set_quickplot_options(blocking=True) distance = 50 width = 10 spacing = 50 n = 4 d = Device() for i in range(n): d.add_port( Port( midpoint=(spacing * i, 0), # midpoint=(0,spacing*i), width=width, orientation=90, name='top_' + str(i))) connector = connect_ports(d, tag='top', distance=distance) d << connector for p in connector.get_ports(depth=0): d.add_port(connector.ports[p.name]) qp(d)
def connect_ports(cell: Device, tag: str = 'top', layers: tuple = (LayoutDefault.layerTop, ), distance: float = 10.0, metal_width: float = None): ''' connects all the ports in the cell with name matching a tag. Parameters: ----------- cell : Device tag: str conn_dist: pt.Point offset from port location layer : tuple. Returns: ---------- cell : Device contains routings and connection port ''' import pirel.pcells as pc ports = pt._find_ports(cell, tag, depth=0) ports.sort(key=lambda x: x.midpoint[0]) ports_centroid = pt._get_centroid_ports(ports) if len(ports) == 1: raise ValueError("pm.connect_ports() : len(ports) must be >1 ") if metal_width is None: metal_width = ports_centroid.width port_mid_norm = pt.Point(ports_centroid.normal[1]) - pt.Point( ports_centroid.normal[0]) midpoint_projected = Point( ports_centroid.midpoint) + port_mid_norm * (distance + metal_width) pad_side = ports_centroid.width new_port = Port(name=tag, orientation=ports_centroid.orientation, width=ports_centroid.width, midpoint=midpoint_projected.coord) output_cell = Device() for p in ports: straight_conn = output_cell << pt._draw_multilayer( 'compass', layers, size=(p.width, abs(midpoint_projected.y - p.midpoint[1]))) straight_conn.connect("S", p) cross_conn = output_cell << pt._draw_multilayer( 'compass', layers, size=(output_cell.xsize, metal_width)) cross_conn.connect('S', new_port, overlap=metal_width) output = pt.join(output_cell) output.add_port(new_port) return output