def _draw_frame(self): rect = pg.bbox(self.clearance, layer=self.layer) rect.add_port(self.source) rect.add_port(self.destination) rect.name = self.name + "frame" return rect
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 global_markers(layer_marker=10, layer_mask=20): D = Device('Global Markers') R = pg.rectangle(size=(20, 20), layer=1) a = D.add_array(R, columns=6, rows=6, spacing=(100, 100)) a.move([-260, -260]) #Center of the array R = pg.rectangle(size=(20, 20), layer=1) a = D.add_array(R, columns=6, rows=6, spacing=(100, 100)) a.move([-260, -260]) #Center of the array #Add marker cover cover = pg.bbox(bbox=a.bbox, layer=layer_mask) D << pg.offset(cover, distance=100, layer=layer_mask) return D
import traceback bbox = ((0, 100), (400, 600)) route = pc.Routing() route.source = dl.Port(name='source', midpoint=(200, 0), width=50, orientation=90) route.destination = dl.Port(name='destination', midpoint=(200, 800), width=50, orientation=0) route.trace_width = 20 route.clearance = bbox route.overhang = 50 route.side = 'right' cell = pg.bbox(bbox) # st.check(route._draw_frame()) cell << route.draw() st.check(cell)
from phidl import quickplot as qp D = pg.rectangle(size = (4.5, 2), layer = 0) qp(D) # quickplot the geometry create_image(D, 'rectangle') # example-bbox import phidl.geometry as pg from phidl import quickplot as qp from phidl import Device D = Device() arc = D << pg.arc(radius = 10, width = 0.5, theta = 85, layer = 1).rotate(25) # Draw a rectangle around the arc we created by using the arc's bounding box rect = D << pg.bbox(bbox = arc.bbox, layer = 0) qp(D) # quickplot the geometry create_image(D, 'bbox') # example-cross import phidl.geometry as pg from phidl import quickplot as qp D = pg.cross(length = 10, width = 0.5, layer = 0) qp(D) # quickplot the geometry create_image(D, 'cross') # example-ellipse import phidl.geometry as pg from phidl import quickplot as qp
def verniers(scale=[1, 0.5, 0.1], layers=[1, 2], label='TE', text_size=20, reversed=False): """ Create a cell with vernier aligners. Parameters ---------- scale : iterable of float (default [1,0.5,0.25]) each float in list is the offset of a vernier. for each of them a vernier will be created in the X-Y axis layers : 2-len iterable of int (default [1,2]) define the two layers for the verniers. label : str (default "TE") add a label to the set of verniers. text_size : float (default) label size reversed : boolean if true, creates a negative alignment mark for the second layer Returns ------- cell : phidl.Device. """ cell = dl.Device(name="verniers") import numpy if not isinstance(scale, numpy.ndarray): scale = np.array(scale) scale = np.sort(scale) xvern = [] for dim in scale: notch_size = [dim * 5, dim * 25] notch_spacing = dim * 10 num_notches = 5 notch_offset = dim row_spacing = 0 layer1 = layers[0] layer2 = layers[1] cal=pg.litho_calipers(\ notch_size,\ notch_spacing,\ num_notches,\ notch_offset,\ row_spacing,\ layer1,\ layer2) cal.flatten() if reversed: tobedel = cal.get_polygons(by_spec=(layer2, 0)) cal = cal.remove_polygons( lambda pts, layer, datatype: layer == layer2) replica = dl.Device() replica.add(gdspy.PolygonSet(tobedel, layer=layer2)) frame = dl.Device() frame.add(pg.bbox(replica.bbox, layer=layer2)) frame_ext = dl.Device() frame_ext.add( gdspy.PolygonSet(frame.copy('tmp', scale=1.5).get_polygons(), layer=layer2)) frame_ext.flatten() frame_ext.move(origin=frame_ext.center, destination=replica.center) new_cal = pg.boolean(replica, frame_ext, 'xor', layer=layer2) new_cal.rotate(angle=180, center=(cal.xmin + cal.xsize / 2, cal.ymin)) new_cal.move(destination=(0, -notch_size[1])) cal << new_cal cal.flatten() xvern.append(cal) g = dl.Group(xvern) g.distribute(direction='y', spacing=scale[-1] * 20) g.align(alignment='x') xcell = dl.Device(name="x") for x in xvern: xcell << x xcell = pt.join(xcell) vern_x = cell << xcell vern_y = cell << xcell vern_y.rotate(angle=-90) vern_y.move(origin=(vern_y.xmin,vern_y.y),\ destination=(vern_x.x+scale[-1]*10,vern_x.ymin-scale[-1]*10)) cell.absorb(vern_x) cell.absorb(vern_y) label = pg.text(text=label, size=text_size, layer=layers[0]) label.move(destination=(cell.xmax - label.xsize / 2, cell.ymax - 2 * label.ysize)) overlabel = pg.bbox(label.bbox, layer=layers[1]) overlabel_scaled = dl.Device().add( gdspy.PolygonSet(overlabel.copy('tmp', scale=2).get_polygons(), layer=layers[1])) overlabel_scaled.move(origin=overlabel_scaled.center,\ destination=label.center) cutlab = pg.boolean(label, overlabel_scaled, 'xor', layer=layers[1]) cell << label cell << cutlab cell = pt.join(cell) return cell
def alignment_marks_4layers(scale=[0.2, 0.5, 1]): def dr(cell): return DeviceReference(cell) BElayer = pt.LayoutDefault.layerBottom TElayer = pt.LayoutDefault.layerTop VIAlayer = pt.LayoutDefault.layerVias ETCHlayer = pt.LayoutDefault.layerEtch PETCHlayer = pt.LayoutDefault.layerPartialEtch Zerolayer = pt.LayoutDefault.layerPad align1 = verniers(scale, layers=[BElayer, VIAlayer], label='VIA', reversed=True) align_ph = pg.bbox(align1.bbox) #only for space align2 = verniers(scale, layers=[BElayer, TElayer], label='TE') align3 = verniers(scale, layers=[BElayer, ETCHlayer], label='ETCH') align4 = verniers(scale, layers=[BElayer, PETCHlayer], label='PETCH') text = pc.Text() text.size = 300 text.layer = (BElayer, TElayer, ETCHlayer, PETCHlayer, VIAlayer) text.label = "Align to BE" t1 = text.draw() g = Group([align1, dr(align_ph), align2, align3, align4, t1]) g.distribute(direction='x', spacing=150) g.align(alignment='y') align5 = verniers(scale, layers=[TElayer, VIAlayer], label='VIA', reversed=True) align6 = verniers(scale, layers=[TElayer, ETCHlayer], label='ETCH') align7 = verniers(scale, layers=[TElayer, PETCHlayer], label='PETCH') text.layer = (TElayer, ETCHlayer, PETCHlayer, VIAlayer) text.label = 'Align to TE' t2 = text.draw() g2 = Group([align5, dr(align_ph), dr(align_ph), align6, align7, t2]) g2.distribute(direction='x', spacing=150) g2.align(alignment='y') align8 = verniers(scale, layers=[Zerolayer, VIAlayer], label='VIA', reversed=True) align9 = verniers(scale, layers=[Zerolayer, BElayer], label='BE') align10 = verniers(scale, layers=[Zerolayer, TElayer], label='TE') align11 = verniers(scale, layers=[Zerolayer, ETCHlayer], label='ETCH') align12 = verniers(scale, layers=[Zerolayer, PETCHlayer], label='PETCH') text.layer = (Zerolayer, BElayer, TElayer, ETCHlayer, PETCHlayer, VIAlayer) text.label = "Align to Pad" t3 = text.draw() g3 = Group([align8, align9, align10, align11, align12, t3]) g3.distribute(direction='x', spacing=150) g3.align(alignment='y') g_tot = Group([g, g2, g3]) g_tot.distribute(direction='y', spacing=150) g_tot.align(alignment='xmin') cell = Device("Alignments") for c in [align1, align2, align3, align4, t1]: cell.add(c) for c in [align5, align6, align7, t2]: cell.add(c) for c in [align8, align9, align10, align11, align12, t3]: cell.add(c) return cell
def test_bbox(): D = pg.bbox(bbox=[(-1, -1), (3, 4)], layer=0) h = D.hash_geometry(precision=1e-4) assert (h == 'e04a2dc0457f4ae300e9d1235fde335362c8b454')
def path(self): tol = 1e-3 bbox = pg.bbox(self.clearance) ll, lr, ul, ur = get_corners(bbox) source = self.source destination = self.destination if source.y > destination.y: source = self.destination destination = self.source if Point(source.midpoint).in_box(bbox.bbox): raise ValueError( f" Source of routing {source.midport} is in clearance area {bbox.bbox}" ) if Point(destination.midpoint).in_box(bbox.bbox): raise ValueError( f" Destination of routing{destination.midpoint} is in clearance area{bbox.bbox}" ) if destination.y <= ll.y + tol: # destination is below clearance if not(destination.orientation==source.orientation+180 or \ destination.orientation==source.orientation-180): raise Exception( "Routing error: non-hindered routing needs +90 -> -90 oriented ports" ) distance=Point(destination.midpoint)-\ Point(source.midpoint) p1 = Point(source.midpoint) p2 = p1 + Point(distance.x, distance.y * (3 / 4)) p3 = p2 + Point(0, distance.y / 4) list_points = _check_points_path(p1, p2, p3, trace_width=self.trace_width) try: p = pp.smooth(points=list_points, radius=self._radius, num_pts=self._num_pts) except Exception: raise ValueError("error for non-hindered path ") else: #destination is above clearance if not destination.orientation == 90: raise ValueError("Routing case not covered yet") elif source.orientation == 0: #right ground_pad_side source.name = 'source' destination.name = 'destination' p0 = Point(source.midpoint) p1 = Point(ur.x + self.trace_width, p0.y) p2 = Point(p1.x, ur.y + self.trace_width) p3 = Point(destination.x, p2.y) p4 = Point(destination.x, destination.y) list_points_rx = _check_points_path( p0, p1, p2, p3, p4, trace_width=self.trace_width) try: p = pp.smooth(points=list_points_rx, radius=self._radius, num_pts=self._num_pts) except Exception: raise ValueError("error in +0 source, rx path") if source.orientation == 90: if source.x + self.trace_width > ll.x and source.x - self.trace_width < lr.x: #source tucked inside clearance p0 = Point(source.midpoint) center_box = Point(bbox.center) #left path p1 = p0 - Point(0, source.width / 2) p2 = Point(ll.x - self.trace_width, p1.y) p3 = Point(p2.x, self.trace_width + destination.y) p4 = Point(destination.x, p3.y) p5 = Point(destination.x, destination.y) list_points_lx = _check_points_path( p0, p1, p2, p3, p4, p5, trace_width=self.trace_width) try: p_lx = pp.smooth(points=list_points_lx, radius=self._radius, num_pts=self._num_pts) except: raise ValueError( "error in +90 hindered tucked path, lx path") #right path p1 = p0 - Point(0, source.width / 2) p2 = Point(lr.x + self.trace_width, p1.y) p3 = Point(p2.x, self.trace_width + destination.y) p4 = Point(destination.x, p3.y) p5 = Point(destination.x, destination.y) list_points_rx = _check_points_path( p0, p1, p2, p3, p4, p5, trace_width=self.trace_width) try: p_rx = pp.smooth(points=list_points_rx, radius=self._radius, num_pts=self._num_pts) except: raise ValueError("error in +90 source, rx path") if self.side == 'auto': if p_lx.length() < p_rx.length(): p = p_lx else: p = p_rx elif self.side == 'left': p = p_lx elif self.side == 'right': p = p_rx else: raise ValueError("Invalid option for side :{}".format( self.side)) else: # source is not tucked under the clearance p0 = Point(source.midpoint) ll, lr, ul, ur = get_corners(bbox) center_box = Point(bbox.center) #left path p1 = Point(p0.x, destination.y + self.trace_width) p2 = Point(destination.x, p1.y) p3 = Point(destination.x, destination.y) list_points = _check_points_path( p0, p1, p2, p3, trace_width=self.trace_width) try: p = pp.smooth(points=list_points, radius=self._radius, num_pts=self._num_pts ) #source tucked inside clearance except Exception: raise ValueError( "error for non-tucked hindered p ,90 deg") elif source.orientation == 180: #left path p0 = Point(source.midpoint) p1 = Point(ll.x - self.trace_width, p0.y) p2 = Point(p1.x, ur.y + self.trace_width) p3 = Point(destination.x, p2.y) p4 = Point(destination.x, destination.y) list_points_lx = _check_points_path( p0, p1, p2, p3, p4, trace_width=self.trace_width) try: p = pp.smooth(points=list_points_lx, radius=self._radius, num_pts=self._num_pts) except Exception: import pdb pdb.set_trace() raise ValueError("error in +180 source, lx path") return p