def draw(self): def mirror_label(str): if 'N_' in str: return str.replace('N_', 'S_') if 'E_' in str: return str.replace('E_', 'W_') if 'S_' in str: return str.replace('S_', 'N_') if 'W_' in str: return str.replace('W_', 'E_') return str cell = Device(name=self.name) probe_cell = super().draw() p1 = cell.add_ref(probe_cell, alias='Port1') p2 = cell.add_ref(probe_cell, alias='Port2') p2.rotate(center=(p1.x, p1.ymax), angle=180) p2.move(destination=self.offset.coord) for n, p in p1.ports.items(): cell.add_port(port=p, name=n + '_1') for n, p in p2.ports.items(): if 'LX' in n: cell.add_port(port=p, name=mirror_label(n + '_2').replace( 'LX', 'RX')) else: if 'RX' in n: cell.add_port(port=p, name=mirror_label(n + '_2').replace( 'RX', 'LX')) else: cell.add_port(port=p, name=mirror_label(n + '_2')) return cell
def draw(self): self._set_relations() device_cell=cls.draw(self) probe_cell=self.probe.draw() cell=Device(name=self.name) cell.add_ref(device_cell, alias="Device") self._add_signal_connection(cell,'bottom') probe_ref=cell.add_ref(probe_cell, alias="Probe") self._move_probe_ref(cell) self._setup_signal_routing(cell) cell.absorb(cell<<self._draw_signal_routing()) try: self._setup_ground_routing( cell, 'straight') routing_cell=self._draw_ground_routing() except: self._setup_ground_routing( cell, 'side') try: routing_cell=self._draw_ground_routing() except: import pdb; pdb.set_trace() self._draw_ground_routing() _add_default_ground_vias(self,routing_cell) cell.add_ref(routing_cell,alias=self.name+"GroundTrace") return cell
def route_turn_manhattan(port1, port2, layer=0, radius=20): """ Mahattan routing between two ports. If directions are not cardinal, adds a turn to make cardinal and then routes. Parameters ---------- port1, port2: Port objects Ports to route to and from layer: int (default: 0) Layer to use for the routes radius: float (default: 20) Curve radius for bends Returns ---------- Device object Notes ---------- If direction is not cardinal, will route to nearest cardinal, then call route_manhattan. """ D = Device() new_ports = [] for port in (port1, port2): if port.orientation % 90 == 0: new_ports.append(port) else: turn_angle = get_turn_angle(port.orientation, to_cardinal(port.orientation)) turn_route = turn(port, radius=radius, angle=turn_angle, layer=layer) D.add_ref(turn_route) new_ports.append(turn_route.ports[2]) #Manhattan on new ports route = route_manhattan(new_ports[0], new_ports[1], bendType='circular', layer=layer, radius=radius) D.add_ref(route) return D
def add_pads(cell, pad, tag='top', exact=False): ''' add pad designs to a cell, connecting it to selected ports. Parameters: ----------- cell : Device pad : pt.LayoutPart tags : str (or iterable of str) used to find ports ''' if not isinstance(tag, str): ports = [] for t in tag: ports.extend(pt._find_ports(cell, t, depth=0, exact=exact)) else: ports = pt._find_ports(cell, tag, depth=0, exact=exact) pad_cell = Device() for port in ports: pad.port = port pad_ref = pad_cell.add_ref(pad.draw()) pad_ref.connect('conn', destination=port) cell.add_ref(pad_cell, alias="Pad")
def add_vias(cell: Device, bbox, via: pt.LayoutPart, spacing: float = 0, tolerance: float = 0): ''' adds via pattern to cell with constraints. Parameters: ----------- cell : Device where the vias will be located bbox : iterable x,y coordinates of box ll and ur to define vias patterns size via : pt.LayoutPart spacing : float tolerance : float (default 0) if not 0, used to determine via distance from target object border Returns: -------- cell_out : Device ''' bbox_cell = pg.bbox(bbox) nvias_x = int(np.floor(bbox_cell.xsize / (via.size + spacing))) nvias_y = int(np.floor(bbox_cell.ysize / (via.size + spacing))) if nvias_x == 0 or nvias_y == 0: return via_cell = pt.draw_array(via.draw(), nvias_x, nvias_y, row_spacing=spacing, column_spacing=spacing) via_cell.move(origin=(via_cell.x, via_cell.y), destination=(bbox_cell.x, bbox_cell.y)) tbr = [] for elem in via_cell.references: if not pt.is_cell_inside(elem, cell, tolerance): tbr.append(elem) via_cell.remove(tbr) cell.absorb(cell.add_ref(via_cell, alias="Vias"))
def draw(self): self._setup_routings() device_cell=cls.draw(self) probe_cell=self.probe.draw() cell=Device(name=self.name) cell.add_ref(device_cell, alias=self.name+"Device") probe_ref=cell.add_ref(probe_cell, alias=self.name+"Probe") self._move_probe_ref(probe_ref) cell.add_ref(self._draw_probe_routing(),alias=self.name+"GndTrace") return cell
def draw(self): cell = Device(self.name) d_ref = cell.add_ref(cls.draw(self), alias='Device') pt._copy_ports(d_ref, cell) add_pads(cell, self.pad, side) return cell
def draw(self): device_cell=cls.draw(self) cell=Device(name=self.name) device_ref=cell.add_ref(device_cell, alias="Device") self._setup_probe(device_ref) probe_cell=self.probe.draw() probe_ref=cell.add_ref(probe_cell, alias="Probe") for tag in ('top','bottom'): self._add_signal_connection(cell,tag) self._move_probe_ref(cell) self._setup_ground_routing(device_ref,probe_ref,'side') routing_cell=self._draw_ground_routing() self._setup_signal_routing(cell) routing_cell.add(self._draw_device_grounds(device_ref,probe_ref,'side')) routing_cell.add(self._draw_ground_extensions(cell)) cell.add_ref(routing_cell,alias="GroundTrace") routing_cell.add(self._draw_ground_extensions(cell)) _add_default_ground_vias(self,routing_cell) routing_cell.add(self._draw_signal_routing()) return cell
def draw(self): cell=Device() cell.name=self.name d_ref=cell.add_ref(cls.draw(self)) for name,port in d_ref.ports.items(): self.pad.port=port pad_ref=cell.add_ref(self.pad.draw()) pad_ref.connect(pad_ref.ports['conn'], destination=port) cell.absorb(pad_ref) cell.add_port(port,name) return cell
def draw(self): cell = Device(name=self.name) lfe_cell = LFERes.draw(self) cell.add_ref(lfe_cell, alias='TopCell') idt_bottom = copy(self.idt) idt_bottom.layer = self.bottom_layer idt_ref = cell.add_ref(idt_bottom.draw(), alias='BottomIDT') p_bott = idt_ref.ports['bottom'] p_bott_coord = Point(p_bott.midpoint) idt_ref.mirror(p1=(p_bott_coord-Point(p_bott.width/2,0)).coord,\ p2=(p_bott_coord+Point(p_bott.width/2,0)).coord) idt_ref.move(origin=(idt_ref.xmin,idt_ref.ymax),\ destination=(idt_ref.xmin,idt_ref.ymax+self.idt.length+self.idt.y_offset)) bus_bottom = copy(self.bus) bus_bottom.layer = self.bottom_layer bus_ref = cell.add_ref(bus_bottom.draw(), alias='BottomBus') bus_ref.move(origin=(0,0),\ destination=(0,-self.bus.size.y)) anchor_bottom = copy(self.anchor) anchor_bottom.layer = self.bottom_layer anchor_bottom.etch_choice = False anchor_ref = cell.add_ref(anchor_bottom.draw(), alias="BottomAnchor_Top") anchor_ref.connect(anchor_ref.ports['conn'],\ destination=idt_ref.ports['top'],\ overlap=-self.bus.size.y) anchor_ref_2 = cell.add_ref(anchor_bottom.draw(), alias="BottomAnchor_Bottom") anchor_ref_2.connect(anchor_ref_2.ports['conn'],\ destination=idt_ref.ports['bottom'],\ overlap=-self.bus.size.y) for p_value in lfe_cell.ports.values(): cell.add_port(p_value) return cell
def route_manhattan_auto(ports, bendType='circular', layer=0, radius=20): """ routes a one-dimensional array of ports using manhattan algorithm and give it a series of ports to route to in a continuous list. accepts same parameters as ordinary route_manhattan to determine bending """ Total = Device() for x in xrange(int(np.floor(len(ports) / 2)) + 1): R = route_manhattan(port1=ports[x], port2=ports[x + 1], bendType=bendType, layer=layer, radius=radius) r = Total.add_ref(R) return Total
def attach_taper(cell : Device , port : Port , length : float , \ width2 : float, layer=LayoutDefault.layerTop) : t = pg.taper(length=length, width1=port.width, width2=width2, layer=layer) t_ref = cell.add_ref(t) t_ref.connect(1, destination=port) new_port = t_ref.ports[2] new_port.name = port.name cell.absorb(t_ref) cell.remove(port) cell.add_port(new_port)
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 route_manhattan90(port1, port2, bendType='circular', layer=0, radius=20): #this is a subroutine of route_manhattan() and should not be used by itself. Total = Device() width = port1.width #first map into uniform plane with normal x,y coords #allows each situation to be put into uniform cases of quadrants for routing. #this is because bends change direction and positioning. if port1.orientation == 0: p2 = [port2.midpoint[0], port2.midpoint[1]] p1 = [port1.midpoint[0], port1.midpoint[1]] if port1.orientation == 90: p2 = [port2.midpoint[1], -port2.midpoint[0]] p1 = [port1.midpoint[1], -port1.midpoint[0]] if port1.orientation == 180: p2 = [-port2.midpoint[0], -port2.midpoint[1]] p1 = [-port1.midpoint[0], -port1.midpoint[1]] if port1.orientation == 270: p2 = [-port2.midpoint[1], port2.midpoint[0]] p1 = [-port1.midpoint[1], port1.midpoint[0]] #create placeholder ports based on the imaginary coordinates we created Total.add_port(name='t1', midpoint=[0, 0], orientation=0, width=width) #CHECK THIS #first quadrant target, route upward if (p2[1] > p1[1]) & (p2[0] > p1[0]): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=-90, width=width) if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff, 0]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) R2 = route_basic(port1=b1.ports[2], port2=Total.ports['t2'], layer=layer) r1 = Total.add_ref(R1) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #fourth quadrant target, route downward if (p2[1] < p1[1]) & (p2[0] > p1[0]): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=90, width=width) if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff, 0]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) R2 = route_basic(port1=b1.ports[2], port2=Total.ports['t2'], layer=layer) r1 = Total.add_ref(R1) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) Total.rotate(angle=port1.orientation, center=p1) Total.move(origin=Total.ports['t1'], destination=port1) return Total
def _gradual_bend( radius=20, width=1.0, angular_coverage=15, num_steps=10, angle_resolution=0.1, start_angle=0, direction='ccw', layer=0, ): """ creates a 90-degree bent waveguide the bending radius is gradually increased until it reaches the minimum value of the radius at the "angular coverage" angle. it essentially creates a smooth transition to a bent waveguide mode. user can control number of steps provided. direction determined by start angle and cw or ccw switch ############ with the default 10 "num_steps" and 15 degree coverage, effective radius is about 1.5*radius. """ angular_coverage = np.deg2rad(angular_coverage) D = Device() #determines the increment in radius through its inverse from 0 to 1/r inc_rad = (radius**-1) / (num_steps) angle_step = angular_coverage / num_steps #construct a series of sub-arcs with equal angles but gradually decreasing bend radius arcs = [] for x in xrange(num_steps): A = _arc(radius=1 / ((x + 1) * inc_rad), width=width, theta=np.rad2deg(angle_step), start_angle=x * np.rad2deg(angle_step), angle_resolution=angle_resolution, layer=layer) a = D.add_ref(A) arcs.append(a) if x > 0: a.connect(port=1, destination=prevPort) prevPort = a.ports[2] D.add_port(name=1, port=arcs[0].ports[1]) #now connect a regular bend for the normal curved portion B = _arc(radius=radius, width=width, theta=45 - np.rad2deg(angular_coverage), start_angle=angular_coverage, angle_resolution=angle_resolution, layer=layer) b = D.add_ref(B) b.connect(port=1, destination=prevPort) prevPort = b.ports[2] D.add_port(name=2, port=prevPort) #now create the overall structure Total = Device() #clone the half-curve into two objects and connect for a 90 deg bend. D1 = Total.add_ref(D) D2 = Total.add_ref(D) D2.reflect(p1=[0, 0], p2=[1, 1]) D2.connect(port=2, destination=D1.ports[2]) Total.xmin = 0 Total.ymin = 0 #orient to default settings... Total.reflect(p1=[0, 0], p2=[1, 1]) Total.reflect(p1=[0, 0], p2=[1, 0]) #orient to user-provided settings if direction == 'cw': Total.reflect(p1=[0, 0], p2=[1, 0]) Total.rotate(angle=start_angle, center=Total.center) Total.center = [0, 0] Total.add_port(name=1, port=D1.ports[1]) Total.add_port(name=2, port=D2.ports[1]) return Total
def route_manhattan180(port1, port2, bendType='circular', layer=0, radius=20): #this is a subroutine of route_manhattan() and should not be used by itself. Total = Device() width = port1.width #first map into uniform plane with normal x,y coords #allows each situation to be put into uniform cases of quadrants for routing. #this is because bends change direction and positioning. if port1.orientation == 0: p2 = [port2.midpoint[0], port2.midpoint[1]] p1 = [port1.midpoint[0], port1.midpoint[1]] if port1.orientation == 90: p2 = [port2.midpoint[1], -port2.midpoint[0]] p1 = [port1.midpoint[1], -port1.midpoint[0]] if port1.orientation == 180: p2 = [-port2.midpoint[0], -port2.midpoint[1]] p1 = [-port1.midpoint[0], -port1.midpoint[1]] if port1.orientation == 270: p2 = [-port2.midpoint[1], port2.midpoint[0]] p1 = [-port1.midpoint[1], port1.midpoint[0]] #create placeholder ports based on the imaginary coordinates we created Total.add_port(name='t1', midpoint=[0, 0], orientation=0, width=width) if (port1.orientation != port2.orientation): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=180, width=width) else: Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=0, width=width) if port1.orientation == port2.orientation: #first quadrant target if (p2[1] > p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0], 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #second quadrant target if (p2[1] > p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b2.ports[2], port2=Total.ports['t2'], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=b1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #third quadrant target if (p2[1] < p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b2.ports[2], port2=Total.ports['t2'], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=b1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #fourth quadrant target if (p2[1] < p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0], 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #other port orientations are not supported: elif np.round(np.abs(np.mod(port1.orientation - port2.orientation, 360)), 3) != 180: raise ValueError( '[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)' ) #otherwise, they are 180 degrees apart: else: #first quadrant target if (p2[1] > p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff * 2, 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #second quadrant target if (p2[1] > p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) B3 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=180, theta=-90) B4 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') B3 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=180, direction='cw') B4 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b3 = Total.add_ref(B3) b4 = Total.add_ref(B4) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 4]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) b3.connect(port=b3.ports[1], destination=b2.ports[2]) b3.move([p2[0] - p1[0], 0]) R2 = route_basic(port1=b2.ports[2], port2=b3.ports[1], layer=layer) r2 = Total.add_ref(R2) b4.connect(port=b4.ports[1], destination=b3.ports[2]) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b4.ports[2]) #third quadrant target if (p2[1] < p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) B3 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-180, theta=90) B4 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') B3 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-180, direction='ccw') B4 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b3 = Total.add_ref(B3) b4 = Total.add_ref(B4) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 4]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) b3.connect(port=b3.ports[1], destination=b2.ports[2]) b3.move([p2[0] - p1[0], 0]) R2 = route_basic(port1=b2.ports[2], port2=b3.ports[1], layer=layer) r2 = Total.add_ref(R2) b4.connect(port=b4.ports[1], destination=b3.ports[2]) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b4.ports[2]) #fourth quadrant target if (p2[1] < p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff * 2, 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) Total.rotate(angle=port1.orientation, center=p1) Total.move(origin=Total.ports['t1'], destination=port1) return Total
def route_manhattan(port1, port2, bendType='circular', layer=0, radius=20): #route along cardinal directions between any two ports placed diagonally #from each other Total = Device() width = port1.width #first map into uniform plane with normal x,y coords #allows each situation to be put into uniform cases of quadrants for routing. #this is because bends change direction and positioning. if port1.orientation == 0: p2 = [port2.midpoint[0], port2.midpoint[1]] p1 = [port1.midpoint[0], port1.midpoint[1]] if port1.orientation == 90: p2 = [port2.midpoint[1], -port2.midpoint[0]] p1 = [port1.midpoint[1], -port1.midpoint[0]] if port1.orientation == 180: p2 = [-port2.midpoint[0], -port2.midpoint[1]] p1 = [-port1.midpoint[0], -port1.midpoint[1]] if port1.orientation == 270: p2 = [-port2.midpoint[1], port2.midpoint[0]] p1 = [-port1.midpoint[1], port1.midpoint[0]] Total.add_port(name=1, port=port1) Total.add_port(name=2, port=port2) if p2[1] == p1[1] or p2[0] == p1[0]: raise ValueError('Error - ports must be at different x AND y values.') #if it is parallel or anti-parallel, route with 180 option if (np.round(np.abs(np.mod(port1.orientation - port2.orientation, 360)), 3) == 180) or (np.round( np.abs(np.mod(port1.orientation - port2.orientation, 360)), 3) == 0): R1 = route_manhattan180(port1=port1, port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) else: #first quadrant case if (p2[1] > p1[1]) & (p2[0] > p1[0]): #simple 90 degree single-bend case if port2.orientation == port1.orientation - 90 or port2.orientation == port1.orientation + 270: R1 = route_manhattan90(port1=port1, port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) elif port2.orientation == port1.orientation + 90 or port2.orientation == port1.orientation - 270: if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=port1.orientation, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=port1.orientation, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=1, destination=port1) R1 = route_manhattan180(port1=b1.ports[2], port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) #second quadrant case if (p2[1] > p1[1]) & (p2[0] < p1[0]): if np.abs(port1.orientation - port2.orientation) == 90 or np.abs( port1.orientation - port2.orientation) == 270: if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=port1.orientation, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=port1.orientation, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=1, destination=port1) R1 = route_manhattan180(port1=b1.ports[2], port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) #third quadrant case if (p2[1] < p1[1]) & (p2[0] < p1[0]): if np.abs(port1.orientation - port2.orientation) == 90 or np.abs( port1.orientation - port2.orientation) == 270: if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=port1.orientation, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=port1.orientation, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=1, destination=port1) R1 = route_manhattan180(port1=b1.ports[2], port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) #fourth quadrant case if (p2[1] < p1[1]) & (p2[0] > p1[0]): #simple 90 degree single-bend case if port2.orientation == port1.orientation + 90 or port2.orientation == port1.orientation - 270: R1 = route_manhattan90(port1=port1, port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) elif port2.orientation == port1.orientation - 90 or port2.orientation == port1.orientation + 270: if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=port1.orientation, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=port1.orientation, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=1, destination=port1) R1 = route_manhattan180(port1=b1.ports[2], port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) return Total
def draw(self): self._set_relations() idt_cell = self.idt.draw() cell = Device(self.name) idt_ref = cell.add_ref(idt_cell, alias="IDT") idt_top_port = idt_ref.ports['top'] idt_bottom_port = idt_ref.ports['bottom'] bus_cell = self.bus.draw() bus_ref = cell.add_ref(bus_cell, alias="BUS") bus_ref.connect(port=bus_cell.ports['conn'],\ destination=idt_bottom_port) etch_cell = self.etchpit.draw() etch_ref = cell.add_ref(etch_cell, alias='EtchPit') etch_ref.connect(etch_ref.ports['bottom'],\ destination=idt_ref.ports['bottom'],\ overlap=-self.bus.size.y-self.anchor.etch_margin.y) anchor_cell = self.anchor.draw() anchor_bottom = cell.add_ref(anchor_cell, alias='AnchorBottom') anchor_bottom.connect(anchor_bottom.ports['conn'], destination=idt_ref.ports['bottom'], overlap=-self.bus.size.y) if not self._stretch_top_margin: anchor_top = cell.add_ref(anchor_cell, alias='AnchorTop') else: anchor_top_dev = deepcopy(self.anchor) anchor_top_dev.metalized = Point(anchor_top_dev.size.x - 2, anchor_top_dev.metalized.y) anchor_top = cell.add_ref(anchor_top_dev.draw(), alias='AnchorTop') del anchor_top_dev anchor_top.connect(anchor_top.ports['conn'],\ idt_ref.ports['top'],overlap=-self.bus.size.y) outport_top = anchor_top.ports['conn'] outport_bottom = anchor_bottom.ports['conn'] outport_top.name = 'top' outport_top.orientation = 90 outport_bottom.name = 'bottom' outport_bottom.orientation = -90 outport_top.midpoint=(\ outport_top.x,\ outport_top.y+self.anchor.metalized.y) outport_bottom.midpoint=(\ outport_bottom.x,\ outport_bottom.y-self.anchor.metalized.y) cell.add_port(outport_top) cell.add_port(outport_bottom) del idt_cell, bus_cell, etch_cell, anchor_cell return cell
def draw(self): cell = Device() cell.name = self.name supercell = LFERes.draw(self) super_ref = cell.add_ref(supercell) if self.plate_position == 'out, short': plate=pg.rectangle(size=(self.etchpit.active_area.x+8*self.idt.active_area_margin,self.idt.length-self.idt.y_offset/2),\ layer=self.platelayer) plate_ref = cell.add_ref(plate, alias='Plate') transl_rel=Point(self.etchpit.x-4*self.idt.active_area_margin,self.anchor.size.y+2*self.anchor.etch_margin.y+self.bus.size.y\ +self.idt.y_offset*3/4) lr_cell = get_corners(cell)[0] lr_plate = get_corners(plate_ref)[0] plate_ref.move(origin=lr_plate.coord,\ destination=(lr_plate+lr_cell+transl_rel).coord) cell.absorb(plate_ref) del plate elif self.plate_position == 'in, short': plate=pg.rectangle(\ size=(\ self.etchpit.active_area.x-\ 2*self.idt.active_area_margin,\ self.idt.length-self.idt.y_offset/2),\ layer=self.platelayer) plate_ref = cell.add_ref(plate, alias='Plate') transl_rel=Point(self.etchpit.x+\ self.idt.active_area_margin,\ self.anchor.size.y+\ 2*self.anchor.etch_margin.y+\ self.bus.size.y+\ self.idt.y_offset*3/4) lr_cell = get_corners(cell)[0] lr_plate = get_corners(plate_ref)[0] plate_ref.move(origin=lr_plate.coord,\ destination=(lr_plate+lr_cell+transl_rel).coord) cell.absorb(plate_ref) del plate elif self.plate_position == 'out, long': plate=pg.rectangle(\ size=(self.etchpit.active_area.x+\ 8*self.idt.active_area_margin,\ self.idt.length+\ 2*self.bus.size.y+\ self.idt.y_offset),\ layer=self.platelayer) plate_ref = cell.add_ref(plate, alias='Plate') transl_rel=Point(self.etchpit.x-\ 4*self.idt.active_area_margin,\ self.anchor.size.y+2*self.anchor.etch_margin.y) lr_cell = get_corners(cell)[0] lr_plate = get_corners(plate_ref)[0] plate_ref.move(origin=lr_plate.coord,\ destination=(lr_plate+lr_cell+transl_rel).coord) cell.absorb(plate_ref) del plate elif self.plate_position == 'in, long': plate=pg.rectangle(\ size=(\ self.etchpit.active_area.x-\ 2*self.idt.active_area_margin,\ self.idt.length+\ 2*self.bus.size.y+\ self.idt.y_offset), layer=self.platelayer) plate_ref = cell.add_ref(plate, alias='Plate') transl_rel=Point(self.etchpit.x+\ self.idt.active_area_margin,\ self.anchor.size.y+2*self.anchor.etch_margin.y) lr_cell = get_corners(cell)[0] lr_plate = get_corners(plate_ref)[0] plate_ref.move(origin=lr_plate.coord,\ destination=(lr_plate+lr_cell+transl_rel).coord) cell.absorb(plate_ref) del plate for name, port in supercell.ports.items(): cell.add_port(port, name) return cell
def text(text='abcd', size=10, justify='left', layer=0, font="DEPLOF"): """ Creates geometries of text Parameters ---------- text : str Text string to be written. size : int or float Size of the text justify : {'left', 'right', 'center'} Justification of the text. layer : int, array-like[2], or set Specific layer(s) to put polygon geometry on. font: str Font face to use. Default DEPLOF does not require additional libraries, otherwise freetype will be used to load fonts. Font can be given either by name (e.g. "Times New Roman"), or by file path. OTF or TTF fonts are supported. Returns ------- t : Device A Device containing the text geometry. """ t = Device('text') xoffset = 0 yoffset = 0 face = font if face == "DEPLOF": scaling = size / 1000 for line in text.split('\n'): l = Device(name='textline') for c in line: ascii_val = ord(c) if c == ' ': xoffset += 500 * scaling elif (33 <= ascii_val <= 126) or (ascii_val == 181): for poly in _glyph[ascii_val]: xpts = np.array(poly)[:, 0] * scaling ypts = np.array(poly)[:, 1] * scaling l.add_polygon([xpts + xoffset, ypts + yoffset], layer=layer) xoffset += (_width[ascii_val] + _indent[ascii_val]) * scaling else: valid_chars = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ยต' warnings.warn('[PHIDL] text(): Warning, some characters ignored, no geometry for character "%s" with ascii value %s. ' \ 'Valid characters: %s' % (chr(ascii_val), ascii_val, valid_chars)) t.add_ref(l) yoffset -= 1500 * scaling xoffset = 0 else: from .font import _get_font_by_name, _get_font_by_file, _get_glyph # Load the font # If we've passed a valid file, try to load that, otherwise search system fonts font = None if (face.endswith(".otf") or face.endswith(".ttf")) and os.path.exists(face): font = _get_font_by_file(face) else: try: font = _get_font_by_name(face) except ValueError: pass if font is None: raise ValueError(( '[PHIDL] Failed to find font: "%s". ' + 'Try specifying the exact (full) path to the .ttf or .otf file. ' + 'Otherwise, it might be resolved by rebuilding the matplotlib font cache' ) % (face)) # Render each character for line in text.split('\n'): l = Device('textline') xoffset = 0 for letter in line: letter_dev = Device("letter") letter_template, advance_x = _get_glyph(font, letter) for poly in letter_template.polygons: letter_dev.add_polygon(poly.polygons, layer=layer) ref = l.add_ref(letter_dev) ref.move(destination=(xoffset, 0)) ref.magnification = size xoffset += size * advance_x ref = t.add_ref(l) ref.move(destination=(0, yoffset)) yoffset -= size justify = justify.lower() for l in t.references: if justify == 'left': pass if justify == 'right': l.xmax = 0 if justify == 'center': l.move(origin=l.center, destination=(0, 0), axis='x') t.flatten() return t