diff --git a/compiler/base/utils.py b/compiler/base/utils.py index f9597f39..6e25730b 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -88,9 +88,9 @@ def get_libcell_size(name, units, layer): return(get_gds_size(name, cell_gds, units, layer)) -def get_gds_pins(pin_list, name, gds_filename, units, layer): +def get_gds_pins(pin_names, name, gds_filename, units): """ - Open a GDS file and find the pins in pin_list as text on a given layer. + Open a GDS file and find the pins in pin_names as text on a given layer. Return these as a rectangle layer pair for each pin. """ cell_vlsi = gdsMill.VlsiLayout(units=units) @@ -98,23 +98,23 @@ def get_gds_pins(pin_list, name, gds_filename, units, layer): reader.loadFromFile(gds_filename) cell = {} - for pin in pin_list: - cell[str(pin)]=[] - label_list=cell_vlsi.getPinShapeByLabel(str(pin)) - for label in label_list: - (name,layer,boundary)=label + for pin_name in pin_names: + cell[str(pin_name)]=[] + pin_list=cell_vlsi.getAllPinShapes(str(pin_name)) + for pin_shape in pin_list: + (layer,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)].append(pin_layout(pin, rect, layer)) + cell[str(pin_name)].append(pin_layout(pin_name, rect, layer)) return cell -def get_libcell_pins(pin_list, name, units, layer): +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. Return these as a rectangle layer pair for each pin. """ cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" - return(get_gds_pins(pin_list, name, cell_gds, units, layer)) + return(get_gds_pins(pin_list, name, cell_gds, units)) diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index e6943f47..5df86c87 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -13,7 +13,7 @@ class bitcell(design.design): pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) def __init__(self): design.design.__init__(self, "cell_6t") diff --git a/compiler/bitcells/bitcell_1rw_1r.py b/compiler/bitcells/bitcell_1rw_1r.py index 2f13a910..47ebe5fc 100644 --- a/compiler/bitcells/bitcell_1rw_1r.py +++ b/compiler/bitcells/bitcell_1rw_1r.py @@ -13,7 +13,7 @@ class bitcell_1rw_1r(design.design): pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] (width,height) = utils.get_libcell_size("cell_1rw_1r", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"]) def __init__(self): design.design.__init__(self, "cell_1rw_1r") diff --git a/compiler/bitcells/replica_bitcell.py b/compiler/bitcells/replica_bitcell.py index 7bbdbe06..ca4b72c0 100644 --- a/compiler/bitcells/replica_bitcell.py +++ b/compiler/bitcells/replica_bitcell.py @@ -12,7 +12,7 @@ class replica_bitcell(design.design): pin_names = ["bl", "br", "wl", "vdd", "gnd"] (width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"]) def __init__(self): design.design.__init__(self, "replica_cell_6t") diff --git a/compiler/bitcells/replica_bitcell_1rw_1r.py b/compiler/bitcells/replica_bitcell_1rw_1r.py index 790a6251..aaf5b1dc 100644 --- a/compiler/bitcells/replica_bitcell_1rw_1r.py +++ b/compiler/bitcells/replica_bitcell_1rw_1r.py @@ -12,7 +12,7 @@ class replica_bitcell_1rw_1r(design.design): pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] (width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"]) def __init__(self): design.design.__init__(self, "replica_cell_1rw_1r") diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index f6d301a1..21faeeaa 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -60,6 +60,9 @@ class VlsiLayout: self.tempCoordinates=None self.tempPassFail = True + # This is the pin map + self.pins = {} + def rotatedCoordinates(self,coordinatesToRotate,rotateAngle): #helper method to rotate a list of coordinates angle=math.radians(float(0)) @@ -206,7 +209,11 @@ class VlsiLayout: def initialize(self): self.deduceHierarchy() #self.traverseTheHierarchy() - self.populateCoordinateMap() + self.populateCoordinateMap() + + for layerNumber in self.layerNumbersInUse: + self.processLabelPins(layerNumber) + def populateCoordinateMap(self): def addToXyTree(startingStructureName = None,transformPath = None): @@ -478,6 +485,10 @@ class VlsiLayout: return False #these shapes are ok def isPointInsideOfBox(self,pointCoordinates,boxCoordinates): + """ + Check if a point is contained in the shape + """ + debug.check(len(boxCoordinates)==4,"Invalid number of coordinates for box.") leftBound = boxCoordinates[0][0] rightBound = boxCoordinates[0][0] topBound = boxCoordinates[0][1] @@ -499,7 +510,9 @@ class VlsiLayout: return True def isShapeInsideOfBox(self,shapeCoordinates, boxCoordinates): - #go through every point in the shape to test if they are all inside the box + """ + Go through every point in the shape to test if they are all inside the box. + """ for point in shapeCoordinates: if not self.isPointInsideOfBox(point,boxCoordinates): return False @@ -634,160 +647,85 @@ class VlsiLayout: return cellBoundary - def getLabelDBInfo(self,label_name): + def getTexts(self, layer): """ - Return the coordinates in DB units and layer of all matching labels + Get all of the labels on a given layer only at the root level. """ - label_list = [] - label_layer = None - label_coordinate = [None, None] - - # Why must this be the last one found? It breaks if we return the first. + text_list = [] for Text in self.structures[self.rootStructureName].texts: - if Text.textString == label_name or Text.textString == label_name+"\x00": - label_layer = Text.drawingLayer - label_coordinate = Text.coordinates[0] - if label_layer!=None: - label_list.append((label_coordinate,label_layer)) - - debug.check(len(label_list)>0,"Did not find labels {0}.".format(label_name)) - return label_list - - - def getLabelInfo(self,label_name): - """ - Return the coordinates in USER units and layer of a label - """ - label_list=self.getLabelDBInfo(label_name) - new_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - user_coordinates = [x*self.units[0] for x in label_coordinate] - new_list.append(user_coordinates,label_layer) - return new_list + if Text.drawingLayer == layer: + text_list.append(Text) + return text_list - def getPinShapeByLocLayer(self, coordinate, layer): - """ - Return the largest enclosing rectangle on a layer and at a location. - Coordinates should be in USER units. - """ - db_coordinate = [x/self.units[0] for x in coordinate] - return self.getPinShapeByDBLocLayer(db_coordinate, layer) - - def getPinShapeByDBLocLayer(self, coordinate, layer): - """ - Return the largest enclosing rectangle on a layer and at a location. - Coordinates should be in DB units. - """ - pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer) - - if len(pin_boundaries) == 0: - debug.warning("Did not find pin on layer {0} at coordinate {1}".format(layer, coordinate)) - - # sort the boundaries, return the max area pin boundary - pin_boundaries.sort(key=boundaryArea,reverse=True) - pin_boundary=pin_boundaries[0] - - # Convert to USER units - pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0], - pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]] - - # Make a name if we don't have the pin name - return ["p"+str(coordinate)+"_"+str(layer), layer, pin_boundary] - - def getAllPinShapesByLocLayer(self, coordinate, layer): - """ - Return ALL the enclosing rectangles on the same layer - at the given coordinate. Coordinates should be in USER units. - """ - db_coordinate = [int(x/self.units[0]) for x in coordinate] - return self.getAllPinShapesByDBLocLayer(db_coordinate, layer) - - def getAllPinShapesByDBLocLayer(self, coordinate, layer): - """ - Return ALL the enclosing rectangles on the same layer - at the given coordinate. Input coordinates should be in DB units. - Returns user unit shapes. - """ - pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer) - - # Convert to user units - new_boundaries = [] - for pin_boundary in pin_boundaries: - new_pin_boundary = [pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0], - pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]] - new_boundaries.append(["p"+str(coordinate)+"_"+str(layer), layer, new_pin_boundary]) - return new_boundaries - - def getPinShapeByLabel(self,label_name): + def getPinShape(self, pin_name): """ Search for a pin label and return the largest enclosing rectangle on the same layer as the pin label. + Signal an error if there are multiple labels on different layers. """ - label_list=self.getLabelDBInfo(label_name) - shape_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - shape = self.getPinShapeByDBLocLayer(label_coordinate, label_layer) - shape_list.append(shape) - return shape_list + pin_list = self.getAllPinShapes(pin_name) + max_pin = None + max_area = 0 + for pin in pin_list: + (layer,boundary) = pin + new_area = boundaryArea(boundary) + if max_pin == None or new_area>max_area: + max_pin = pin + max_area = new_area - def getAllPinShapesByLabel(self,label_name): + return max_pin + + + def getAllPinShapes(self, pin_name): """ Search for a pin label and return ALL the enclosing rectangles on the same layer as the pin label. """ - - label_list=self.getLabelDBInfo(label_name) - shape_list=[] - for label in label_list: - (label_coordinate,label_layer)=label - shape_list.extend(self.getAllPinShapesByDBLocLayer(label_coordinate, label_layer)) + shape_list = [] + pin_map = self.pins[pin_name] + for pin in pin_map: + (pin_layer, boundary) = pin + shape_list.append(pin) + return shape_list - - def getAllPinShapesInStructureList(self,coordinates,layer): + + + def processLabelPins(self, layer): """ - Given a coordinate, search for enclosing structures on the given layer. - Return all pin shapes. + Find all text labels and create a map to a list of shapes that + they enclose on the given layer. """ - boundaries = [] - for TreeUnit in self.xyTree: - boundaries.extend(self.getPinInStructure(coordinates,layer,TreeUnit)) + # Get the labels on a layer in the root level + labels = self.getTexts(layer) + # Get all of the shapes on the layer at all levels + # and transform them to the current level + shapes = self.getAllShapes(layer) - return boundaries + 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): + pin_shapes.append((layer, boundary)) + label_text = label.textString + # Remove the padding if it exists + if label_text[-1] == "\x00": + label_text = label_text[0:-1] - def getPinInStructure(self,coordinates,layer,structure): - """ - Go through all the shapes in a structure and return the list of shapes - that the label coordinates are inside. + try: + self.pins[label_text] + except KeyError: + self.pins[label_text] = [] + self.pins[label_text].extend(pin_shapes) + + + + def getAllShapes(self,layer): """ - - (structureName,structureOrigin,structureuVector,structurevVector)=structure - boundaries = [] - for boundary in self.structures[str(structureName)].boundaries: - # Pin enclosures only work on rectangular pins so ignore any non rectangle - # This may report not finding pins, but the user should fix this by adding a rectangle. - if len(boundary.coordinates)!=5: - continue - if layer==boundary.drawingLayer: - 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=self.transformRectangle(boundaryRect,structureuVector,structurevVector) - boundaryRect=[boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(), - boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()] - - if self.labelInRectangle(coordinates,boundaryRect): - boundaries.append(boundaryRect) - - return boundaries - - - def getAllShapesInStructureList(self,layer): - """ - Return all pin shapes on a given layer. + Return all gshapes on a given layer in [llx, lly, urx, ury] format and + user units. """ boundaries = [] for TreeUnit in self.xyTree: @@ -812,7 +750,8 @@ class VlsiLayout: def getShapesInStructure(self,layer,structure): """ - Go through all the shapes in a structure and return the list of shapes. + Go through all the shapes in a structure and return the list of shapes in + the form [llx, lly, urx, ury] """ (structureName,structureOrigin,structureuVector,structurevVector)=structure @@ -820,6 +759,7 @@ class VlsiLayout: boundaries = [] for boundary in self.structures[str(structureName)].boundaries: # FIXME: Right now, this only supports rectangular shapes! + #debug.check(len(boundary.coordinates)==5,"Non-rectangular shape.") if len(boundary.coordinates)!=5: continue if layer==boundary.drawingLayer: @@ -874,8 +814,8 @@ class VlsiLayout: """ Checks if a coordinate is within a given rectangle. Rectangle is [leftx, bottomy, rightx, topy]. """ - coordinate_In_Rectangle_x_range=(coordinate[0]>=int(rectangle[0]))&(coordinate[0]<=int(rectangle[2])) - coordinate_In_Rectangle_y_range=(coordinate[1]>=int(rectangle[1]))&(coordinate[1]<=int(rectangle[3])) + coordinate_In_Rectangle_x_range=(coordinate[0]>=rectangle[0])&(coordinate[0]<=rectangle[2]) + coordinate_In_Rectangle_y_range=(coordinate[1]>=rectangle[1])&(coordinate[1]<=rectangle[3]) if coordinate_In_Rectangle_x_range & coordinate_In_Rectangle_y_range: return True else: diff --git a/compiler/modules/dff.py b/compiler/modules/dff.py index d72aae2e..936bc822 100644 --- a/compiler/modules/dff.py +++ b/compiler/modules/dff.py @@ -12,7 +12,7 @@ class dff(design.design): pin_names = ["D", "Q", "clk", "vdd", "gnd"] (width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"]) def __init__(self, name="dff"): design.design.__init__(self, name) diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index 3a857efd..e2a0e131 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -13,7 +13,7 @@ class sense_amp(design.design): pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"] (width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"]) def __init__(self, name): design.design.__init__(self, name) diff --git a/compiler/modules/tri_gate.py b/compiler/modules/tri_gate.py index cce7683c..da42c7a8 100644 --- a/compiler/modules/tri_gate.py +++ b/compiler/modules/tri_gate.py @@ -12,7 +12,7 @@ class tri_gate(design.design): pin_names = ["in", "en", "en_bar", "out", "gnd", "vdd"] (width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"]) unique_id = 1 diff --git a/compiler/modules/write_driver.py b/compiler/modules/write_driver.py index 67477a8d..8d1291a7 100644 --- a/compiler/modules/write_driver.py +++ b/compiler/modules/write_driver.py @@ -13,7 +13,7 @@ class write_driver(design.design): pin_names = ["din", "bl", "br", "en", "gnd", "vdd"] (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) - pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"], layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"]) def __init__(self, name): design.design.__init__(self, name) diff --git a/compiler/router/router.py b/compiler/router/router.py index 41a5ad9a..ad96de19 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1,6 +1,7 @@ import sys import gdsMill from tech import drc,GDS +from tech import layer as techlayer import math import debug from router_tech import router_tech @@ -39,7 +40,7 @@ class router(router_tech): self.reader = gdsMill.Gds2reader(self.layout) self.reader.loadFromFile(gds_filename) self.top_name = self.layout.rootStructureName - + ### The pin data structures # A map of pin names to a set of pin_layout structures self.pins = {} @@ -95,12 +96,13 @@ class router(router_tech): def retrieve_pins(self,pin_name): """ - Retrieve the pin shapes from the layout. + Retrieve the pin shapes on metal 3 from the layout. """ - shape_list=self.layout.getAllPinShapesByLabel(str(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: - (name,layer,boundary)=shape + (layer,boundary)=shape # GDSMill boundaries are in (left, bottom, right, top) order # so repack and snap to the grid ll = vector(boundary[0],boundary[1]).snap_to_grid() @@ -125,10 +127,14 @@ class router(router_tech): Pin can either be a label or a location,layer pair: [[x,y],layer]. """ debug.info(1,"Finding pins for {}.".format(pin_name)) - + import datetime + from globals import print_time + start_time = datetime.datetime.now() self.retrieve_pins(pin_name) + print_time("Retrieve pins", datetime.datetime.now(), start_time) + start_time = datetime.datetime.now() self.analyze_pins(pin_name) - + print_time("Analyze pins", datetime.datetime.now(), start_time) def find_blockages(self): """ @@ -443,7 +449,7 @@ class router(router_tech): Recursive find boundaries as blockages to the routing grid. """ - shapes = self.layout.getAllShapesInStructureList(layer_num) + shapes = self.layout.getAllShapes(layer_num) for boundary in shapes: ll = vector(boundary[0],boundary[1]) ur = vector(boundary[2],boundary[3]) @@ -626,6 +632,8 @@ class router(router_tech): """ Analyze the shapes of a pin and combine them into groups which are connected. """ + debug.info(2,"Analyzing pin groups for {}.".format(pin_name)) + pin_set = self.pins[pin_name] local_debug = False diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 0367c5f2..4ec40dc7 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -16,22 +16,25 @@ class library_lvs_test(openram_test): import verify (gds_dir, sp_dir, allnames) = setup_files() + drc_errors = 0 lvs_errors = 0 debug.info(1, "Performing LVS on: " + ", ".join(allnames)) for f in allnames: gds_name = "{0}/{1}.gds".format(gds_dir, f) sp_name = "{0}/{1}.sp".format(sp_dir, f) + name = re.sub('\.gds$', '', f) if not os.path.isfile(gds_name): lvs_errors += 1 debug.error("Missing GDS file {}".format(gds_name)) if not os.path.isfile(sp_name): lvs_errors += 1 debug.error("Missing SPICE file {}".format(gds_name)) + drc_errors += verify.run_drc(name, gds_name) lvs_errors += verify.run_lvs(f, gds_name, sp_name) # fail if the error count is not zero - self.assertEqual(lvs_errors, 0) + self.assertEqual(drc_errors+lvs_errors, 0) globals.end_openram() def setup_files():