diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index 10d10774..ae431bc6 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -11,6 +11,7 @@ from vector import vector from tech import layer import math + class pin_layout: """ A class to represent a rectangular design pin. It is limited to a @@ -20,40 +21,45 @@ class pin_layout: def __init__(self, name, rect, layer_name_pp): self.name = name # repack the rect as a vector, just in case - if type(rect[0])==vector: + if type(rect[0]) == vector: self.rect = rect else: - self.rect = [vector(rect[0]),vector(rect[1])] + self.rect = [vector(rect[0]), vector(rect[1])] # snap the rect to the grid self.rect = [x.snap_to_grid() for x in self.rect] - debug.check(self.width()>0,"Zero width pin.") - debug.check(self.height()>0,"Zero height pin.") + debug.check(self.width() > 0,"Zero width pin.") + debug.check(self.height() > 0,"Zero height pin.") # if it's a string, use the name - if type(layer_name_pp)==str: - self.layer=layer_name_pp + if type(layer_name_pp) == str: + self.layer = layer_name_pp # else it is required to be a lpp else: for (layer_name, lpp) in layer.items(): - if layer_name_pp[0]==lpp[0] and (layer_name_pp[1]==None or layer_name_pp[1]==lpp[1]): - self.layer=layer_name + if layer_name_pp[0] == lpp[0] and (not layer_name_pp[1] or layer_name_pp[1]==lpp[1]): + self.layer = layer_name break else: - debug.error("Couldn't find layer {}".format(layer_name_pp),-1) + debug.error("Couldn't find layer {}".format(layer_name_pp), -1) self.lpp = layer[self.layer] def __str__(self): """ override print function output """ - return "({} layer={} ll={} ur={})".format(self.name,self.layer,self.rect[0],self.rect[1]) + return "({} layer={} ll={} ur={})".format(self.name, + self.layer, + self.rect[0], + self.rect[1]) def __repr__(self): - """ - override repr function output (don't include + """ + override repr function output (don't include name since pin shapes could have same shape but diff name e.g. blockage vs A) """ - return "(layer={} ll={} ur={})".format(self.layer,self.rect[0],self.rect[1]) + return "(layer={} ll={} ur={})".format(self.layer, + self.rect[0], + self.rect[1]) def __hash__(self): """ Implement the hash function for sets etc. """ @@ -72,15 +78,15 @@ class pin_layout: def __eq__(self, other): """ Check if these are the same pins for duplicate checks """ if isinstance(other, self.__class__): - return (self.layer==other.layer and self.rect == other.rect) + return (self.layer == other.layer and self.rect == other.rect) else: - return False + return False def bbox(self, pin_list): """ Given a list of layout pins, create a bounding box layout. """ - (ll, ur) = self.rect + (ll, ur) = self.rect min_x = ll.x max_x = ur.x min_y = ll.y @@ -92,17 +98,17 @@ class pin_layout: min_y = min(min_y, pin.ll().y) max_y = max(max_y, pin.ur().y) - self.rect = [vector(min_x,min_y),vector(max_x,max_y)] + self.rect = [vector(min_x, min_y), vector(max_x, max_y)] def inflate(self, spacing=None): - """ - Inflate the rectangle by the spacing (or other rule) - and return the new rectangle. + """ + Inflate the rectangle by the spacing (or other rule) + and return the new rectangle. """ if not spacing: spacing = 0.5*drc("{0}_to_{0}".format(self.layer)) - (ll,ur) = self.rect + (ll, ur) = self.rect spacing = vector(spacing, spacing) newll = ll - spacing newur = ur + spacing @@ -111,20 +117,20 @@ class pin_layout: def intersection(self, other): """ Check if a shape overlaps with a rectangle """ - (ll,ur) = self.rect - (oll,our) = other.rect + (ll, ur) = self.rect + (oll, our) = other.rect min_x = max(ll.x, oll.x) max_x = min(ll.x, oll.x) min_y = max(ll.y, oll.y) max_y = min(ll.y, oll.y) - return [vector(min_x,min_y),vector(max_x,max_y)] + return [vector(min_x, min_y), vector(max_x, max_y)] def xoverlaps(self, other): """ Check if shape has x overlap """ - (ll,ur) = self.rect - (oll,our) = other.rect + (ll, ur) = self.rect + (oll, our) = other.rect x_overlaps = False # check if self is within other x range if (ll.x >= oll.x and ll.x <= our.x) or (ur.x >= oll.x and ur.x <= our.x): @@ -137,8 +143,8 @@ class pin_layout: def yoverlaps(self, other): """ Check if shape has x overlap """ - (ll,ur) = self.rect - (oll,our) = other.rect + (ll, ur) = self.rect + (oll, our) = other.rect y_overlaps = False # check if self is within other y range @@ -152,15 +158,15 @@ class pin_layout: def xcontains(self, other): """ Check if shape contains the x overlap """ - (ll,ur) = self.rect - (oll,our) = other.rect + (ll, ur) = self.rect + (oll, our) = other.rect return (oll.x >= ll.x and our.x <= ur.x) def ycontains(self, other): """ Check if shape contains the y overlap """ - (ll,ur) = self.rect - (oll,our) = other.rect + (ll, ur) = self.rect + (oll, our) = other.rect return (oll.y >= ll.y and our.y <= ur.y) @@ -189,7 +195,6 @@ class pin_layout: return True return False - def overlaps(self, other): """ Check if a shape overlaps with a rectangle """ # Can only overlap on the same layer @@ -215,40 +220,45 @@ class pin_layout: def normalize(self): """ Re-find the LL and UR points after a transform """ - (first,second)=self.rect - ll = vector(min(first[0],second[0]),min(first[1],second[1])) - ur = vector(max(first[0],second[0]),max(first[1],second[1])) - self.rect=[ll,ur] + (first, second) = self.rect + ll = vector(min(first[0], second[0]), min(first[1], second[1])) + ur = vector(max(first[0], second[0]), max(first[1], second[1])) + self.rect=[ll, ur] - def transform(self,offset,mirror,rotate): - """ Transform with offset, mirror and rotation to get the absolute pin location. - We must then re-find the ll and ur. The master is the cell instance. """ - (ll,ur) = self.rect - if mirror=="MX": - ll=ll.scale(1,-1) - ur=ur.scale(1,-1) - elif mirror=="MY": - ll=ll.scale(-1,1) - ur=ur.scale(-1,1) - elif mirror=="XY": - ll=ll.scale(-1,-1) - ur=ur.scale(-1,-1) + def transform(self, offset, mirror, rotate): + """ + Transform with offset, mirror and rotation + to get the absolute pin location. + We must then re-find the ll and ur. + The master is the cell instance. + """ + (ll, ur) = self.rect + if mirror == "MX": + ll = ll.scale(1, -1) + ur = ur.scale(1, -1) + elif mirror == "MY": + ll = ll.scale(-1, 1) + ur = ur.scale(-1, 1) + elif mirror == "XY": + ll = ll.scale(-1, -1) + ur = ur.scale(-1, -1) - if rotate==90: - ll=ll.rotate_scale(-1,1) - ur=ur.rotate_scale(-1,1) - elif rotate==180: - ll=ll.scale(-1,-1) - ur=ur.scale(-1,-1) - elif rotate==270: - ll=ll.rotate_scale(1,-1) - ur=ur.rotate_scale(1,-1) + if rotate == 90: + ll = ll.rotate_scale(-1, 1) + ur = ur.rotate_scale(-1, 1) + elif rotate == 180: + ll = ll.scale(-1, -1) + ur = ur.scale(-1, -1) + elif rotate == 270: + ll = ll.rotate_scale(1, -1) + ur = ur.rotate_scale(1, -1) - self.rect=[offset+ll,offset+ur] + self.rect = [offset + ll, offset + ur] self.normalize() def center(self): - return vector(0.5*(self.rect[0].x+self.rect[1].x),0.5*(self.rect[0].y+self.rect[1].y)) + return vector(0.5*(self.rect[0].x+self.rect[1].x), + 0.5*(self.rect[0].y+self.rect[1].y)) def cx(self): """ Center x """ @@ -265,17 +275,17 @@ class pin_layout: def ul(self): """ Upper left point """ - return vector(self.rect[0].x,self.rect[1].y) + return vector(self.rect[0].x, self.rect[1].y) def lr(self): """ Lower right point """ - return vector(self.rect[1].x,self.rect[0].y) + return vector(self.rect[1].x, self.rect[0].y) def ur(self): """ Upper right point """ return self.rect[1] - # The possible y edge values + # The possible y edge values def uy(self): """ Upper y value """ return self.rect[1].y @@ -294,30 +304,33 @@ class pin_layout: """ Right x value """ return self.rect[1].x - # The edge centers def rc(self): """ Right center point """ - return vector(self.rect[1].x,0.5*(self.rect[0].y+self.rect[1].y)) + return vector(self.rect[1].x, + 0.5*(self.rect[0].y+self.rect[1].y)) def lc(self): """ Left center point """ - return vector(self.rect[0].x,0.5*(self.rect[0].y+self.rect[1].y)) + return vector(self.rect[0].x, + 0.5*(self.rect[0].y+self.rect[1].y)) def uc(self): """ Upper center point """ - return vector(0.5*(self.rect[0].x+self.rect[1].x),self.rect[1].y) + return vector(0.5*(self.rect[0].x+self.rect[1].x), + self.rect[1].y) def bc(self): """ Bottom center point """ - return vector(0.5*(self.rect[0].x+self.rect[1].x),self.rect[0].y) - + return vector(0.5*(self.rect[0].x+self.rect[1].x), + self.rect[0].y) def gds_write_file(self, newLayout): """Writes the pin shape and label to GDS""" - debug.info(4, "writing pin (" + str(self.layer) + "):" - + str(self.width()) + "x" + str(self.height()) + " @ " + str(self.ll())) - (layer_num,purpose) = layer[self.layer] + debug.info(4, "writing pin (" + str(self.layer) + "):" + + str(self.width()) + "x" + + str(self.height()) + " @ " + str(self.ll())) + (layer_num, purpose) = layer[self.layer] newLayout.addBox(layerNumber=layer_num, purposeNumber=purpose, offsetInMicrons=self.ll(), @@ -325,37 +338,37 @@ class pin_layout: height=self.height(), center=False) # Add the tet in the middle of the pin. - # This fixes some pin label offsetting when GDS gets imported into Magic. + # This fixes some pin label offsetting when GDS gets + # imported into Magic. newLayout.addText(text=self.name, layerNumber=layer_num, purposeNumber=purpose, offsetInMicrons=self.center(), magnification=GDS["zoom"], rotate=None) - def compute_overlap(self, other): """ Calculate the rectangular overlap of two rectangles. """ - (r1_ll,r1_ur) = self.rect - (r2_ll,r2_ur) = other.rect + (r1_ll, r1_ur) = self.rect + (r2_ll, r2_ur) = other.rect - #ov_ur = vector(min(r1_ur.x,r2_ur.x),min(r1_ur.y,r2_ur.y)) - #ov_ll = vector(max(r1_ll.x,r2_ll.x),max(r1_ll.y,r2_ll.y)) + # ov_ur = vector(min(r1_ur.x,r2_ur.x),min(r1_ur.y,r2_ur.y)) + # ov_ll = vector(max(r1_ll.x,r2_ll.x),max(r1_ll.y,r2_ll.y)) - dy = min(r1_ur.y,r2_ur.y)-max(r1_ll.y,r2_ll.y) - dx = min(r1_ur.x,r2_ur.x)-max(r1_ll.x,r2_ll.x) + dy = min(r1_ur.y, r2_ur.y) - max(r1_ll.y, r2_ll.y) + dx = min(r1_ur.x, r2_ur.x) - max(r1_ll.x, r2_ll.x) - if dx>=0 and dy>=0: - return [dx,dy] + if dx >= 0 and dy >= 0: + return [dx, dy] else: - return [0,0] + return [0, 0] def distance(self, other): - """ + """ Calculate the distance to another pin layout. """ - (r1_ll,r1_ur) = self.rect - (r2_ll,r2_ur) = other.rect + (r1_ll, r1_ur) = self.rect + (r2_ll, r2_ur) = other.rect def dist(x1, y1, x2, y2): return math.sqrt((x2-x1)**2 + (y2-y1)**2) @@ -376,7 +389,7 @@ class pin_layout: elif left: return r1_ll.x - r2_ur.x elif right: - return r2_ll.x - r1.ur.x + return r2_ll.x - r1_ur.x elif bottom: return r1_ll.y - r2_ur.y elif top: @@ -385,9 +398,8 @@ class pin_layout: # rectangles intersect return 0 - def overlap_length(self, other): - """ + """ Calculate the intersection segment and determine its length """ @@ -399,21 +411,21 @@ class pin_layout: intersections = self.compute_overlap_segment(other) # This is the common case where two pairs of edges overlap # at two points, so just find the distance between those two points - if len(intersections)==2: - (p1,p2) = intersections - return math.sqrt(pow(p1[0]-p2[0],2) + pow(p1[1]-p2[1],2)) + if len(intersections) == 2: + (p1, p2) = intersections + return math.sqrt(pow(p1[0]-p2[0], 2) + pow(p1[1]-p2[1], 2)) else: # This is where we had a corner intersection or none return 0 def compute_overlap_segment(self, other): - """ - Calculate the intersection segment of two rectangles + """ + Calculate the intersection segment of two rectangles (if any) """ - (r1_ll,r1_ur) = self.rect - (r2_ll,r2_ur) = other.rect + (r1_ll, r1_ur) = self.rect + (r2_ll, r2_ur) = other.rect # The other corners besides ll and ur r1_ul = vector(r1_ll.x, r1_ur.y) @@ -422,6 +434,7 @@ class pin_layout: r2_lr = vector(r2_ur.x, r2_ll.y) from itertools import tee + def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = tee(iterable) @@ -431,14 +444,14 @@ class pin_layout: # R1 edges CW r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll] r1_edges = [] - for (p,q) in pairwise(r1_cw_points): - r1_edges.append([p,q]) + for (p, q) in pairwise(r1_cw_points): + r1_edges.append([p, q]) # R2 edges CW r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll] r2_edges = [] - for (p,q) in pairwise(r2_cw_points): - r2_edges.append([p,q]) + for (p, q) in pairwise(r2_cw_points): + r2_edges.append([p, q]) # There are 4 edges on each rectangle # so just brute force check intersection of each @@ -460,18 +473,18 @@ class pin_layout: q.x >= min(p.x, r.x) and \ q.y <= max(p.y, r.y) and \ q.y >= min(p.y, r.y): - return True + return True return False def segment_intersection(self, s1, s2): - """ + """ Determine the intersection point of two segments Return the a segment if they overlap. Return None if they don't. """ - (a,b) = s1 - (c,d) = s2 + (a, b) = s1 + (c, d) = s2 # Line AB represented as a1x + b1y = c1 a1 = b.y - a.y b1 = a.x - b.x @@ -484,11 +497,11 @@ class pin_layout: determinant = a1*b2 - a2*b1 - if determinant!=0: + if determinant != 0: x = (b2*c1 - b1*c2)/determinant y = (a1*c2 - a2*c1)/determinant - r = vector(x,y).snap_to_grid() + r = vector(x, y).snap_to_grid() if self.on_segment(a, r, b) and self.on_segment(c, r, d): return r