From 131f4bda4a75b5b74169cb7d15cb767806741ed5 Mon Sep 17 00:00:00 2001 From: Matthew Guthaus Date: Thu, 14 Nov 2019 18:17:20 +0000 Subject: [PATCH] Add layer-purpose GDS support. Various PEP8 fixes. --- compiler/base/pin_layout.py | 22 ++- compiler/base/utils.py | 40 +++--- compiler/debug.py | 12 +- compiler/gdsMill/gdsMill/vlsiLayout.py | 177 +++++++++++++++---------- compiler/modules/dff.py | 10 +- compiler/router/pin_group.py | 141 ++++++++++---------- compiler/router/router.py | 32 +++-- compiler/router/router_tech.py | 26 ++-- compiler/router/supply_grid_router.py | 3 +- technology/freepdk45/tech/tech.py | 1 - technology/scn4m_subm/tech/tech.py | 1 - 11 files changed, 267 insertions(+), 198 deletions(-) diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index ae431bc6..f5d25263 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -28,8 +28,8 @@ class pin_layout: # 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: @@ -37,7 +37,7 @@ class pin_layout: # else it is required to be a lpp else: for (layer_name, lpp) in layer.items(): - if layer_name_pp[0] == lpp[0] and (not layer_name_pp[1] or layer_name_pp[1]==lpp[1]): + if self.same_lpp(layer_name_pp, lpp): self.layer = layer_name break else: @@ -78,7 +78,7 @@ 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.lpp == other.lpp and self.rect == other.rect) else: return False @@ -177,7 +177,7 @@ class pin_layout: return True # Can only overlap on the same layer - if self.layer != other.layer: + if not self.same_lpp(self.lpp, other.lpp): return False if not self.xcontains(other): @@ -198,7 +198,7 @@ class pin_layout: def overlaps(self, other): """ Check if a shape overlaps with a rectangle """ # Can only overlap on the same layer - if self.layer != other.layer: + if not self.same_lpp(self.lpp, other.lpp): return False x_overlaps = self.xoverlaps(other) @@ -506,3 +506,13 @@ class pin_layout: return r return None + + def same_lpp(self, lpp1, lpp2): + """ + Check if the layers and purposes are the same. + Ignore if purpose is a None. + """ + if lpp1[1] == None or lpp2[1] == None: + return lpp1[0] == lpp2[0] + + return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1] diff --git a/compiler/base/utils.py b/compiler/base/utils.py index 1816a508..f02b2b85 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -5,7 +5,6 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import os import gdsMill import tech import math @@ -16,6 +15,7 @@ from pin_layout import pin_layout OPTS = globals.OPTS + def ceil(decimal): """ Performs a ceiling function on the decimal place specified by the DRC grid. @@ -23,27 +23,33 @@ def ceil(decimal): grid = tech.drc["grid"] return math.ceil(decimal * 1 / grid) / (1 / grid) + def round_to_grid(number): """ Rounds an arbitrary number to the grid. """ - grid = tech.drc["grid"] + grid = tech.drc["grid"] # this gets the nearest integer value number_grid = int(round(round((number / grid), 2), 0)) number_off = number_grid * grid return number_off + def snap_to_grid(offset): """ Changes the coodrinate to match the grid settings """ - return [round_to_grid(offset[0]),round_to_grid(offset[1])] + return [round_to_grid(offset[0]), + round_to_grid(offset[1])] + def pin_center(boundary): """ This returns the center of a pin shape in the vlsiLayout border format. """ - return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])] + return [0.5 * (boundary[0] + boundary[2]), + 0.5 * (boundary[1] + boundary[3])] + def auto_measure_libcell(pin_list, name, units, lpp): """ @@ -57,35 +63,34 @@ def auto_measure_libcell(pin_list, name, units, lpp): cell = {} measure_result = cell_vlsi.getLayoutBorder(lpp[0]) - if measure_result == None: + if measure_result: measure_result = cell_vlsi.measureSize(name) [cell["width"], cell["height"]] = measure_result for pin in pin_list: - (name,lpp,boundary)=cell_vlsi.getPinShapeByLabel(str(pin)) + (name, lpp, boundary) = cell_vlsi.getPinShapeByLabel(str(pin)) cell[str(pin)] = pin_center(boundary) return cell - def get_gds_size(name, gds_filename, units, lpp): """ Open a GDS file and return the size from either the bounding box or a border layer. """ - debug.info(4,"Creating VLSI layout for {}".format(name)) + debug.info(4, "Creating VLSI layout for {}".format(name)) cell_vlsi = gdsMill.VlsiLayout(units=units) reader = gdsMill.Gds2reader(cell_vlsi) reader.loadFromFile(gds_filename) - cell = {} measure_result = cell_vlsi.getLayoutBorder(lpp) - if measure_result == None: - debug.info(2,"Layout border failed. Trying to measure size for {}".format(name)) + if not measure_result: + debug.info(2, "Layout border failed. Trying to measure size for {}".format(name)) measure_result = cell_vlsi.measureSize(name) # returns width,height return measure_result + def get_libcell_size(name, units, lpp): """ Open a GDS file and return the library cell size from either the @@ -106,15 +111,18 @@ def get_gds_pins(pin_names, name, gds_filename, units): cell = {} for pin_name in pin_names: - cell[str(pin_name)]=[] - pin_list=cell_vlsi.getPinShape(str(pin_name)) + cell[str(pin_name)] = [] + pin_list = cell_vlsi.getPinShape(str(pin_name)) for pin_shape in pin_list: - (lpp,boundary)=pin_shape - rect=[vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] - # this is a list because other cells/designs may have must-connect pins + (lpp, boundary) = pin_shape + rect = [vector(boundary[0], boundary[1]), + vector(boundary[2], boundary[3])] + # this is a list because other cells/designs + # may have must-connect pins cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp)) return cell + def get_libcell_pins(pin_list, name, units): """ Open a GDS file and find the pins in pin_list as text on a given layer. diff --git a/compiler/debug.py b/compiler/debug.py index 228193a5..a902bca0 100644 --- a/compiler/debug.py +++ b/compiler/debug.py @@ -27,9 +27,9 @@ def check(check, str): os.path.basename(filename), line_number, str)) if globals.OPTS.debug_level > 0: - import pdb; pdb.set_trace() - else: - assert 0 + import pdb + pdb.set_trace() + assert 0 def error(str, return_value=0): @@ -41,9 +41,9 @@ def error(str, return_value=0): os.path.basename(filename), line_number, str)) if globals.OPTS.debug_level > 0: - import pdb; pdb.set_trace() - else: - assert return_value == 0 + import pdb + pdb.set_trace() + assert return_value == 0 def warning(str): diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index 8d071e9f..eec1870c 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -213,11 +213,11 @@ class VlsiLayout: def initialize(self): self.deduceHierarchy() - #self.traverseTheHierarchy() + # self.traverseTheHierarchy() self.populateCoordinateMap() for layerNumber in self.layerNumbersInUse: - self.processLabelPins((layerNumber,None)) + self.processLabelPins((layerNumber, None)) def populateCoordinateMap(self): @@ -245,21 +245,24 @@ class VlsiLayout: self.xyTree.append((startingStructureName,origin,uVector,vVector)) self.traverseTheHierarchy(delegateFunction = addToXyTree) - def microns(self,userUnits): + def microns(self, userUnits): """Utility function to convert user units to microns""" userUnit = self.units[1]/self.units[0] - userUnitsPerMicron = userUnit / (userunit) + userUnitsPerMicron = userUnit / userunit layoutUnitsPerMicron = userUnitsPerMicron / self.units[0] return userUnits / layoutUnitsPerMicron - def userUnits(self,microns): + def userUnits(self, microns): """Utility function to convert microns to user units""" userUnit = self.units[1]/self.units[0] - #userUnitsPerMicron = userUnit / 1e-6 + # userUnitsPerMicron = userUnit / 1e-6 userUnitsPerMicron = userUnit / (userUnit) layoutUnitsPerMicron = userUnitsPerMicron / self.units[0] - #print("userUnit:",userUnit,"userUnitsPerMicron",userUnitsPerMicron,"layoutUnitsPerMicron",layoutUnitsPerMicron,[microns,microns*layoutUnitsPerMicron]) - return round(microns*layoutUnitsPerMicron,0) + # print("userUnit:",userUnit, + # "userUnitsPerMicron",userUnitsPerMicron, + # "layoutUnitsPerMicron",layoutUnitsPerMicron, + # [microns,microns*layoutUnitsPerMicron]) + return round(microns*layoutUnitsPerMicron, 0) def changeRoot(self,newRoot, create=False): """ @@ -386,7 +389,7 @@ class VlsiLayout: #add the sref to the root structure self.structures[self.rootStructureName].boundaries.append(boundaryToAdd) - def addPath(self, layerNumber=0, purposeNumber = None, coordinates=[(0,0)], width=1.0): + def addPath(self, layerNumber=0, purposeNumber=None, coordinates=[(0,0)], width=1.0): """ Method to add a path to a layout """ @@ -405,7 +408,7 @@ class VlsiLayout: #add the sref to the root structure self.structures[self.rootStructureName].paths.append(pathToAdd) - def addText(self, text, layerNumber=0, purposeNumber = None, offsetInMicrons=(0,0), magnification=0.1, rotate = None): + def addText(self, text, layerNumber=0, purposeNumber=None, offsetInMicrons=(0,0), magnification=0.1, rotate = None): offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1])) textToAdd = GdsText() textToAdd.drawingLayer = layerNumber @@ -593,42 +596,50 @@ class VlsiLayout: passFailIndex += 1 print("Done\n\n") - def getLayoutBorder(self,lpp): - cellSizeMicron=None + def getLayoutBorder(self, lpp): + cellSizeMicron = None for boundary in self.structures[self.rootStructureName].boundaries: - if boundary.drawingLayer==lpp[0] and \ - (lpp[1]==None or boundary.purposeLayer==None or boundary.purposeLayer==lpp[1]): + if sameLPP((boundary.drawingLayer, boundary.purposeLayer), + lpp): if self.debug: - debug.info(1,"Find border "+str(boundary.coordinates)) - left_bottom=boundary.coordinates[0] - right_top=boundary.coordinates[2] - cellSize=[right_top[0]-left_bottom[0],right_top[1]-left_bottom[1]] - cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]] + debug.info(1, "Find border "+str(boundary.coordinates)) + left_bottom = boundary.coordinates[0] + right_top = boundary.coordinates[2] + cellSize = [right_top[0]-left_bottom[0], + right_top[1]-left_bottom[1]] + cellSizeMicron = [cellSize[0]*self.units[0], + cellSize[1]*self.units[0]] debug.check(cellSizeMicron, "Error: "+str(self.rootStructureName)+".cell_size information not found yet") + return cellSizeMicron - def measureSize(self,startStructure): - self.rootStructureName=self.padText(startStructure) + def measureSize(self, startStructure): + self.rootStructureName = self.padText(startStructure) self.populateCoordinateMap() cellBoundary = [None, None, None, None] for TreeUnit in self.xyTree: - cellBoundary=self.measureSizeInStructure(TreeUnit,cellBoundary) - cellSize=[cellBoundary[2]-cellBoundary[0],cellBoundary[3]-cellBoundary[1]] - cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]] + cellBoundary = self.measureSizeInStructure(TreeUnit, cellBoundary) + cellSize = [cellBoundary[2]-cellBoundary[0], + cellBoundary[3]-cellBoundary[1]] + cellSizeMicron = [cellSize[0]*self.units[0], + cellSize[1]*self.units[0]] return cellSizeMicron - def measureBoundary(self,startStructure): - self.rootStructureName=self.padText(startStructure) + def measureBoundary(self, startStructure): + self.rootStructureName = self.padText(startStructure) self.populateCoordinateMap() cellBoundary = [None, None, None, None] for TreeUnit in self.xyTree: - cellBoundary=self.measureSizeInStructure(TreeUnit,cellBoundary) - return [[self.units[0]*cellBoundary[0],self.units[0]*cellBoundary[1]], - [self.units[0]*cellBoundary[2],self.units[0]*cellBoundary[3]]] + cellBoundary = self.measureSizeInStructure(TreeUnit, cellBoundary) + return [[self.units[0]*cellBoundary[0], + self.units[0]*cellBoundary[1]], + [self.units[0]*cellBoundary[2], + self.units[0]*cellBoundary[3]]] - def measureSizeInStructure(self,structure,cellBoundary): - (structureName,structureOrigin,structureuVector,structurevVector)=structure + def measureSizeInStructure(self, structure, cellBoundary): + (structureName, structureOrigin, + structureuVector, structurevVector) = structure for boundary in self.structures[str(structureName)].boundaries: left_bottom=boundary.coordinates[0] right_top=boundary.coordinates[2] @@ -655,14 +666,14 @@ class VlsiLayout: cellBoundary[3]=right_top_Y return cellBoundary - def getTexts(self, lpp): """ Get all of the labels on a given layer only at the root level. """ text_list = [] for Text in self.structures[self.rootStructureName].texts: - if Text.drawingLayer==lpp[0] and (lpp[1]==None or Text.purposeLayer==lpp[1]): + if sameLPP((Text.drawingLayer, Text.purposeLayer), + lpp): text_list.append(Text) return text_list @@ -678,9 +689,9 @@ class VlsiLayout: max_pin = None max_area = 0 for pin in pin_list: - (layer,boundary) = pin + (layer, boundary) = pin new_area = boundaryArea(boundary) - if max_pin == None or new_area>max_area: + if not max_pin or new_area > max_area: max_pin = pin max_area = new_area max_pins.append(max_pin) @@ -697,11 +708,10 @@ class VlsiLayout: pin_map = self.pins[pin_name] for pin_list in pin_map: for pin in pin_list: - (pin_layer, boundary) = pin + (pin_layer, boundary) = pin shape_list.append(pin) return shape_list - def processLabelPins(self, lpp): """ @@ -710,19 +720,21 @@ class VlsiLayout: """ # Get the labels on a layer in the root level labels = self.getTexts(lpp) + # Get all of the shapes on the layer at all levels # and transform them to the current level - shapes = self.getAllShapes(lpp) + shapes = self.getAllShapes(lpp) for label in labels: label_coordinate = label.coordinates[0] user_coordinate = [x*self.units[0] for x in label_coordinate] pin_shapes = [] for boundary in shapes: - if self.labelInRectangle(user_coordinate,boundary): + if self.labelInRectangle(user_coordinate, boundary): pin_shapes.append((lpp, boundary)) label_text = label.textString + # Remove the padding if it exists if label_text[-1] == "\x00": label_text = label_text[0:-1] @@ -732,11 +744,11 @@ class VlsiLayout: except KeyError: self.pins[label_text] = [] self.pins[label_text].append(pin_shapes) - def getBlockages(self, lpp): """ - Return all blockages on a given layer in [coordinate 1, coordinate 2,...] format and + Return all blockages on a given layer in + [coordinate 1, coordinate 2,...] format and user units. """ blockages = [] @@ -744,69 +756,85 @@ class VlsiLayout: shapes = self.getAllShapes(lpp) for boundary in shapes: vectors = [] - for i in range(0,len(boundary),2): - vectors.append(vector(boundary[i],boundary[i+1])) + for i in range(0, len(boundary), 2): + vectors.append(vector(boundary[i], boundary[i+1])) blockages.append(vectors) - return blockages + + return blockages def getAllShapes(self, lpp): """ - Return all shapes on a given layer in [llx, lly, urx, ury] format and user units for rectangles - and [coordinate 1, coordinate 2,...] format and user units for polygons. + Return all shapes on a given layer in [llx, lly, urx, ury] + format and user units for rectangles + and [coordinate 1, coordinate 2,...] format and user + units for polygons. """ boundaries = set() for TreeUnit in self.xyTree: - #print(TreeUnit[0]) - boundaries.update(self.getShapesInStructure(lpp,TreeUnit)) + # print(TreeUnit[0]) + boundaries.update(self.getShapesInStructure(lpp, TreeUnit)) # Convert to user units user_boundaries = [] for boundary in boundaries: boundaries_list = [] - for i in range(0,len(boundary)): + for i in range(0, len(boundary)): boundaries_list.append(boundary[i]*self.units[0]) user_boundaries.append(boundaries_list) return user_boundaries - def getShapesInStructure(self, lpp, structure): - """ - Go through all the shapes in a structure and return the list of shapes in - the form [llx, lly, urx, ury] for rectangles and [coordinate 1, coordinate 2,...] for polygons. """ - (structureName,structureOrigin,structureuVector,structurevVector)=structure - #print(structureName,"u",structureuVector.transpose(),"v",structurevVector.transpose(),"o",structureOrigin.transpose()) + Go through all the shapes in a structure and + return the list of shapes in + the form [llx, lly, urx, ury] for rectangles + and [coordinate 1, coordinate 2,...] for polygons. + """ + (structureName, structureOrigin, + structureuVector, structurevVector) = structure + # print(structureName, + # "u", structureuVector.transpose(), + # "v",structurevVector.transpose(), + # "o",structureOrigin.transpose()) boundaries = [] for boundary in self.structures[str(structureName)].boundaries: - if boundary.drawingLayer==lpp[0] and (lpp[1]==None or boundary.purposeLayer==lpp[1]): - if len(boundary.coordinates)!=5: + if sameLPP((boundary.drawingLayer, boundary.purposeLayer), + lpp): + if len(boundary.coordinates) != 5: # if shape is a polygon (used in DFF) boundaryPolygon = [] # Polygon is a list of coordinates going ccw - for coord in range(0,len(boundary.coordinates)): + for coord in range(0, len(boundary.coordinates)): boundaryPolygon.append(boundary.coordinates[coord][0]) boundaryPolygon.append(boundary.coordinates[coord][1]) # perform the rotation - boundaryPolygon=self.transformPolygon(boundaryPolygon,structureuVector,structurevVector) - # add the offset + boundaryPolygon = self.transformPolygon(boundaryPolygon, + structureuVector, + structurevVector) + # add the offset polygon = [] - for i in range(0,len(boundaryPolygon),2): - polygon.append(boundaryPolygon[i]+structureOrigin[0].item()) - polygon.append(boundaryPolygon[i+1]+structureOrigin[1].item()) + for i in range(0, len(boundaryPolygon), 2): + polygon.append(boundaryPolygon[i] + structureOrigin[0].item()) + polygon.append(boundaryPolygon[i+1] + structureOrigin[1].item()) # make it a tuple polygon = tuple(polygon) boundaries.append(polygon) else: # else shape is a rectangle - left_bottom=boundary.coordinates[0] - right_top=boundary.coordinates[2] + left_bottom = boundary.coordinates[0] + right_top = boundary.coordinates[2] # Rectangle is [leftx, bottomy, rightx, topy]. - boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]] + boundaryRect = [left_bottom[0], left_bottom[1], + right_top[0], right_top[1]] # perform the rotation - boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector) + boundaryRect = self.transformRectangle(boundaryRect, + structureuVector, + structurevVector) # add the offset and make it a tuple - boundaryRect=(boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(), - boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()) + boundaryRect = (boundaryRect[0]+structureOrigin[0].item(), + boundaryRect[1]+structureOrigin[1].item(), + boundaryRect[2]+structureOrigin[0].item(), + boundaryRect[3]+structureOrigin[1].item()) boundaries.append(boundaryRect) return boundaries @@ -868,6 +896,17 @@ class VlsiLayout: else: return False + +def sameLPP(lpp1, lpp2): + """ + Check if the layers and purposes are the same. + Ignore if purpose is a None. + """ + if lpp1[1] == None or lpp2[1] == None: + return lpp1[0] == lpp2[0] + + return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1] + def boundaryArea(A): """ diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index 3cb1bcf1..8871872d 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -5,13 +5,11 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import globals import design -from math import log -import design -from tech import GDS,layer,spice,parameter +from tech import GDS, layer, spice, parameter import utils + class dff(design.design): """ Memory address flip-flop @@ -19,7 +17,9 @@ class dff(design.design): pin_names = ["D", "Q", "clk", "vdd", "gnd"] type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] - (width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"]) + (width, height) = utils.get_libcell_size("dff", + GDS["unit"], + layer["boundary"]) pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"]) def __init__(self, name="dff"): diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 4fff2ae5..8a1362c2 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -18,16 +18,17 @@ class pin_group: determine how pin shapes get mapped to tracks. It is initially constructed with a single set of (touching) pins. """ + def __init__(self, name, pin_set, router): self.name = name # Flag for when it is routed self.routed = False # Flag for when it is enclosed self.enclosed = False - + # Remove any redundant pins (i.e. contained in other pins) irredundant_pin_set = self.remove_redundant_shapes(list(pin_set)) - + # This is a list because we can have a pin # group of disconnected sets of pins # and these are represented by separate lists @@ -39,7 +40,7 @@ class pin_group: # These are the secondary grids that could # or could not be part of the pin self.secondary_grids = set() - + # The corresponding set of partially blocked grids for each pin group. # These are blockages for other nets but unblocked # for routing this group. These are also blockages if we @@ -52,16 +53,16 @@ class pin_group: def __str__(self): """ override print function output """ total_string = "(pg {} ".format(self.name) - + pin_string = "\n pins={}".format(self.pins) total_string += pin_string - + grids_string = "\n grids={}".format(self.grids) total_string += grids_string grids_string = "\n secondary={}".format(self.secondary_grids) total_string += grids_string - + if self.enclosed: enclosure_string = "\n enclose={}".format(self.enclosures) total_string += enclosure_string @@ -72,7 +73,7 @@ class pin_group: def __repr__(self): """ override repr function output """ return str(self) - + def size(self): return len(self.grids) @@ -81,7 +82,7 @@ class pin_group: def is_routed(self): return self.routed - + def remove_redundant_shapes(self, pin_list): """ Remove any pin layout that is contained within another. @@ -90,7 +91,7 @@ class pin_group: local_debug = False if local_debug: debug.info(0, "INITIAL: {}".format(pin_list)) - + # Make a copy of the list to start new_pin_list = pin_list.copy() @@ -100,7 +101,7 @@ class pin_group: # If we remove this pin, it can't contain other pins if index1 in remove_indices: continue - + for index2, pin2 in enumerate(pin_list): # Can't contain yourself, # but compare the indices and not the pins @@ -110,7 +111,7 @@ class pin_group: # If we already removed it, can't remove it again... if index2 in remove_indices: continue - + if pin1.contains(pin2): if local_debug: debug.info(0, "{0} contains {1}".format(pin1, pin2)) @@ -119,10 +120,10 @@ class pin_group: # Remove them in decreasing order to not invalidate the indices for i in sorted(remove_indices, reverse=True): del new_pin_list[i] - + if local_debug: debug.info(0, "FINAL : {}".format(new_pin_list)) - + return new_pin_list def compute_enclosures(self): @@ -144,11 +145,17 @@ class pin_group: enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z) pin_list.append(enclosure) - # Now simplify the enclosure list + debug.check(len(pin_list) > 0, + "Did not find any enclosures.") + + # Now simplify the enclosure list new_pin_list = self.remove_redundant_shapes(pin_list) - - return new_pin_list + + debug.check(len(new_pin_list) > 0, + "Did not find any enclosures.") + return new_pin_list + def compute_connector(self, pin, enclosure): """ Compute a shape to connect the pin to the enclosure shape. @@ -200,7 +207,7 @@ class pin_group: for shape in enclosures: if shape.xcontains(pin): edge_list.append(shape) - + # Sort them by their bottom edge edge_list.sort(key=lambda x: x.by(), reverse=True) @@ -233,7 +240,7 @@ class pin_group: for shape in enclosures: if shape.xcontains(pin): edge_list.append(shape) - + # Sort them by their upper edge edge_list.sort(key=lambda x: x.uy()) @@ -251,13 +258,13 @@ class pin_group: # If it already overlaps, no connector needed if bottom_item.overlaps(pin): return None - + # Otherwise, make a connector to the item p = self.compute_connector(pin, bottom_item) return p - + def find_left_connector(self, pin, enclosures): - """ + """ Find the enclosure that is to the left of the pin and make a connector to it's right edge. """ @@ -266,14 +273,14 @@ class pin_group: for shape in enclosures: if shape.ycontains(pin): edge_list.append(shape) - + # Sort them by their right edge edge_list.sort(key=lambda x: x.rx()) # Find the right edge that is to the pin's left edge left_item = None for item in edge_list: - if item.rx()<=pin.lx(): + if item.rx() <= pin.lx(): left_item = item else: break @@ -284,13 +291,13 @@ class pin_group: # If it already overlaps, no connector needed if left_item.overlaps(pin): return None - + # Otherwise, make a connector to the item p = self.compute_connector(pin, left_item) return p - + def find_right_connector(self, pin, enclosures): - """ + """ Find the enclosure that is to the right of the pin and make a connector to it's left edge. """ @@ -299,7 +306,7 @@ class pin_group: for shape in enclosures: if shape.ycontains(pin): edge_list.append(shape) - + # Sort them by their right edge edge_list.sort(key=lambda x: x.lx(), reverse=True) @@ -317,11 +324,11 @@ class pin_group: # If it already overlaps, no connector needed if right_item.overlaps(pin): return None - + # Otherwise, make a connector to the item p = self.compute_connector(pin, right_item) return p - + def find_smallest_connector(self, pin_list, shape_list): """ Compute all of the connectors between the overlapping @@ -334,7 +341,7 @@ class pin_group: new_enclosure = self.compute_connector(pin, enclosure) if not smallest or new_enclosure.area() < smallest.area(): smallest = new_enclosure - + return smallest def find_smallest_overlapping(self, pin_list, shape_list): @@ -350,7 +357,7 @@ class pin_group: # overlap_length = pin.overlap_length(overlap_shape) if not smallest_shape or overlap_shape.area() < smallest_shape.area(): smallest_shape = overlap_shape - + return smallest_shape def find_smallest_overlapping_pin(self, pin, shape_list): @@ -369,9 +376,9 @@ class pin_group: if overlap_length > min_width: if not smallest_shape or other.area() < smallest_shape.area(): smallest_shape = other - + return smallest_shape - + def overlap_any_shape(self, pin_list, shape_list): """ Does the given pin overlap any of the shapes in the pin list. @@ -391,7 +398,7 @@ class pin_group: for pin in pin_list: if pin.area() > biggest.area(): biggest = pin - + return pin def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST): @@ -406,8 +413,7 @@ class pin_group: offset2 = direction.get_offset(dir2) # We may have started with an empty set - if not self.grids: - return None + debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.") # Start with the ll and make the widest row row = [ll] @@ -436,7 +442,7 @@ class pin_group: # Add a shape from ll to ur ur = row[-1] return (ll, ur) - + def enclose_pin(self): """ If there is one set of connected pin shapes, @@ -445,13 +451,13 @@ class pin_group: If there is not, it simply returns all the enclosures. """ self.enclosed = True - + # Compute the enclosure pin_layout list of the set of tracks self.enclosures = self.compute_enclosures() # Find a connector to every pin and add it to the enclosures for pin in self.pins: - + # If it is contained, it won't need a connector if pin.contained_by_any(self.enclosures): continue @@ -469,7 +475,7 @@ class pin_group: right_connector, above_connector, below_connector] - filtered_list = list(filter(lambda x: x!=None, connector_list)) + filtered_list = list(filter(lambda x: x != None, connector_list)) if (len(filtered_list) > 0): import copy bbox_connector = copy.copy(pin) @@ -487,8 +493,9 @@ class pin_group: debug.error("Could not find a connector for {} with {}".format(self.pins, self.enclosures)) self.router.write_debug_gds("no_connector.gds") + import pdb; pdb.set_trace() self.enclosures.append(connector) - + # At this point, the pins are overlapping, # but there might be more than one! overlap_set = set() @@ -505,12 +512,11 @@ class pin_group: (sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name, enclosure) self.grids.update(sufficient) - - - debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name, - self.pins, - self.grids, - self.enclosures)) + + debug.info(3, "Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name, + self.pins, + self.grids, + self.enclosures)) def transitive_overlap(self, shape, shape_list): """ @@ -532,22 +538,22 @@ class pin_group: # Remove the original shape connected_set.remove(shape) - + # if len(connected_set) 0: debug.info(2, "Removing blocks {}".format(shared_set)) blockage_set.difference_update(shared_set) - + # At least one of the groups must have some valid tracks if (len(pin_set) == 0 and len(partial_set) == 0 and len(blockage_set) == 0): # debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins)) - + for pin in self.pins: debug.warning(" Expanding conversion {0}".format(pin)) # Determine which tracks the pin overlaps - (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, - pin, - expansion=1) + (sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name, + pin, + expansion=1) pin_set.update(sufficient) partial_set.update(insufficient) - + if len(pin_set) == 0 and len(partial_set) == 0: debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins)) @@ -653,11 +659,12 @@ class pin_group: # Consider all the grids that would be blocked self.grids = pin_set | partial_set + if len(self.grids) < 0: + debug.error("Did not find any unblocked grids: {}".format(str(self.pins))) + self.router.write_debug_gds("blocked_pin.gds") + # Remember the secondary grids for removing adjacent pins self.secondary_grids = partial_set debug.info(2, " pins {}".format(self.grids)) debug.info(2, " secondary {}".format(self.secondary_grids)) - - - diff --git a/compiler/router/router.py b/compiler/router/router.py index 7be3a4c0..cacd5117 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -27,11 +27,10 @@ class router(router_tech): route on a given layer. This is limited to two layer routes. It populates blockages on a grid class. """ - def __init__(self, layers, design, gds_filename=None, rail_track_width=1): """ This will instantiate a copy of the gds file or the module at (0,0) and - route on top of this. The blockages from the gds/module will be + route on top of this. The blockages from the gds/module will be considered. """ router_tech.__init__(self, layers, rail_track_width) @@ -84,7 +83,7 @@ class router(router_tech): self.boundary = self.layout.measureBoundary(self.top_name) # These must be un-indexed to get rid of the matrix type self.ll = vector(self.boundary[0][0], self.boundary[0][1]) - self.ur = vector(self.boundary[1][0], self.boundary[1][1]) + self.ur = vector(self.boundary[1][0], self.boundary[1][1]) def clear_pins(self): """ @@ -93,7 +92,7 @@ class router(router_tech): """ self.pins = {} self.all_pins = set() - self.pin_groups = {} + self.pin_groups = {} # DO NOT clear the blockages as these don't change self.rg.reinit() @@ -112,7 +111,7 @@ class router(router_tech): """ Retrieve the pin shapes on metal 3 from the layout. """ - debug.info(2, "Retrieving pins for {}.".format(pin_name)) + debug.info(2, "Retrieving pins for {}.".format(pin_name)) shape_list = self.layout.getAllPinShapes(str(pin_name)) pin_set = set() for shape in shape_list: @@ -140,13 +139,13 @@ class router(router_tech): This doesn't consider whether the obstacles will be pins or not. They get reset later if they are not actually a blockage. """ - debug.info(1, "Finding blockages.") - for layer in [self.vert_layer_number, self.horiz_layer_number]: - self.retrieve_blockages(layer) + debug.info(1, "Finding blockages.") + for lpp in [self.vert_lpp, self.horiz_lpp]: + self.retrieve_blockages(lpp) def find_pins_and_blockages(self, pin_list): """ - Find the pins and blockages in the design + Find the pins and blockages in the design """ # This finds the pin shapes and sorts them into "groups" that # are connected. This must come before the blockages, so we @@ -447,7 +446,7 @@ class router(router_tech): """ # Inflate the blockage by half a spacing rule [ll, ur] = self.convert_blockage_to_tracks(blockage.inflate()) - zlayer = self.get_zindex(blockage.lpp[0]) + zlayer = self.get_zindex(blockage.lpp) blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer) return blockage_tracks @@ -459,19 +458,19 @@ class router(router_tech): blockage_list = self.convert_blockage(blockage) self.blocked_grids.update(blockage_list) - def retrieve_blockages(self, layer_num): + def retrieve_blockages(self, lpp): """ Recursive find boundaries as blockages to the routing grid. """ - shapes = self.layout.getAllShapes(layer_num) + shapes = self.layout.getAllShapes(lpp) for boundary in shapes: ll = vector(boundary[0], boundary[1]) ur = vector(boundary[2], boundary[3]) rect = [ll, ur] new_pin = pin_layout("blockage{}".format(len(self.blockages)), rect, - layer_num) + lpp) # If there is a rectangle that is the same in the pins, # it isn't a blockage! @@ -529,7 +528,7 @@ class router(router_tech): sufficient_list = set() insufficient_list = set() - zindex = self.get_zindex(pin.lpp[0]) + zindex = self.get_zindex(pin.lpp) for x in range(int(ll[0]) + expansion, int(ur[0]) + 1 + expansion): for y in range(int(ll[1] + expansion), int(ur[1]) + 1 + expansion): (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, @@ -622,7 +621,6 @@ class router(router_tech): """ Return all tracks that an inflated pin overlaps """ - # This is using the full track shape rather # than a single track pin shape # because we will later patch a connector if there isn't overlap. @@ -807,8 +805,8 @@ class router(router_tech): def enclose_pins(self): """ This will find the biggest rectangle enclosing some grid squares and - put a rectangle over it. It does not enclose grid squares that are blocked - by other shapes. + put a rectangle over it. It does not enclose grid squares + that are blocked by other shapes. """ for pin_name in self.pin_groups: debug.info(1, "Enclosing pins for {}".format(pin_name)) diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index 49df06fd..efae2708 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -5,13 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc,layer +from tech import drc, layer from contact import contact -from pin_group import pin_group from vector import vector import debug import math + class router_tech: """ This is a class to hold the router tech constants. @@ -25,9 +25,9 @@ class router_tech: self.layers = layers self.rail_track_width = rail_track_width - if len(self.layers)==1: + if len(self.layers) == 1: self.horiz_layer_name = self.vert_layer_name = self.layers[0] - self.horiz_layer_number = self.vert_layer_number = layer[self.layers[0]] + self.horiz_lpp = self.vert_lpp = layer[self.layers[0]] (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1) (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0) @@ -40,8 +40,8 @@ class router_tech: via_connect = contact(self.layers, (1, 1)) max_via_size = max(via_connect.width,via_connect.height) - self.horiz_layer_number = layer[self.horiz_layer_name] - self.vert_layer_number = layer[self.vert_layer_name] + self.horiz_lpp = layer[self.horiz_layer_name] + self.vert_lpp = layer[self.vert_layer_name] (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1) (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0) @@ -68,8 +68,18 @@ class router_tech: # When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side) self.layer_widths = [self.track_wire, 1, self.track_wire] - def get_zindex(self,layer_num): - if layer_num==self.horiz_layer_number: + def same_lpp(self, lpp1, lpp2): + """ + Check if the layers and purposes are the same. + Ignore if purpose is a None. + """ + if lpp1[1] == None or lpp2[1] == None: + return lpp1[0] == lpp2[0] + + return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1] + + def get_zindex(self, lpp): + if self.same_lpp(lpp, self.horiz_lpp): return 0 else: return 1 diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 5ddabc98..881e87b2 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -55,7 +55,7 @@ class supply_grid_router(router): self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width) def route(self, vdd_name="vdd", gnd_name="gnd"): - """ + """ Add power supply rails and connect all pins to these rails. """ debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) @@ -75,7 +75,6 @@ class supply_grid_router(router): start_time = datetime.now() self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) print_time("Finding pins and blockages",datetime.now(), start_time, 3) - # Add the supply rails in a mesh network and connect H/V with vias start_time = datetime.now() # Block everything diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 8406fa53..4ffeb4fe 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -67,7 +67,6 @@ layer["via9"] = (28, 0) layer["metal10"] = (29, 0) layer["text"] = (239, 0) layer["boundary"]= (239, 0) -#layer["blockage"]= (239, 0) ################################################### ##END GDS Layer Map diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index c74d7ee5..6edaf7aa 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -54,7 +54,6 @@ layer["via3"] = (30, 0) layer["metal4"] = (31, 0) layer["text"] = (63, 0) layer["boundary"] = (63, 0) -#layer["blockage"] = (83, 0) ################################################### ##END GDS Layer Map