diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c83a091a..6eceeaaf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,13 @@ other OpenRAM features. Please see the README.md file on how to run the unit tests. Unit tests should work in all technologies. We will run the tests on your contributions before they will be accepted. +# Internally Development + +For internal development, follow all of the following steps EXCEPT +do not fork your own copy. Instead, create a branch in our private repository +and consult with me when you want to merge it into the dev branch. +All unit tests should pass first. + # Pull Request Process 1. One time, create a GitHub account at http://github.com diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 2e9bc2bb..7144a2cf 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -59,8 +59,8 @@ class geometry: We must then re-find the ll and ur. The master is the cell instance. """ if OPTS.netlist_only: return - (ll,ur) = [vector(0,0),vector(self.width,self.height)] + if mirror=="MX": ll=ll.scale(1,-1) ur=ur.scale(1,-1) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 87659bd7..320276cc 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -16,8 +16,14 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): def __init__(self, name): - self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds" - self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" + try: + self.gds_file + except AttributeError: + self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds" + try: + self.sp_file + except AttributeError: + self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" self.name = name hierarchy_layout.layout.__init__(self, name) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 780baaf9..01a48f89 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -251,7 +251,7 @@ class layout(lef.lef): width=drc["minwidth_{0}".format(layer)] if height==None: height=drc["minwidth_{0}".format(layer)] - + new_pin = pin_layout(text, [offset,offset+vector(width,height)], layer) try: @@ -312,7 +312,7 @@ class layout(lef.lef): position_list=coordinates, width=width) - def add_route(self, design, layers, coordinates): + def add_route(self, layers, coordinates): """Connects a routing path on given layer,coordinates,width. The layers are the (horizontal, via, vertical). add_wire assumes preferred direction routing whereas this includes layers in @@ -390,13 +390,14 @@ class layout(lef.lef): elif rotate==90: corrected_offset = offset + vector(0.5*height,-0.5*width) elif rotate==180: - corrected_offset = offset + vector(-0.5*width,0.5*height) + corrected_offset = offset + vector(0.5*width,0.5*height) elif rotate==270: corrected_offset = offset + vector(-0.5*height,0.5*width) else: debug.error("Invalid rotation argument.",-1) + #print(rotate,offset,"->",corrected_offset) self.add_mod(via) inst=self.add_inst(name=via.name, mod=via, @@ -842,19 +843,21 @@ class layout(lef.lef): width=xmax-xmin, height=ymax-ymin) - def add_power_pin(self, name, loc, rotate=True): + def add_power_pin(self, name, loc, rotate=90): """ - Add a single power pin from M3 own to M1 + Add a single power pin from M3 down to M1 at the given center location """ self.add_via_center(layers=("metal1", "via1", "metal2"), offset=loc, - rotate=90 if rotate else 0) - self.add_via_center(layers=("metal2", "via2", "metal3"), - offset=loc, - rotate=90 if rotate else 0) + rotate=rotate) + via=self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=loc, + rotate=rotate) self.add_layout_pin_rect_center(text=name, layer="metal3", - offset=loc) + offset=loc, + width=via.width, + height=via.height) def add_power_ring(self, bbox): """ diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 0152a6c8..a82eba1b 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -117,7 +117,7 @@ class spice(verilog.verilog): def sp_read(self): """Reads the sp file (and parse the pins) from the library Otherwise, initialize it to null for dynamic generation""" - if os.path.isfile(self.sp_file): + if self.sp_file and os.path.isfile(self.sp_file): debug.info(3, "opening {0}".format(self.sp_file)) f = open(self.sp_file) self.spice = f.readlines() diff --git a/compiler/base/route.py b/compiler/base/route.py index 03b62cff..beff1a58 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -1,11 +1,12 @@ from tech import drc import debug +from design import design from contact import contact from itertools import tee from vector import vector from vector3d import vector3d -class route(): +class route(design): """ Object route (used by the router module) Add a route of minimium metal width between a set of points. @@ -14,10 +15,13 @@ class route(): The points are the center of the wire. This can have non-preferred direction routing. """ + + unique_route_id = 0 + def __init__(self, obj, layer_stack, path): name = "route_{0}".format(route.unique_route_id) route.unique_route_id += 1 - design.design.__init__(self, name) + design.__init__(self, name) debug.info(3, "create route obj {0}".format(name)) self.obj = obj @@ -52,7 +56,7 @@ class route(): next(b, None) return zip(a, b) - plist = pairwise(self.path) + plist = list(pairwise(self.path)) for p0,p1 in plist: if p0.z != p1.z: # via # offset if not rotated @@ -67,6 +71,13 @@ class route(): self.draw_corner_wire(p1) # draw the point to point wire self.draw_wire(p0,p1) + + + # Draw the layers on the ends of the wires to ensure full width + # connections + self.draw_corner_wire(plist[0][0]) + self.draw_corner_wire(plist[-1][1]) + @@ -99,10 +110,10 @@ class route(): height = end.y - start.y width = layer_width - deisgn.add_rect(layer=layer_name, - offset=offset, - width=width, - height=height) + self.obj.add_rect(layer=layer_name, + offset=vector(offset.x,offset.y), + width=width, + height=height) def draw_corner_wire(self, p0): diff --git a/compiler/base/utils.py b/compiler/base/utils.py index cb63b68a..b13f2f7e 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -37,12 +37,6 @@ def pin_center(boundary): """ return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])] -def pin_rect(boundary): - """ - This returns a LL,UR point pair. - """ - return [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] - def auto_measure_libcell(pin_list, name, units, layer): """ Open a GDS file and find the pins in pin_list as text on a given layer. @@ -66,15 +60,14 @@ def auto_measure_libcell(pin_list, name, units, layer): -def get_libcell_size(name, units, layer): +def get_gds_size(name, gds_filename, units, layer): """ - Open a GDS file and return the library cell size from either the + Open a GDS file and return the size from either the bounding box or a border layer. """ - cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" cell_vlsi = gdsMill.VlsiLayout(units=units) reader = gdsMill.Gds2reader(cell_vlsi) - reader.loadFromFile(cell_gds) + reader.loadFromFile(gds_filename) cell = {} measure_result = cell_vlsi.getLayoutBorder(layer) @@ -83,16 +76,23 @@ def get_libcell_size(name, units, layer): # returns width,height return measure_result +def get_libcell_size(name, units, layer): + """ + Open a GDS file and return the library cell size from either the + bounding box or a border layer. + """ + cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" + return(get_gds_size(name, cell_gds, units, layer)) -def get_libcell_pins(pin_list, name, units, layer): + +def get_gds_pins(pin_list, name, gds_filename, units, layer): """ 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" cell_vlsi = gdsMill.VlsiLayout(units=units) reader = gdsMill.Gds2reader(cell_vlsi) - reader.loadFromFile(cell_gds) + reader.loadFromFile(gds_filename) cell = {} for pin in pin_list: @@ -100,11 +100,19 @@ def get_libcell_pins(pin_list, name, units, layer): label_list=cell_vlsi.getPinShapeByLabel(str(pin)) for label in label_list: (name,layer,boundary)=label - rect = pin_rect(boundary) + 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)) return cell +def get_libcell_pins(pin_list, name, units, layer): + """ + 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)) + diff --git a/compiler/gdsMill/gdsMill/gds2reader.py b/compiler/gdsMill/gdsMill/gds2reader.py index a162361e..3d63cf53 100644 --- a/compiler/gdsMill/gdsMill/gds2reader.py +++ b/compiler/gdsMill/gdsMill/gds2reader.py @@ -294,7 +294,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisSref.transFlags=(mirrorFlag,rotateFlag,magnifyFlag) + thisSref.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print( "\t\t\tRotate:"+str(rotateFlag)) @@ -345,7 +345,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisAref.transFlags=(mirrorFlag,rotateFlag,magnifyFlag) + thisAref.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print("\t\t\tRotate:"+str(rotateFlag)) @@ -408,7 +408,7 @@ class Gds2reader: mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy rotateFlag = bool(transFlags&0x0002) magnifyFlag = bool(transFlags&0x0004) - thisText.transFlags=(mirrorFlag,rotateFlag,magnifyFlag) + thisText.transFlags=[mirrorFlag,rotateFlag,magnifyFlag] if(self.debugToTerminal==1): print("\t\t\tMirror X:"+str(mirrorFlag)) print("\t\t\tRotate:"+str(rotateFlag)) diff --git a/compiler/gdsMill/gdsMill/gdsPrimitives.py b/compiler/gdsMill/gdsMill/gdsPrimitives.py index c0981acd..9b148dce 100644 --- a/compiler/gdsMill/gdsMill/gdsPrimitives.py +++ b/compiler/gdsMill/gdsMill/gdsPrimitives.py @@ -118,7 +118,7 @@ class GdsSref: self.elementFlags="" self.plex="" self.sName="" - self.transFlags=(False,False,False) + self.transFlags=[0,0,0] self.magFactor="" self.rotateAngle="" self.coordinates="" @@ -129,7 +129,7 @@ class GdsAref: self.elementFlags="" self.plex="" self.aName="" - self.transFlags=(False,False,False) + self.transFlags=[0,0,0] self.magFactor="" self.rotateAngle="" self.coordinates="" @@ -141,7 +141,7 @@ class GdsText: self.plex="" self.drawingLayer="" self.purposeLayer = None - self.transFlags=(False,False,False) + self.transFlags=[0,00] self.magFactor="" self.rotateAngle="" self.pathType="" @@ -167,4 +167,4 @@ class GdsBox: self.drawingLayer="" self.purposeLayer = None self.boxValue="" - self.coordinates="" \ No newline at end of file + self.coordinates="" diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index cd00bfaf..b9813149 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -70,7 +70,7 @@ class VlsiLayout: for coordinate in coordinatesToRotate: newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle) newY = coordinate[0]*math.sin(angle) + coordinate[1]*math.cos(angle) - coordinatesRotate += [(newX,newY)] + coordinatesRotate.extend((newX,newY)) return coordinatesRotate def rename(self,newName): @@ -141,7 +141,7 @@ class VlsiLayout: contained by any other structure. this is the root.""" structureNames=[] for name in self.structures: - structureNames+=[name] + structureNames.append(name) for name in self.structures: if(len(self.structures[name].srefs)>0): #does this structure reference any others? @@ -152,7 +152,7 @@ class VlsiLayout: self.rootStructureName = structureNames[0] def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None, - transformPath = [], rotateAngle = 0, transFlags = (0,0,0), coordinates = (0,0)): + transformPath = [], rotateAngle = 0, transFlags = [0,0,0], coordinates = (0,0)): #since this is a recursive function, must deal with the default #parameters explicitly if startingStructureName == None: @@ -160,11 +160,12 @@ class VlsiLayout: #set up the rotation matrix if(rotateAngle == None or rotateAngle == ""): - rotateAngle = 0 + angle = 0 else: - rotateAngle = math.radians(float(rotateAngle)) - mRotate = matrix([[math.cos(rotateAngle),-math.sin(rotateAngle),0.0], - [math.sin(rotateAngle),math.cos(rotateAngle),0.0], + # MRG: Added negative to make CCW rotate 8/29/18 + angle = math.radians(float(rotateAngle)) + mRotate = matrix([[math.cos(angle),-math.sin(angle),0.0], + [math.sin(angle),math.cos(angle),0.0], [0.0,0.0,1.0]]) #set up the translation matrix translateX = float(coordinates[0]) @@ -180,7 +181,7 @@ class VlsiLayout: #we need to keep track of all transforms in the hierarchy #when we add an element to the xy tree, we apply all transforms from the bottom up - transformPath += [(mRotate,mScale,mTranslate)] + transformPath.append((mRotate,mScale,mTranslate)) if delegateFunction != None: delegateFunction(startingStructureName, transformPath) #starting with a particular structure, we will recursively traverse the tree @@ -190,15 +191,12 @@ class VlsiLayout: #if not, return back to the caller (caller can be this function) for sref in self.structures[startingStructureName].srefs: #here, we are going to modify the sref coordinates based on the parent objects rotation -# if (sref.sName.count("via") == 0): self.traverseTheHierarchy(startingStructureName = sref.sName, delegateFunction = delegateFunction, transformPath = transformPath, rotateAngle = sref.rotateAngle, transFlags = sref.transFlags, coordinates = sref.coordinates) -# else: -# print("WARNING: via encountered, ignoring:", sref.sName) #MUST HANDLE AREFs HERE AS WELL #when we return, drop the last transform from the transformPath del transformPath[-1] @@ -225,12 +223,14 @@ class VlsiLayout: uVector = transform[0] * uVector #rotate vVector = transform[0] * vVector #rotate origin = transform[1] * origin #scale - uVector = transform[1] * uVector #rotate - vVector = transform[1] * vVector #rotate + uVector = transform[1] * uVector #scale + vVector = transform[1] * vVector #scale origin = transform[2] * origin #translate #we don't need to do a translation on the basis vectors - self.xyTree+=[(startingStructureName,origin,uVector,vVector)] #populate the xyTree with each - #structureName and coordinate space + #uVector = transform[2] * uVector #translate + #vVector = transform[2] * vVector #translate + #populate the xyTree with each structureName and coordinate space + self.xyTree.append((startingStructureName,origin,uVector,vVector)) self.traverseTheHierarchy(delegateFunction = addToXyTree) def microns(self,userUnits): @@ -306,19 +306,7 @@ class VlsiLayout: #also combine the "layers in use" list for layerNumber in layoutToAdd.layerNumbersInUse: if layerNumber not in self.layerNumbersInUse: - self.layerNumbersInUse += [layerNumber] - #Also, check if the user units / microns is the same as this Layout - #if (layoutToAdd.units != self.units): - #print("WARNING: VlsiLayout: Units from design to be added do not match target Layout") - - # if debug: print("DEBUG: vlsilayout: Using %d layers") - - # If we can't find the structure, error - #if StructureFound == False: - #print("ERROR: vlsiLayout.addInstance: [%s] Name not found in local structures, "%(nameOfLayout)) - #return #FIXME: remove! - #exit(1) - + self.layerNumbersInUse.append(layerNumber) #add a reference to the new layout structure in this layout's root layoutToAddSref = GdsSref() @@ -326,9 +314,13 @@ class VlsiLayout: layoutToAddSref.coordinates = offsetInLayoutUnits if mirror or rotate: - ########flags = (mirror around x-axis, absolute rotation, absolute magnification) - layoutToAddSref.transFlags = (False,False,False) - #Below angles are angular angles(relative), not absolute + + layoutToAddSref.transFlags = [0,0,0] + # This is NOT the same as the order in the GDS spec! + # It gets written out in gds2writer in the right order though. + # transFlags = (mirror around x-axis, rotation, magnification) + # If magnification or rotation is true, it is the flags are then + # followed by an amount in the record if mirror=="R90": rotate = 90.0 if mirror=="R180": @@ -336,18 +328,21 @@ class VlsiLayout: if mirror=="R270": rotate = 270.0 if rotate: + #layoutToAddSref.transFlags = [0,1,0] layoutToAddSref.rotateAngle = rotate if mirror == "x" or mirror == "MX": - layoutToAddSref.transFlags = (True,False,False) + layoutToAddSref.transFlags = [1,0,0] if mirror == "y" or mirror == "MY": #NOTE: "MY" option will override specified rotate angle - layoutToAddSref.transFlags = (True,False,False) + #layoutToAddSref.transFlags = [1,1,0] + layoutToAddSref.transFlags = [1,0,0] layoutToAddSref.rotateAngle = 180.0 if mirror == "xy" or mirror == "XY": #NOTE: "XY" option will override specified rotate angle - layoutToAddSref.transFlags = (False,False,False) + #layoutToAddSref.transFlags = [0,1,0] + layoutToAddSref.transFlags = [0,0,0] layoutToAddSref.rotateAngle = 180.0 #add the sref to the root structure - self.structures[self.rootStructureName].srefs+=[layoutToAddSref] + self.structures[self.rootStructureName].srefs.append(layoutToAddSref) def addBox(self,layerNumber=0, purposeNumber=None, offsetInMicrons=(0,0), width=1.0, height=1.0,center=False): """ @@ -365,11 +360,7 @@ class VlsiLayout: (offsetInLayoutUnits[0],offsetInLayoutUnits[1]+heightInLayoutUnits), offsetInLayoutUnits] else: - - #is there where gdsmill is halving the coordinates??? - #if you printGDS of temp.gds, the header says 1 user unit = .0005 database units. By default user units = .001. - #something to do with the ieeedouble in gdswriter.py???? - startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2, offsetInLayoutUnits[1]-heightInLayoutUnits/2) #width/2 height/2 + startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2.0, offsetInLayoutUnits[1]-heightInLayoutUnits/2.0) coordinates=[startPoint, (startPoint[0]+widthInLayoutUnits,startPoint[1]), (startPoint[0]+widthInLayoutUnits,startPoint[1]+heightInLayoutUnits), @@ -382,7 +373,7 @@ class VlsiLayout: boundaryToAdd.coordinates = coordinates boundaryToAdd.purposeLayer = purposeNumber #add the sref to the root structure - self.structures[self.rootStructureName].boundaries+=[boundaryToAdd] + self.structures[self.rootStructureName].boundaries.append(boundaryToAdd) def addPath(self, layerNumber=0, purposeNumber = None, coordinates=[(0,0)], width=1.0): """ @@ -394,14 +385,14 @@ class VlsiLayout: for coordinate in coordinates: cX = self.userUnits(coordinate[0]) cY = self.userUnits(coordinate[1]) - layoutUnitCoordinates += [(cX,cY)] + layoutUnitCoordinates.append((cX,cY)) pathToAdd = GdsPath() pathToAdd.drawingLayer=layerNumber pathToAdd.purposeLayer = purposeNumber pathToAdd.pathWidth=widthInLayoutUnits pathToAdd.coordinates=layoutUnitCoordinates #add the sref to the root structure - self.structures[self.rootStructureName].paths+=[pathToAdd] + self.structures[self.rootStructureName].paths.append(pathToAdd) 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])) @@ -410,17 +401,17 @@ class VlsiLayout: textToAdd.purposeLayer = purposeNumber textToAdd.dataType = 0 textToAdd.coordinates = [offsetInLayoutUnits] + textToAdd.transFlags = [0,0,0] if(len(text)%2 == 1): - #pad with a zero text = text + '\x00' textToAdd.textString = text - textToAdd.transFlags = (False,False,True) + textToAdd.transFlags = [0,0,1] textToAdd.magFactor = magnification if rotate: - textToAdd.transFlags = (False,True,True) + textToAdd.transFlags = [0,1,1] textToAdd.rotateAngle = rotate #add the sref to the root structure - self.structures[self.rootStructureName].texts+=[textToAdd] + self.structures[self.rootStructureName].texts.append(textToAdd) def isBounded(self,testPoint,startPoint,endPoint): #these arguments are touples of (x,y) coordinates @@ -532,7 +523,7 @@ class VlsiLayout: #remap coordinates shiftedBoundaryCoordinates = [] for shapeCoordinate in boundary.rotatedCoordinates(rotateAngle): - shiftedBoundaryCoordinates+=[(shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1])] + shiftedBoundaryCoordinates.append((shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1])) joint = self.doShapesIntersect(self.tempCoordinates, shiftedBoundaryCoordinates) if joint: self.tempPassFail = False @@ -545,7 +536,7 @@ class VlsiLayout: #remap coordinates shiftedBoundaryCoordinates = [] for shapeCoordinate in path.equivalentBoundaryCoordinates(rotateAngle): - shiftedBoundaryCoordinates+=[(shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1])] + shiftedBoundaryCoordinates.append((shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1])) joint = self.doShapesIntersect(self.tempCoordinates, shiftedBoundaryCoordinates) if joint: self.tempPassFail = False @@ -568,7 +559,7 @@ class VlsiLayout: self.traverseTheHierarchy(delegateFunction = isThisBlockOk) #if its bad, this global tempPassFail will be false #if true, we can add the block - passFailRecord+=[self.tempPassFail] + passFailRecord.append(self.tempPassFail) print("Percent Complete:"+str(percentDone)) @@ -579,10 +570,11 @@ class VlsiLayout: blockY = (yIndex*effectiveBlock)+offsetInMicrons[1] if passFailRecord[passFailIndex]: self.addBox(layerToFill, (blockX,blockY), width=blockSize, height=blockSize) - passFailIndex+=1 + passFailIndex += 1 print("Done\n\n") def getLayoutBorder(self,borderlayer): + cellSizeMicron=None for boundary in self.structures[self.rootStructureName].boundaries: if boundary.drawingLayer==borderlayer: if self.debug: @@ -614,23 +606,15 @@ class VlsiLayout: 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=Structure[0] - StructureOrigin=[Structure[1][0],Structure[1][1]] - StructureuVector=[Structure[2][0],Structure[2][1],Structure[2][2]] - StructurevVector=[Structure[3][0],Structure[3][1],Structure[3][2]] - #debug.info(debug_level,"Checking Structure: "+str(StructureName)) - #debug.info(debug_level,"-Structure Structure Origin:"+str(StructureOrigin)) - #debug.info(debug_level,"-Structure direction: uVector["+str(StructureuVector)+"]") - #debug.info(debug_level,"-Structure direction: vVector["+str(StructurevVector)+"]") - - for boundary in self.structures[str(StructureName)].boundaries: + 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] thisBoundary=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]] - thisBoundary=self.transformRectangle(thisBoundary,StructureuVector,StructurevVector) - thisBoundary=[thisBoundary[0]+StructureOrigin[0],thisBoundary[1]+StructureOrigin[1], - thisBoundary[2]+StructureOrigin[0],thisBoundary[3]+StructureOrigin[1]] + thisBoundary=self.transformRectangle(thisBoundary,structureuVector,structurevVector) + thisBoundary=[thisBoundary[0]+structureOrigin[0],thisBoundary[1]+structureOrigin[1], + thisBoundary[2]+structureOrigin[0],thisBoundary[3]+structureOrigin[1]] cellBoundary=self.updateBoundary(thisBoundary,cellBoundary) return cellBoundary @@ -722,7 +706,8 @@ class VlsiLayout: def getAllPinShapesByDBLocLayer(self, coordinate, layer): """ Return ALL the enclosing rectangles on the same layer - at the given coordinate. Coordinates should be in DB units. + at the given coordinate. Input coordinates should be in DB units. + Returns user unit shapes. """ pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer) @@ -732,8 +717,7 @@ class VlsiLayout: new_boundaries.append([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, new_boundaries] + return new_boundaries def getPinShapeByLabel(self,label_name): """ @@ -758,7 +742,7 @@ class VlsiLayout: shape_list=[] for label in label_list: (label_coordinate,label_layer)=label - shape_list.append(self.getAllPinShapesByDBLocLayer(label_coordinate, label_layer)) + shape_list.extend(self.getAllPinShapesByDBLocLayer(label_coordinate, label_layer)) return shape_list def getAllPinShapesInStructureList(self,coordinates,layer): @@ -767,9 +751,8 @@ class VlsiLayout: Return all pin shapes. """ boundaries = [] - for TreeUnit in self.xyTree: - boundaries += self.getPinInStructure(coordinates,layer,TreeUnit) + boundaries.extend(self.getPinInStructure(coordinates,layer,TreeUnit)) return boundaries @@ -780,14 +763,60 @@ class VlsiLayout: that the label coordinates are inside. """ - # check if this is a rectangle - structureName=structure[0] - structureOrigin=[structure[1][0],structure[1][1]] - structureuVector=[structure[2][0],structure[2][1],structure[2][2]] - structurevVector=[structure[3][0],structure[3][1],structure[3][2]] - + (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. + """ + boundaries = [] + for TreeUnit in self.xyTree: + #print(TreeUnit[0]) + boundaries.extend(self.getShapesInStructure(layer,TreeUnit)) + + # Remove duplicates without defining a hash + # (could be sped up by creating hash and using list(set()) + new_boundaries = [] + for boundary in boundaries: + if boundary not in new_boundaries: + new_boundaries.append(boundary) + + # Convert to user units + boundaries = [] + for boundary in new_boundaries: + boundaries.append([boundary[0]*self.units[0],boundary[1]*self.units[0], + boundary[2]*self.units[0],boundary[3]*self.units[0]]) + + return boundaries + + + def getShapesInStructure(self,layer,structure): + """ + Go through all the shapes in a structure and return the list of shapes. + """ + + (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. @@ -802,11 +831,10 @@ class VlsiLayout: 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) + boundaries.append(boundaryRect) return boundaries - + def transformRectangle(self,originalRectangle,uVector,vVector): """ Transforms the four coordinates of a rectangle in space @@ -848,6 +876,7 @@ class VlsiLayout: else: return False + def boundaryArea(A): """ Returns boundary area for sorting. diff --git a/compiler/globals.py b/compiler/globals.py index 52b33450..9acfc945 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -283,7 +283,7 @@ def setup_paths(): # Add all of the subdirs to the python path # These subdirs are modules and don't need to be added: characterizer, verify - for subdir in ["gdsMill", "tests", "router", "modules", "base", "pgates"]: + for subdir in ["gdsMill", "tests", "modules", "base", "pgates"]: full_path = "{0}/{1}".format(OPENRAM_HOME,subdir) debug.check(os.path.isdir(full_path), "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir,full_path)) diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py new file mode 100644 index 00000000..3a63c890 --- /dev/null +++ b/compiler/modules/multibank.py @@ -0,0 +1,874 @@ +import sys +from tech import drc, parameter +import debug +import design +import math +from math import log,sqrt,ceil +import contact +from pinv import pinv +from pnand2 import pnand2 +from pnor2 import pnor2 +from vector import vector +from pinvbuf import pinvbuf + +from globals import OPTS + +class multibank(design.design): + """ + Dynamically generated a single bank including bitcell array, + hierarchical_decoder, precharge, (optional column_mux and column decoder), + write driver and sense amplifiers. + This module includes the tristate and bank select logic. + """ + + def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""): + + mod_list = ["tri_gate", "bitcell", "decoder", "ms_flop_array", "wordline_driver", + "bitcell_array", "sense_amp_array", "precharge_array", + "column_mux_array", "write_driver_array", "tri_gate_array", + "dff", "bank_select"] + from importlib import reload + for mod_name in mod_list: + config_mod_name = getattr(OPTS, mod_name) + class_file = reload(__import__(config_mod_name)) + mod_class = getattr(class_file , config_mod_name) + setattr (self, "mod_"+mod_name, mod_class) + + if name == "": + name = "bank_{0}_{1}".format(word_size, num_words) + design.design.__init__(self, name) + debug.info(2, "create sram of size {0} with {1} words".format(word_size,num_words)) + + self.word_size = word_size + self.num_words = num_words + self.words_per_row = words_per_row + self.num_banks = num_banks + + # The local control signals are gated when we have bank select logic, + # so this prefix will be added to all of the input signals to create + # the internal gated signals. + if self.num_banks>1: + self.prefix="gated_" + else: + self.prefix="" + + self.compute_sizes() + self.add_pins() + self.create_modules() + self.add_modules() + self.setup_layout_constraints() + + # FIXME: Move this to the add modules function + self.add_bank_select() + + self.route_layout() + + + # Can remove the following, but it helps for debug! + self.add_lvs_correspondence_points() + + # Remember the bank center for further placement + self.bank_center=self.offset_all_coordinates().scale(-1,-1) + + self.DRC_LVS() + + def add_pins(self): + """ Adding pins for Bank module""" + for i in range(self.word_size): + self.add_pin("DOUT[{0}]".format(i),"OUT") + for i in range(self.word_size): + self.add_pin("BANK_DIN[{0}]".format(i),"IN") + for i in range(self.addr_size): + self.add_pin("A[{0}]".format(i),"INPUT") + + # For more than one bank, we have a bank select and name + # the signals gated_*. + if self.num_banks > 1: + self.add_pin("bank_sel","INPUT") + for pin in ["s_en","w_en","tri_en_bar","tri_en", + "clk_buf_bar","clk_buf"]: + self.add_pin(pin,"INPUT") + self.add_pin("vdd","POWER") + self.add_pin("gnd","GROUND") + + def route_layout(self): + """ Create routing amoung the modules """ + self.route_central_bus() + self.route_precharge_to_bitcell_array() + self.route_col_mux_to_bitcell_array() + self.route_sense_amp_to_col_mux_or_bitcell_array() + #self.route_sense_amp_to_trigate() + #self.route_tri_gate_out() + self.route_sense_amp_out() + self.route_wordline_driver() + self.route_write_driver() + self.route_row_decoder() + self.route_column_address_lines() + self.route_control_lines() + self.add_control_pins() + if self.num_banks > 1: + self.route_bank_select() + + self.route_vdd_gnd() + + def add_modules(self): + """ Add modules. The order should not matter! """ + + # Above the bitcell array + self.add_bitcell_array() + self.add_precharge_array() + + # Below the bitcell array + self.add_column_mux_array() + self.add_sense_amp_array() + self.add_write_driver_array() + # Not needed for single bank + #self.add_tri_gate_array() + + # To the left of the bitcell array + self.add_row_decoder() + self.add_wordline_driver() + self.add_column_decoder() + + + + def compute_sizes(self): + """ Computes the required sizes to create the bank """ + + self.num_cols = int(self.words_per_row*self.word_size) + self.num_rows = int(self.num_words / self.words_per_row) + + self.row_addr_size = int(log(self.num_rows, 2)) + self.col_addr_size = int(log(self.words_per_row, 2)) + self.addr_size = self.col_addr_size + self.row_addr_size + + debug.check(self.num_rows*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.") + debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.") + + # Width for the vdd/gnd rails + self.supply_rail_width = 4*self.m2_width + # FIXME: This spacing should be width dependent... + self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space + + # Number of control lines in the bus + self.num_control_lines = 6 + # The order of the control signals on the control bus: + self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"] + # These will be outputs of the gaters if this is multibank, if not, normal signals. + if self.num_banks > 1: + self.control_signals = ["gated_"+str for str in self.input_control_signals] + else: + self.control_signals = self.input_control_signals + # The central bus is the column address (one hot) and row address (binary) + if self.col_addr_size>0: + self.num_col_addr_lines = 2**self.col_addr_size + else: + self.num_col_addr_lines = 0 + + # The width of this bus is needed to place other modules (e.g. decoder) + # A width on each side too + self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width + + # A space for wells or jogging m2 + self.m2_gap = max(2*drc["pwell_to_nwell"] + drc["well_enclosure_active"], + 2*self.m2_pitch) + + + + def create_modules(self): + """ Create all the modules using the class loader """ + self.tri = self.mod_tri_gate() + self.bitcell = self.mod_bitcell() + + self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, + rows=self.num_rows) + self.add_mod(self.bitcell_array) + + self.precharge_array = self.mod_precharge_array(columns=self.num_cols) + self.add_mod(self.precharge_array) + + if self.col_addr_size > 0: + self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.column_mux_array) + + + self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size, + words_per_row=self.words_per_row) + self.add_mod(self.sense_amp_array) + + self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.write_driver_array) + + self.row_decoder = self.mod_decoder(rows=self.num_rows) + self.add_mod(self.row_decoder) + + self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.tri_gate_array) + + self.wordline_driver = self.mod_wordline_driver(rows=self.num_rows) + self.add_mod(self.wordline_driver) + + self.inv = pinv() + self.add_mod(self.inv) + + if(self.num_banks > 1): + self.bank_select = self.mod_bank_select() + self.add_mod(self.bank_select) + + + def add_bitcell_array(self): + """ Adding Bitcell Array """ + + self.bitcell_array_inst=self.add_inst(name="bitcell_array", + mod=self.bitcell_array, + offset=vector(0,0)) + temp = [] + for i in range(self.num_cols): + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + for j in range(self.num_rows): + temp.append("wl[{0}]".format(j)) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + + def add_precharge_array(self): + """ Adding Precharge """ + + # The wells must be far enough apart + # The enclosure is for the well and the spacing is to the bitcell wells + y_offset = self.bitcell_array.height + self.m2_gap + self.precharge_array_inst=self.add_inst(name="precharge_array", + mod=self.precharge_array, + offset=vector(0,y_offset)) + temp = [] + for i in range(self.num_cols): + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + temp.extend([self.prefix+"clk_buf_bar", "vdd"]) + self.connect_inst(temp) + + def add_column_mux_array(self): + """ Adding Column Mux when words_per_row > 1 . """ + if self.col_addr_size > 0: + self.column_mux_height = self.column_mux_array.height + self.m2_gap + else: + self.column_mux_height = 0 + return + + y_offset = self.column_mux_height + self.col_mux_array_inst=self.add_inst(name="column_mux_array", + mod=self.column_mux_array, + offset=vector(0,y_offset).scale(-1,-1)) + temp = [] + for i in range(self.num_cols): + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + for k in range(self.words_per_row): + temp.append("sel[{0}]".format(k)) + for j in range(self.word_size): + temp.append("bl_out[{0}]".format(j)) + temp.append("br_out[{0}]".format(j)) + temp.append("gnd") + self.connect_inst(temp) + + def add_sense_amp_array(self): + """ Adding Sense amp """ + + y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap + self.sense_amp_array_inst=self.add_inst(name="sense_amp_array", + mod=self.sense_amp_array, + offset=vector(0,y_offset).scale(-1,-1)) + temp = [] + for i in range(self.word_size): + temp.append("sa_out[{0}]".format(i)) + if self.words_per_row == 1: + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + else: + temp.append("bl_out[{0}]".format(i)) + temp.append("br_out[{0}]".format(i)) + + temp.extend([self.prefix+"s_en", "vdd", "gnd"]) + self.connect_inst(temp) + + def add_write_driver_array(self): + """ Adding Write Driver """ + + y_offset = self.sense_amp_array.height + self.column_mux_height \ + + self.m2_gap + self.write_driver_array.height + self.write_driver_array_inst=self.add_inst(name="write_driver_array", + mod=self.write_driver_array, + offset=vector(0,y_offset).scale(-1,-1)) + + temp = [] + for i in range(self.word_size): + temp.append("BANK_DIN[{0}]".format(i)) + for i in range(self.word_size): + if (self.words_per_row == 1): + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + else: + temp.append("bl_out[{0}]".format(i)) + temp.append("br_out[{0}]".format(i)) + temp.extend([self.prefix+"w_en", "vdd", "gnd"]) + self.connect_inst(temp) + + def add_tri_gate_array(self): + """ data tri gate to drive the data bus """ + y_offset = self.sense_amp_array.height+self.column_mux_height \ + + self.m2_gap + self.tri_gate_array.height + self.tri_gate_array_inst=self.add_inst(name="tri_gate_array", + mod=self.tri_gate_array, + offset=vector(0,y_offset).scale(-1,-1)) + + temp = [] + for i in range(self.word_size): + temp.append("sa_out[{0}]".format(i)) + for i in range(self.word_size): + temp.append("DOUT[{0}]".format(i)) + temp.extend([self.prefix+"tri_en", self.prefix+"tri_en_bar", "vdd", "gnd"]) + self.connect_inst(temp) + + def add_row_decoder(self): + """ Add the hierarchical row decoder """ + + + # The address and control bus will be in between decoder and the main memory array + # This bus will route address bits to the decoder input and column mux inputs. + # The wires are actually routed after we placed the stuff on both sides. + # The predecoder is below the x-axis and the main decoder is above the x-axis + # The address flop and decoder are aligned in the x coord. + + x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) + self.row_decoder_inst=self.add_inst(name="row_decoder", + mod=self.row_decoder, + offset=vector(x_offset,0)) + + temp = [] + for i in range(self.row_addr_size): + temp.append("A[{0}]".format(i+self.col_addr_size)) + for j in range(self.num_rows): + temp.append("dec_out[{0}]".format(j)) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + def add_wordline_driver(self): + """ Wordline Driver """ + + # The wordline driver is placed to the right of the main decoder width. + x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch + self.wordline_driver_inst=self.add_inst(name="wordline_driver", + mod=self.wordline_driver, + offset=vector(x_offset,0)) + + temp = [] + for i in range(self.num_rows): + temp.append("dec_out[{0}]".format(i)) + for i in range(self.num_rows): + temp.append("wl[{0}]".format(i)) + temp.append(self.prefix+"clk_buf") + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) + + + def add_column_decoder_module(self): + """ + Create a 2:4 or 3:8 column address decoder. + """ + # Place the col decoder right aligned with row decoder + x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) + y_off = -(self.col_decoder.height + 2*drc["well_to_well"]) + self.col_decoder_inst=self.add_inst(name="col_address_decoder", + mod=self.col_decoder, + offset=vector(x_off,y_off)) + + temp = [] + for i in range(self.col_addr_size): + temp.append("A[{0}]".format(i)) + for j in range(self.num_col_addr_lines): + temp.append("sel[{0}]".format(j)) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + def add_column_decoder(self): + """ + Create a decoder to decode column select lines. This could be an inverter/buffer for 1:2, + 2:4 decoder, or 3:8 decoder. + """ + if self.col_addr_size == 0: + return + elif self.col_addr_size == 1: + self.col_decoder = pinvbuf(height=self.mod_dff.height) + self.add_mod(self.col_decoder) + elif self.col_addr_size == 2: + self.col_decoder = self.row_decoder.pre2_4 + elif self.col_addr_size == 3: + self.col_decoder = self.row_decoder.pre3_8 + else: + # No error checking before? + debug.error("Invalid column decoder?",-1) + + self.add_column_decoder_module() + + + def add_bank_select(self): + """ Instantiate the bank select logic. """ + + if not self.num_banks > 1: + return + + x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) + if self.col_addr_size > 0: + y_off = min(self.col_decoder_inst.by(), self.col_mux_array_inst.by()) + else: + y_off = self.row_decoder_inst.by() + y_off -= (self.bank_select.height + drc["well_to_well"]) + self.bank_select_pos = vector(x_off,y_off) + self.bank_select_inst = self.add_inst(name="bank_select", + mod=self.bank_select, + offset=self.bank_select_pos) + + temp = [] + temp.extend(self.input_control_signals) + temp.append("bank_sel") + temp.extend(self.control_signals) + temp.extend(["vdd", "gnd"]) + self.connect_inst(temp) + + def route_vdd_gnd(self): + """ Propagate all vdd/gnd pins up to this level for all modules """ + + # These are the instances that every bank has + top_instances = [self.bitcell_array_inst, + self.precharge_array_inst, + self.sense_amp_array_inst, + self.write_driver_array_inst, +# self.tri_gate_array_inst, + self.row_decoder_inst, + self.wordline_driver_inst] + # Add these if we use the part... + if self.col_addr_size > 0: + top_instances.append(self.col_decoder_inst) + top_instances.append(self.col_mux_array_inst) + + if self.num_banks > 1: + top_instances.append(self.bank_select_inst) + + + for inst in top_instances: + # Column mux has no vdd + if self.col_addr_size==0 or (self.col_addr_size>0 and inst != self.col_mux_array_inst): + self.copy_layout_pin(inst, "vdd") + # Precharge has no gnd + if inst != self.precharge_array_inst: + self.copy_layout_pin(inst, "gnd") + + def route_bank_select(self): + """ Route the bank select logic. """ + for input_name in self.input_control_signals+["bank_sel"]: + self.copy_layout_pin(self.bank_select_inst, input_name) + + for gated_name in self.control_signals: + # Connect the inverter output to the central bus + out_pos = self.bank_select_inst.get_pin(gated_name).rc() + bus_pos = vector(self.bus_xoffset[gated_name], out_pos.y) + self.add_path("metal3",[out_pos, bus_pos]) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=bus_pos, + rotate=90) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=out_pos, + rotate=90) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=out_pos, + rotate=90) + + + def setup_layout_constraints(self): + """ After the modules are instantiated, find the dimensions for the + control bus, power ring, etc. """ + + #The minimum point is either the bottom of the address flops, + #the column decoder (if there is one) or the tristate output + #driver. + # Leave room for the output below the tri gate. + #tri_gate_min_y_offset = self.tri_gate_array_inst.by() - 3*self.m2_pitch + write_driver_min_y_offset = self.write_driver_array_inst.by() - 3*self.m2_pitch + row_decoder_min_y_offset = self.row_decoder_inst.by() + if self.col_addr_size > 0: + col_decoder_min_y_offset = self.col_decoder_inst.by() + else: + col_decoder_min_y_offset = row_decoder_min_y_offset + + if self.num_banks>1: + # The control gating logic is below the decoder + # Min of the control gating logic and tri gate. + self.min_y_offset = min(col_decoder_min_y_offset - self.bank_select.height, write_driver_min_y_offset) + else: + # Just the min of the decoder logic logic and tri gate. + self.min_y_offset = min(col_decoder_min_y_offset, write_driver_min_y_offset) + + # The max point is always the top of the precharge bitlines + # Add a vdd and gnd power rail above the array + self.max_y_offset = self.precharge_array_inst.uy() + 3*self.m1_width + self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width + self.min_x_offset = self.row_decoder_inst.lx() + + # # Create the core bbox for the power rings + ur = vector(self.max_x_offset, self.max_y_offset) + ll = vector(self.min_x_offset, self.min_y_offset) + self.core_bbox = [ll, ur] + + self.height = ur.y - ll.y + self.width = ur.x - ll.x + + + + def route_central_bus(self): + """ Create the address, supply, and control signal central bus lines. """ + + # Overall central bus width. It includes all the column mux lines, + # and control lines. + # The bank is at (0,0), so this is to the left of the y-axis. + # 2 pitches on the right for vias/jogs to access the inputs + control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0) + control_bus_length = self.bitcell_array_inst.uy() + self.bus_xoffset = self.create_vertical_bus(layer="metal2", + pitch=self.m2_pitch, + offset=control_bus_offset, + names=self.control_signals, + length=control_bus_length) + + + + def route_precharge_to_bitcell_array(self): + """ Routing of BL and BR between pre-charge and bitcell array """ + + for i in range(self.num_cols): + precharge_bl = self.precharge_array_inst.get_pin("bl[{}]".format(i)).bc() + precharge_br = self.precharge_array_inst.get_pin("br[{}]".format(i)).bc() + bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).uc() + bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).uc() + + yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) + self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), + vector(bitcell_bl.x,yoffset), bitcell_bl]) + self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset), + vector(bitcell_br.x,yoffset), bitcell_br]) + + + def route_col_mux_to_bitcell_array(self): + """ Routing of BL and BR between col mux and bitcell array """ + + # Only do this if we have a column mux! + if self.col_addr_size==0: + return + + for i in range(self.num_cols): + col_mux_bl = self.col_mux_array_inst.get_pin("bl[{}]".format(i)).uc() + col_mux_br = self.col_mux_array_inst.get_pin("br[{}]".format(i)).uc() + bitcell_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc() + bitcell_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc() + + yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y) + self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset), + vector(bitcell_bl.x,yoffset), bitcell_bl]) + self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset), + vector(bitcell_br.x,yoffset), bitcell_br]) + + def route_sense_amp_to_col_mux_or_bitcell_array(self): + """ Routing of BL and BR between sense_amp and column mux or bitcell array """ + + for i in range(self.word_size): + sense_amp_bl = self.sense_amp_array_inst.get_pin("bl[{}]".format(i)).uc() + sense_amp_br = self.sense_amp_array_inst.get_pin("br[{}]".format(i)).uc() + + if self.col_addr_size>0: + # Sense amp is connected to the col mux + connect_bl = self.col_mux_array_inst.get_pin("bl_out[{}]".format(i)).bc() + connect_br = self.col_mux_array_inst.get_pin("br_out[{}]".format(i)).bc() + else: + # Sense amp is directly connected to the bitcell array + connect_bl = self.bitcell_array_inst.get_pin("bl[{}]".format(i)).bc() + connect_br = self.bitcell_array_inst.get_pin("br[{}]".format(i)).bc() + + + yoffset = 0.5*(sense_amp_bl.y+connect_bl.y) + self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset), + vector(connect_bl.x,yoffset), connect_bl]) + self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset), + vector(connect_br.x,yoffset), connect_br]) + + def route_sense_amp_to_trigate(self): + """ Routing of sense amp output to tri_gate input """ + + for i in range(self.word_size): + # Connection of data_out of sense amp to data_in + tri_gate_in = self.tri_gate_array_inst.get_pin("in[{}]".format(i)).lc() + sa_data_out = self.sense_amp_array_inst.get_pin("data[{}]".format(i)).bc() + + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=tri_gate_in) + self.add_via_center(layers=("metal2", "via2", "metal3"), + offset=sa_data_out) + self.add_path("metal3",[sa_data_out,tri_gate_in]) + + def route_sense_amp_out(self): + """ Add pins for the sense amp output """ + for i in range(self.word_size): + data_pin = self.sense_amp_array_inst.get_pin("data[{}]".format(i)) + self.add_layout_pin_rect_center(text="DOUT[{}]".format(i), + layer=data_pin.layer, + offset=data_pin.center(), + height=data_pin.height(), + width=data_pin.width()), + + def route_tri_gate_out(self): + """ Metal 3 routing of tri_gate output data """ + for i in range(self.word_size): + data_pin = self.tri_gate_array_inst.get_pin("out[{}]".format(i)) + self.add_layout_pin_rect_center(text="DOUT[{}]".format(i), + layer=data_pin.layer, + offset=data_pin.center(), + height=data_pin.height(), + width=data_pin.width()), + + + def route_row_decoder(self): + """ Routes the row decoder inputs and supplies """ + + # Create inputs for the row address lines + for i in range(self.row_addr_size): + addr_idx = i + self.col_addr_size + decoder_name = "A[{}]".format(i) + addr_name = "A[{}]".format(addr_idx) + self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name) + + + def route_write_driver(self): + """ Connecting write driver """ + + for i in range(self.word_size): + data_name = "data[{}]".format(i) + din_name = "BANK_DIN[{}]".format(i) + self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) + + + + def route_wordline_driver(self): + """ Connecting Wordline driver output to Bitcell WL connection """ + + # we don't care about bends after connecting to the input pin, so let the path code decide. + for i in range(self.num_rows): + # The pre/post is to access the pin from "outside" the cell to avoid DRCs + decoder_out_pos = self.row_decoder_inst.get_pin("decode[{}]".format(i)).rc() + driver_in_pos = self.wordline_driver_inst.get_pin("in[{}]".format(i)).lc() + mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) + mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) + self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos]) + + # The mid guarantees we exit the input cell to the right. + driver_wl_pos = self.wordline_driver_inst.get_pin("wl[{}]".format(i)).rc() + bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl[{}]".format(i)).lc() + mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) + mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) + self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) + + + + def route_column_address_lines(self): + """ Connecting the select lines of column mux to the address bus """ + if not self.col_addr_size>0: + return + + + + if self.col_addr_size == 1: + + # Connect to sel[0] and sel[1] + decode_names = ["Zb", "Z"] + + # The Address LSB + self.copy_layout_pin(self.col_decoder_inst, "A", "A[0]") + + elif self.col_addr_size > 1: + decode_names = [] + for i in range(self.num_col_addr_lines): + decode_names.append("out[{}]".format(i)) + + for i in range(self.col_addr_size): + decoder_name = "in[{}]".format(i) + addr_name = "A[{}]".format(i) + self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name) + + + # This will do a quick "river route" on two layers. + # When above the top select line it will offset "inward" again to prevent conflicts. + # This could be done on a single layer, but we follow preferred direction rules for later routing. + top_y_offset = self.col_mux_array_inst.get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy() + for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)): + mux_name = "sel[{}]".format(i) + mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc() + + decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center() + + # To get to the edge of the decoder and one track out + delta_offset = self.col_decoder_inst.rx() - decode_out_pos.x + self.m2_pitch + if decode_out_pos.y > top_y_offset: + mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y) + else: + mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y) + mid2_pos = vector(mid1_pos.x,mux_addr_pos.y) + #self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) + self.add_path("metal1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos]) + + + + + + def add_lvs_correspondence_points(self): + """ This adds some points for easier debugging if LVS goes wrong. + These should probably be turned off by default though, since extraction + will show these as ports in the extracted netlist. + """ + # Add the wordline names + for i in range(self.num_rows): + wl_name = "wl[{}]".format(i) + wl_pin = self.bitcell_array_inst.get_pin(wl_name) + self.add_label(text=wl_name, + layer="metal1", + offset=wl_pin.center()) + + # Add the bitline names + for i in range(self.num_cols): + bl_name = "bl[{}]".format(i) + br_name = "br[{}]".format(i) + bl_pin = self.bitcell_array_inst.get_pin(bl_name) + br_pin = self.bitcell_array_inst.get_pin(br_name) + self.add_label(text=bl_name, + layer="metal2", + offset=bl_pin.center()) + self.add_label(text=br_name, + layer="metal2", + offset=br_pin.center()) + + # # Add the data output names to the sense amp output + # for i in range(self.word_size): + # data_name = "data[{}]".format(i) + # data_pin = self.sense_amp_array_inst.get_pin(data_name) + # self.add_label(text="sa_out[{}]".format(i), + # layer="metal2", + # offset=data_pin.center()) + + # Add labels on the decoder + for i in range(self.word_size): + data_name = "dec_out[{}]".format(i) + pin_name = "in[{}]".format(i) + data_pin = self.wordline_driver_inst.get_pin(pin_name) + self.add_label(text=data_name, + layer="metal1", + offset=data_pin.center()) + + + def route_control_lines(self): + """ Route the control lines of the entire bank """ + + # Make a list of tuples that we will connect. + # From control signal to the module pin + # Connection from the central bus to the main control block crosses + # pre-decoder and this connection is in metal3 + connection = [] + #connection.append((self.prefix+"tri_en_bar", self.tri_gate_array_inst.get_pin("en_bar").lc())) + #connection.append((self.prefix+"tri_en", self.tri_gate_array_inst.get_pin("en").lc())) + connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst.get_pin("en").lc())) + connection.append((self.prefix+"w_en", self.write_driver_array_inst.get_pin("en").lc())) + connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc())) + + for (control_signal, pin_pos) in connection: + control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) + self.add_path("metal1", [control_pos, pin_pos]) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=control_pos, + rotate=90) + + # clk to wordline_driver + control_signal = self.prefix+"clk_buf" + pin_pos = self.wordline_driver_inst.get_pin("en").uc() + mid_pos = pin_pos + vector(0,self.m1_pitch) + control_x_offset = self.bus_xoffset[control_signal].x + control_pos = vector(control_x_offset + self.m1_width, mid_pos.y) + self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) + control_via_pos = vector(control_x_offset, mid_pos.y) + self.add_via_center(layers=("metal1", "via1", "metal2"), + offset=control_via_pos, + rotate=90) + + def add_control_pins(self): + """ Add the control signal input pins """ + + for ctrl in self.control_signals: + # xoffsets are the center of the rail + x_offset = self.bus_xoffset[ctrl].x - 0.5*self.m2_width + if self.num_banks > 1: + # it's not an input pin if we have multiple banks + self.add_label_pin(text=ctrl, + layer="metal2", + offset=vector(x_offset, self.min_y_offset), + width=self.m2_width, + height=self.max_y_offset-self.min_y_offset) + else: + self.add_layout_pin(text=ctrl, + layer="metal2", + offset=vector(x_offset, self.min_y_offset), + width=self.m2_width, + height=self.max_y_offset-self.min_y_offset) + + + def connect_rail_from_right(self,inst, pin, rail): + """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ + in_pin = inst.get_pin(pin).lc() + rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y) + self.add_wire(("metal3","via2","metal2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) + # Bring it up to M2 for M2/M3 routing + self.add_via(layers=("metal1","via1","metal2"), + offset=in_pin + contact.m1m2.offset, + rotate=90) + self.add_via(layers=("metal2","via2","metal3"), + offset=in_pin + self.m2m3_via_offset, + rotate=90) + + + def connect_rail_from_left(self,inst, pin, rail): + """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """ + in_pin = inst.get_pin(pin).rc() + rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y) + self.add_wire(("metal3","via2","metal2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) + self.add_via(layers=("metal1","via1","metal2"), + offset=in_pin + contact.m1m2.offset, + rotate=90) + self.add_via(layers=("metal2","via2","metal3"), + offset=in_pin + self.m2m3_via_offset, + rotate=90) + + def analytical_delay(self, slew, load): + """ return analytical delay of the bank""" + decoder_delay = self.row_decoder.analytical_delay(slew, self.wordline_driver.input_load()) + + word_driver_delay = self.wordline_driver.analytical_delay(decoder_delay.slew, self.bitcell_array.input_load()) + + bitcell_array_delay = self.bitcell_array.analytical_delay(word_driver_delay.slew) + + bl_t_data_out_delay = self.sense_amp_array.analytical_delay(bitcell_array_delay.slew, + self.bitcell_array.output_load()) + # output load of bitcell_array is set to be only small part of bl for sense amp. + + data_t_DATA_delay = self.tri_gate_array.analytical_delay(bl_t_data_out_delay.slew, load) + + result = decoder_delay + word_driver_delay + bitcell_array_delay + bl_t_data_out_delay + data_t_DATA_delay + return result + diff --git a/compiler/modules/replica_bitline.py b/compiler/modules/replica_bitline.py index ee3e89d4..b15f4515 100644 --- a/compiler/modules/replica_bitline.py +++ b/compiler/modules/replica_bitline.py @@ -192,7 +192,7 @@ class replica_bitline(design.design): # Replica bitcell needs to be routed up to M3 pin=self.rbc_inst.get_pin("vdd") # Don't rotate this via to vit in FreePDK45 - self.add_power_pin("vdd", pin.center(), False) + self.add_power_pin("vdd", pin.center(), rotate=0) for pin in self.rbc_inst.get_pins("gnd"): self.add_power_pin("gnd", pin.center()) diff --git a/compiler/router/astar_grid.py b/compiler/router/astar_grid.py new file mode 100644 index 00000000..65d9e245 --- /dev/null +++ b/compiler/router/astar_grid.py @@ -0,0 +1,250 @@ +from itertools import tee +import debug +from vector3d import vector3d +import grid +from heapq import heappush,heappop + +class astar_grid(grid.grid): + """ + Expand the two layer grid to include A* search functions for a source and target. + """ + + def __init__(self): + """ Create a routing map of width x height cells and 2 in the z-axis. """ + grid.grid.__init__(self) + + # list of the source/target grid coordinates + self.source = [] + self.target = [] + + # priority queue for the maze routing + self.q = [] + + def set_source(self,n): + self.add_map(n) + self.map[n].source=True + self.source.append(n) + + def set_target(self,n): + self.add_map(n) + self.map[n].target=True + self.target.append(n) + + + def add_source(self,track_list): + debug.info(2,"Adding source list={0}".format(str(track_list))) + for n in track_list: + if not self.is_blocked(n): + debug.info(3,"Adding source ={0}".format(str(n))) + self.set_source(n) + + def add_target(self,track_list): + debug.info(2,"Adding target list={0}".format(str(track_list))) + for n in track_list: + if not self.is_blocked(n): + self.set_target(n) + + def is_target(self,point): + """ + Point is in the target set, so we are done. + """ + return point in self.target + + def reinit(self): + """ Reinitialize everything for a new route. """ + + # Reset all the cells in the map + for p in self.map.values(): + p.reset() + + # clear source and target pins + self.source=[] + self.target=[] + + # Clear the queue + while len(self.q)>0: + heappop(self.q) + self.counter = 0 + + def init_queue(self): + """ + Populate the queue with all the source pins with cost + to the target. Each item is a path of the grid cells. + We will use an A* search, so this cost must be pessimistic. + Cost so far will be the length of the path. + """ + debug.info(4,"Initializing queue.") + + # uniquify the source (and target while we are at it) + self.source = list(set(self.source)) + self.target = list(set(self.target)) + + # Counter is used to not require data comparison in Python 3.x + # Items will be returned in order they are added during cost ties + self.counter = 0 + for s in self.source: + cost = self.cost_to_target(s) + debug.info(1,"Init: cost=" + str(cost) + " " + str([s])) + heappush(self.q,(cost,self.counter,[s])) + self.counter+=1 + + + def route(self,detour_scale): + """ + This does the A* maze routing with preferred direction routing. + """ + + # We set a cost bound of the HPWL for run-time. This can be + # over-ridden if the route fails due to pruning a feasible solution. + cost_bound = detour_scale*self.cost_to_target(self.source[0])*self.PREFERRED_COST + + # Make sure the queue is empty if we run another route + while len(self.q)>0: + heappop(self.q) + + # Put the source items into the queue + self.init_queue() + cheapest_path = None + cheapest_cost = None + + # Keep expanding and adding to the priority queue until we are done + while len(self.q)>0: + # should we keep the path in the queue as well or just the final node? + (cost,count,path) = heappop(self.q) + debug.info(2,"Queue size: size=" + str(len(self.q)) + " " + str(cost)) + debug.info(3,"Expanding: cost=" + str(cost) + " " + str(path)) + + # expand the last element + neighbors = self.expand_dirs(path) + debug.info(3,"Neighbors: " + str(neighbors)) + + for n in neighbors: + # node is added to the map by the expand routine + newpath = path + [n] + # check if we hit the target and are done + if self.is_target(n): + return (newpath,self.cost(newpath)) + elif not self.map[n].visited: + # current path cost + predicted cost + current_cost = self.cost(newpath) + target_cost = self.cost_to_target(n) + predicted_cost = current_cost + target_cost + # only add the cost if it is less than our bound + if (predicted_cost < cost_bound): + if (self.map[n].min_cost==-1 or current_cost=0 and not self.is_blocked(down) and not down in path: + neighbors.append(down) + + return neighbors + + + def hpwl(self, src, dest): + """ + Return half perimeter wire length from point to another. + Either point can have positive or negative coordinates. + Include the via penalty if there is one. + """ + hpwl = max(abs(src.x-dest.x),abs(dest.x-src.x)) + hpwl += max(abs(src.y-dest.y),abs(dest.y-src.y)) + hpwl += max(abs(src.z-dest.z),abs(dest.z-src.z)) + if src.x!=dest.x or src.y!=dest.y: + hpwl += self.VIA_COST + return hpwl + + def cost_to_target(self,source): + """ + Find the cheapest HPWL distance to any target point ignoring + blockages for A* search. + """ + cost = self.hpwl(source,self.target[0]) + for t in self.target: + cost = min(self.hpwl(source,t),cost) + return cost + + + def cost(self,path): + """ + The cost of the path is the length plus a penalty for the number + of vias. We assume that non-preferred direction is penalized. + """ + + # Ignore the source pin layer change, FIXME? + def pairwise(iterable): + "s -> (s0,s1), (s1,s2), (s2, s3), ..." + a, b = tee(iterable) + next(b, None) + return zip(a, b) + + + plist = pairwise(path) + cost = 0 + for p0,p1 in plist: + if p0.z != p1.z: # via + cost += self.VIA_COST + elif p0.x != p1.x: # horizontal + cost += self.NONPREFERRED_COST if (p0.z == 1) else self.PREFERRED_COST + elif p0.y != p1.y: # vertical + cost += self.NONPREFERRED_COST if (p0.z == 0) else self.PREFERRED_COST + else: + debug.error("Non-changing direction!") + + return cost + + def get_inertia(self,p0,p1): + """ + Sets the direction based on the previous direction we came from. + """ + # direction (index) of movement + if p0.x==p1.x: + return 1 + elif p0.y==p1.y: + return 0 + else: + # z direction + return 2 + + + diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 050826e8..5f60da4e 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -6,20 +6,14 @@ from vector3d import vector3d from cell import cell import os -try: - import Queue as Q # ver. < 3.0 -except ImportError: - import queue as Q - - class grid: - """A two layer routing map. Each cell can be blocked in the vertical + """ + A two layer routing map. Each cell can be blocked in the vertical or horizontal layer. - """ def __init__(self): - """ Create a routing map of width x height cells and 2 in the z-axis. """ + """ Initialize the map and define the costs. """ # costs are relative to a unit grid # non-preferred cost allows an off-direction jog of 1 grid @@ -27,17 +21,10 @@ class grid: self.VIA_COST = 2 self.NONPREFERRED_COST = 4 self.PREFERRED_COST = 1 - - # list of the source/target grid coordinates - self.source = [] - self.target = [] # let's leave the map sparse, cells are created on demand to reduce memory self.map={} - # priority queue for the maze routing - self.q = Q.PriorityQueue() - def set_blocked(self,n): self.add_map(n) self.map[n].blocked=True @@ -46,168 +33,24 @@ class grid: self.add_map(n) return self.map[n].blocked - def set_source(self,n): - self.add_map(n) - self.map[n].source=True - self.source.append(n) - - def set_target(self,n): - self.add_map(n) - self.map[n].target=True - self.target.append(n) - - def reinit(self): - """ Reinitialize everything for a new route. """ - - self.reset_cells() - - # clear source and target pins - self.source=[] - self.target=[] - - # clear the queue - while (not self.q.empty()): - self.q.get(False) - - def add_blockage_shape(self,ll,ur,z): debug.info(3,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) + + if ll[0]<42 and ll[0]>38 and ll[1]<3 and ll[1]>0: + debug.info(0,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) + + block_list = [] for x in range(int(ll[0]),int(ur[0])+1): for y in range(int(ll[1]),int(ur[1])+1): - n = vector3d(x,y,z) - self.set_blocked(n) + block_list.append(vector3d(x,y,z)) + + self.add_blockage(block_list) def add_blockage(self,block_list): - debug.info(3,"Adding blockage list={0}".format(str(block_list))) + debug.info(2,"Adding blockage list={0}".format(str(block_list))) for n in block_list: self.set_blocked(n) - def add_source(self,track_list): - debug.info(3,"Adding source list={0}".format(str(track_list))) - for n in track_list: - if not self.is_blocked(n): - self.set_source(n) - - def add_target(self,track_list): - debug.info(3,"Adding target list={0}".format(str(track_list))) - for n in track_list: - if not self.is_blocked(n): - self.set_target(n) - - def reset_cells(self): - """ - Reset the path and costs for all the grid cells. - """ - for p in self.map.values(): - p.reset() - - - def add_path(self,path): - """ - Mark the path in the routing grid for visualization - """ - self.path=path - for p in path: - self.map[p].path=True - - def route(self,detour_scale): - """ - This does the A* maze routing with preferred direction routing. - """ - - # We set a cost bound of the HPWL for run-time. This can be - # over-ridden if the route fails due to pruning a feasible solution. - cost_bound = detour_scale*self.cost_to_target(self.source[0])*self.PREFERRED_COST - - # Make sure the queue is empty if we run another route - while not self.q.empty(): - self.q.get() - - # Put the source items into the queue - self.init_queue() - cheapest_path = None - cheapest_cost = None - - # Keep expanding and adding to the priority queue until we are done - while not self.q.empty(): - # should we keep the path in the queue as well or just the final node? - (cost,path) = self.q.get() - debug.info(2,"Queue size: size=" + str(self.q.qsize()) + " " + str(cost)) - debug.info(3,"Expanding: cost=" + str(cost) + " " + str(path)) - - # expand the last element - neighbors = self.expand_dirs(path) - debug.info(3,"Neighbors: " + str(neighbors)) - - for n in neighbors: - # node is added to the map by the expand routine - newpath = path + [n] - # check if we hit the target and are done - if self.is_target(n): - return (newpath,self.cost(newpath)) - elif not self.map[n].visited: - # current path cost + predicted cost - current_cost = self.cost(newpath) - target_cost = self.cost_to_target(n) - predicted_cost = current_cost + target_cost - # only add the cost if it is less than our bound - if (predicted_cost < cost_bound): - if (self.map[n].min_cost==-1 or current_cost=0 and not self.is_blocked(down) and not down in path: - neighbors.append(down) - - - - return neighbors - def add_map(self,p): """ Add a point to the map if it doesn't exist. @@ -215,47 +58,13 @@ class grid: if p not in self.map.keys(): self.map[p]=cell() - def init_queue(self): - """ - Populate the queue with all the source pins with cost - to the target. Each item is a path of the grid cells. - We will use an A* search, so this cost must be pessimistic. - Cost so far will be the length of the path. - """ - debug.info(4,"Initializing queue.") - - # uniquify the source (and target while we are at it) - self.source = list(set(self.source)) - self.target = list(set(self.target)) - - for s in self.source: - cost = self.cost_to_target(s) - debug.info(4,"Init: cost=" + str(cost) + " " + str([s])) - self.q.put((cost,[s])) - - def hpwl(self, src, dest): + def add_path(self,path): """ - Return half perimeter wire length from point to another. - Either point can have positive or negative coordinates. - Include the via penalty if there is one. + Mark the path in the routing grid for visualization """ - hpwl = max(abs(src.x-dest.x),abs(dest.x-src.x)) - hpwl += max(abs(src.y-dest.y),abs(dest.y-src.y)) - hpwl += max(abs(src.z-dest.z),abs(dest.z-src.z)) - if src.x!=dest.x or src.y!=dest.y: - hpwl += self.VIA_COST - return hpwl - - def cost_to_target(self,source): - """ - Find the cheapest HPWL distance to any target point ignoring - blockages for A* search. - """ - cost = self.hpwl(source,self.target[0]) - for t in self.target: - cost = min(self.hpwl(source,t),cost) - return cost - + self.path=path + for p in path: + self.map[p].path=True def cost(self,path): """ @@ -285,15 +94,6 @@ class grid: return cost - def get_inertia(self,p0,p1): - """ - Sets the direction based on the previous direction we came from. - """ - # direction (index) of movement - if p0.x==p1.x: - return 1 - elif p0.y==p1.y: - return 0 - else: - # z direction - return 2 + + + diff --git a/compiler/router/regress.sh b/compiler/router/regress.sh deleted file mode 100755 index 904ef9d8..00000000 --- a/compiler/router/regress.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -python tests/regress.py -t freepdk45 -python tests/regress.py -t scn3me_subm diff --git a/compiler/router/router.py b/compiler/router/router.py index 43e5af90..bb784b3c 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -3,34 +3,41 @@ import tech from contact import contact import math import debug -import grid +from pin_layout import pin_layout from vector import vector from vector3d import vector3d from globals import OPTS class router: - """A router class to read an obstruction map from a gds and plan a + """ + A router class to read an obstruction map from a gds and plan a route on a given layer. This is limited to two layer routes. + It populates blockages on a grid class. """ - def __init__(self, gds_name): - """Use the gds file for the blockages with the top module topName and + def __init__(self, gds_name=None, module=None): + """Use the gds file or the cell for the blockages with the top module topName and layers for the layers to route on """ - # Load the gds file and read in all the shapes self.gds_name = gds_name + self.module = module + debug.check(not (gds_name and module), "Specify only a GDS file or module") + + # If we specified a module instead, write it out to read the gds + # This isn't efficient, but easy for now + if module: + gds_name = OPTS.openram_temp+"temp.gds" + module.gds_write(gds_name) + + # Load the gds file and read in all the shapes self.layout = gdsMill.VlsiLayout(units=tech.GDS["unit"]) self.reader = gdsMill.Gds2reader(self.layout) self.reader.loadFromFile(gds_name) self.top_name = self.layout.rootStructureName - self.source_pin_shapes = [] - self.source_pin_zindex = None - self.target_pin_shapes = [] - self.target_pin_zindex = None - # the list of all blockage shapes - self.blockages = [] - # all thepaths we've routed so far (to supplement the blockages) + self.pins = {} + self.blockages=[] + # all the paths we've routed so far (to supplement the blockages) self.paths = [] # The boundary will determine the limits to the size of the routing grid @@ -76,17 +83,6 @@ class router: - def create_routing_grid(self): - """ - Create a routing grid that spans given area. Wires cannot exist outside region. - """ - # We will add a halo around the boundary - # of this many tracks - size = self.ur - self.ll - debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) - - self.rg = grid.grid() - def find_pin(self,pin): """ @@ -94,20 +90,18 @@ class router: Pin can either be a label or a location,layer pair: [[x,y],layer]. """ - if type(pin)==str: - (pin_name,pin_layer,pin_shapes) = self.layout.getAllPinShapesByLabel(str(pin)) - else: - (pin_name,pin_layer,pin_shapes) = self.layout.getAllPinShapesByLocLayer(pin[0],pin[1]) + label_list=self.layout.getPinShapeByLabel(str(pin)) + pin_list = [] + for label in label_list: + (name,layer,boundary)=label + rect = [vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] + # this is a list because other cells/designs may have must-connect pins + pin_list.append(pin_layout(pin, rect, layer)) + + debug.check(len(pin_list)>0,"Did not find any pin shapes for {0}.".format(str(pin))) + + return pin_list - new_pin_shapes = [] - for pin_shape in pin_shapes: - debug.info(2,"Find pin {0} layer {1} shape {2}".format(pin_name,str(pin_layer),str(pin_shape))) - # repack the shape as a pair of vectors rather than four values - new_pin_shapes.append([vector(pin_shape[0],pin_shape[1]),vector(pin_shape[2],pin_shape[3])]) - - debug.check(len(new_pin_shapes)>0,"Did not find any pin shapes for {0}.".format(str(pin))) - - return (pin_layer,new_pin_shapes) def find_blockages(self): """ @@ -115,9 +109,9 @@ class router: This doesn't consider whether the obstacles will be pins or not. They get reset later if they are not actually a blockage. """ - for layer in self.layers: - self.get_blockages(self.top_name) - + #for layer in [self.vert_layer_number,self.horiz_layer_number]: + # self.get_blockages(layer) + self.get_blockages(self.horiz_layer_number) def clear_pins(self): """ @@ -126,187 +120,12 @@ class router: Convert the routed path to blockages. Keep the other blockages unchanged. """ - self.source_pin = None - self.source_pin_shapes = [] - self.source_pin_zindex = None - self.target_pin = None - self.target_pin_shapes = [] - self.target_pin_zindex = None + self.pins = {} # DO NOT clear the blockages as these don't change self.rg.reinit() - def route(self, cell, layers, src, dest, detour_scale=2): - """ - Route a single source-destination net and return - the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route. - This is used to speed up the routing when there is not much detouring needed. - """ - self.cell = cell - - # Clear the pins if we have previously routed - if (hasattr(self,'rg')): - self.clear_pins() - else: - # Set up layers and track sizes - self.set_layers(layers) - # Creat a routing grid over the entire area - # FIXME: This could be created only over the routing region, - # but this is simplest for now. - self.create_routing_grid() - # This will get all shapes as blockages - self.find_blockages() - - # Get the pin shapes - self.get_source(src) - self.get_target(dest) - - # Now add the blockages (all shapes except the src/tgt pins) - self.add_blockages() - # Add blockages from previous paths - self.add_path_blockages() - - # Now add the src/tgt if they are not blocked by other shapes - self.add_source() - self.add_target() - - - # returns the path in tracks - (path,cost) = self.rg.route(detour_scale) - if path: - debug.info(1,"Found path: cost={0} ".format(cost)) - debug.info(2,str(path)) - self.add_route(path) - return True - else: - self.write_debug_gds() - # clean up so we can try a reroute - self.clear_pins() - - - return False - - def write_debug_gds(self,): - """ - Write out a GDS file with the routing grid and search information annotated on it. - """ - # Only add the debug info to the gds file if we have any debugging on. - # This is because we may reroute a wire with detours and don't want the debug information. - if OPTS.debug_level==0: return - - self.add_router_info() - debug.error("Writing debug_route.gds from {0} to {1}".format(self.source_pin,self.target_pin)) - self.cell.gds_write("debug_route.gds") - - def add_router_info(self): - """ - Write the routing grid and router cost, blockage, pins on - the boundary layer for debugging purposes. This can only be - called once or the labels will overlap. - """ - debug.info(0,"Adding router info for {0} to {1}".format(self.source_pin,self.target_pin)) - grid_keys=self.rg.map.keys() - partial_track=vector(0,self.track_width/6.0) - for g in grid_keys: - shape = self.convert_full_track_to_shape(g) - self.cell.add_rect(layer="boundary", - offset=shape[0], - width=shape[1].x-shape[0].x, - height=shape[1].y-shape[0].y) - # These are the on grid pins - #rect = self.convert_track_to_pin(g) - #self.cell.add_rect(layer="boundary", - # offset=rect[0], - # width=rect[1].x-rect[0].x, - # height=rect[1].y-rect[0].y) - - t=self.rg.map[g].get_type() - - # midpoint offset - off=vector((shape[1].x+shape[0].x)/2, - (shape[1].y+shape[0].y)/2) - if g[2]==1: - # Upper layer is upper right label - type_off=off+partial_track - else: - # Lower layer is lower left label - type_off=off-partial_track - if t!=None: - self.cell.add_label(text=str(t), - layer="text", - offset=type_off) - self.cell.add_label(text="{0},{1}".format(g[0],g[1]), - layer="text", - offset=shape[0]) - - def add_route(self,path): - """ - Add the current wire route to the given design instance. - """ - debug.info(3,"Set path: " + str(path)) - - # Keep track of path for future blockages - self.paths.append(path) - - # This is marked for debug - self.rg.add_path(path) - - # For debugging... if the path failed to route. - if False or path==None: - self.write_debug_gds() - - if 'Xout_4_1' in [self.source_pin, self.target_pin]: - self.write_debug_gds() - - - # First, simplify the path for - #debug.info(1,str(self.path)) - contracted_path = self.contract_path(path) - debug.info(1,str(contracted_path)) - - # Make sure there's a pin enclosure on the source and dest - add_src_via = contracted_path[0].z!=self.source_pin_zindex - self.add_grid_pin(contracted_path[0],add_src_via) - add_tgt_via = contracted_path[-1].z!=self.target_pin_zindex - self.add_grid_pin(contracted_path[-1],add_tgt_via) - - # convert the path back to absolute units from tracks - abs_path = map(self.convert_point_to_units,contracted_path) - debug.info(1,str(abs_path)) - self.cell.add_route(self.layers,abs_path) - - def add_grid_pin(self,point,add_via=False): - """ - Create a rectangle at the grid 3D point that is 1/2 DRC smaller - than the routing grid on all sides. - """ - pin = self.convert_track_to_pin(point) - self.cell.add_rect(layer=self.layers[2*point.z], - offset=pin[0], - width=pin[1].x-pin[0].x, - height=pin[1].y-pin[0].y) - - if add_via: - # offset this by 1/2 the via size - c=contact(self.layers, (1, 1)) - via_offset = vector(-0.5*c.width,-0.5*c.height) - self.cell.add_via(self.layers,vector(point[0],point[1])+via_offset) - - - def create_steiner_routes(self,pins): - """ - Find a set of steiner points and then return the list of - point-to-point routes. - """ - pass - - def find_steiner_points(self,pins): - """ - Find the set of steiner points and return them. - """ - pass - def translate_coordinates(self, coord, mirr, angle, xyShift): """ Calculate coordinates after flip, rotate, and shift @@ -382,105 +201,64 @@ class router: self.rg.set_blocked(grid) - def get_source(self,pin): - """ - Gets the source pin shapes only. Doesn't add to grid. - """ - self.source_pin = pin - (self.source_pin_layer,self.source_pin_shapes) = self.find_pin(pin) - zindex = 0 if self.source_pin_layer==self.horiz_layer_number else 1 - self.source_pin_zindex = zindex - - def add_source(self): - """ - Mark the grids that are in the pin rectangle ranges to have the source property. - pin can be a location or a label. - """ - - found_pin = False - for shape in self.source_pin_shapes: - (pin_in_tracks,blockage_in_tracks)=self.convert_pin_to_tracks(shape,self.source_pin_zindex,self.source_pin) - if (len(pin_in_tracks)>0): found_pin=True - debug.info(1,"Set source: " + str(self.source_pin) + " " + str(pin_in_tracks) + " z=" + str(self.source_pin_zindex)) - self.rg.add_source(pin_in_tracks) - self.rg.add_blockage(blockage_in_tracks) - - if not found_pin: - self.write_debug_gds() - debug.check(found_pin,"Unable to find source pin on grid.") - - def get_target(self,pin): - """ - Gets the target pin shapes only. Doesn't add to grid. - """ - self.target_pin = pin - (self.target_pin_layer,self.target_pin_shapes) = self.find_pin(pin) - zindex = 0 if self.target_pin_layer==self.horiz_layer_number else 1 - self.target_pin_zindex = zindex - - def add_target(self): - """ - Mark the grids that are in the pin rectangle ranges to have the target property. - pin can be a location or a label. - """ - found_pin=False - for shape in self.target_pin_shapes: - (pin_in_tracks,blockage_in_tracks)=self.convert_pin_to_tracks(shape,self.target_pin_zindex,self.target_pin) - if (len(pin_in_tracks)>0): found_pin=True - debug.info(1,"Set target: " + str(self.target_pin) + " " + str(pin_in_tracks) + " z=" + str(self.target_pin_zindex)) - self.rg.add_target(pin_in_tracks) - self.rg.add_blockage(blockage_in_tracks) - - if not found_pin: - self.write_debug_gds() - debug.check(found_pin,"Unable to find target pin on grid.") - def add_blockages(self): """ Add the blockages except the pin shapes """ for blockage in self.blockages: - (shape,zlayer) = blockage - # Skip source pin shapes - if zlayer==self.source_pin_zindex and shape in self.source_pin_shapes: - continue - # Skip target pin shapes - if zlayer==self.target_pin_zindex and shape in self.target_pin_shapes: - continue - [ll,ur]=self.convert_blockage_to_tracks(shape) - self.rg.add_blockage_shape(ll,ur,zlayer) + # Skip pin shapes + all_pins = [x[0] for x in list(self.pins.values())] + for pin in all_pins: + if blockage.overlaps(pin): + break + else: + [ll,ur]=self.convert_blockage_to_tracks(blockage.rect) + zlayer = 0 if blockage.layer_num==self.horiz_layer_number else 1 + self.rg.add_blockage_shape(ll,ur,zlayer) - def get_blockages(self, sref, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)): + def get_blockages(self, layer_num): """ Recursive find boundaries as blockages to the routing grid. - Recurses for each Structure in GDS. """ - for boundary in self.layout.structures[sref].boundaries: - coord_trans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift) - shape_coords = self.min_max_coord(coord_trans) - shape = self.convert_shape_to_units(shape_coords) - # only consider the two layers that we are routing on - if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]: - zlayer = 0 if boundary.drawingLayer==self.horiz_layer_number else 1 - self.blockages.append((shape,zlayer)) + shapes = self.layout.getAllShapesInStructureList(layer_num) + 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) + self.blockages.append(new_pin) + + # for boundary in self.layout.structures[sref].boundaries: + # coord_trans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift) + # shape_coords = self.min_max_coord(coord_trans) + # shape = self.convert_shape_to_units(shape_coords) + + # # only consider the two layers that we are routing on + # if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]: + # # store the blockages as pin layouts so they are easy to compare etc. + # new_pin = pin_layout("blockage",shape,boundary.drawingLayer) + # # avoid repeated blockage pins + # if new_pin not in self.blockages: + # self.blockages.append(new_pin) - # recurse given the mirror, angle, etc. - for cur_sref in self.layout.structures[sref].srefs: - sMirr = 1 - if cur_sref.transFlags[0] == True: - sMirr = -1 - sAngle = math.radians(float(0)) - if cur_sref.rotateAngle: - sAngle = math.radians(float(cur_sref.rotateAngle)) - sAngle += angle - x = cur_sref.coordinates[0] - y = cur_sref.coordinates[1] - newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0] - newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1] - sxyShift = (newX, newY) + # # recurse given the mirror, angle, etc. + # for cur_sref in self.layout.structures[sref].srefs: + # sMirr = 1 + # if cur_sref.transFlags[0] == True: + # sMirr = -1 + # sAngle = math.radians(float(0)) + # if cur_sref.rotateAngle: + # sAngle = math.radians(float(cur_sref.rotateAngle)) + # sAngle += angle + # x = cur_sref.coordinates[0] + # y = cur_sref.coordinates[1] + # newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0] + # newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1] + # sxyShift = (newX, newY) - self.get_blockages(cur_sref.sName, sMirr, sAngle, sxyShift) + # self.get_blockages(cur_sref.sName, sMirr, sAngle, sxyShift) + def convert_point_to_units(self,p): """ @@ -490,32 +268,36 @@ class router: pt=pt.scale(self.track_widths[0],self.track_widths[1],1) return pt - def convert_blockage_to_tracks(self,shape,round_bigger=False): + def convert_blockage_to_tracks(self,shape): """ Convert a rectangular blockage shape into track units. """ - [ll,ur] = shape - ll = snap_to_grid(ll) - ur = snap_to_grid(ur) + (ll,ur) = shape + # ll = snap_to_grid(ll) + # ur = snap_to_grid(ur) # to scale coordinates to tracks - #debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur)) + debug.info(3,"Converting [ {0} , {1} ]".format(ll,ur)) + old_ll = ll + old_ur = ur ll=ll.scale(self.track_factor) ur=ur.scale(self.track_factor) - ll = ll.floor() if round_bigger else ll.round() - ur = ur.ceil() if round_bigger else ur.round() - #debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur)) + ll = ll.floor() + ur = ur.ceil() + if ll[0]<45 and ll[0]>35 and ll[1]<10 and ll[1]>0: + debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur)) + debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur)) return [ll,ur] - def convert_pin_to_tracks(self,shape,zindex,pin): + def convert_pin_to_tracks(self, pin): """ Convert a rectangular pin shape into a list of track locations,layers. If no on-grid pins are found, it searches for the nearest off-grid pin(s). If a pin has insufficent overlap, it returns the blockage list to avoid it. """ - [ll,ur] = shape - ll = snap_to_grid(ll) - ur = snap_to_grid(ur) + (ll,ur) = pin.rect + #ll = snap_to_grid(ll) + #ur = snap_to_grid(ur) #debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur)) @@ -524,6 +306,7 @@ class router: ur=ur.scale(self.track_factor).ceil() # width depends on which layer it is + zindex = 0 if pin.layer_num==self.horiz_layer_number else 1 if zindex==0: width = self.horiz_layer_width else: @@ -539,12 +322,12 @@ class router: # if dimension of overlap is greater than min width in any dimension, # it will be an on-grid pin rect = self.convert_track_to_pin(vector3d(x,y,zindex)) - max_overlap=max(self.compute_overlap(shape,rect)) + max_overlap=max(self.compute_overlap(pin.rect,rect)) # however, if there is not enough overlap, then if there is any overlap at all, # we need to block it to prevent routes coming in on that grid full_rect = self.convert_full_track_to_shape(vector3d(x,y,zindex)) - full_overlap=max(self.compute_overlap(shape,full_rect)) + full_overlap=max(self.compute_overlap(pin.rect,full_rect)) #debug.info(1,"Check overlap: {0} {1} max={2}".format(shape,rect,max_overlap)) if max_overlap >= width: @@ -552,7 +335,7 @@ class router: elif full_overlap>0: block_list.append(vector3d(x,y,zindex)) else: - debug.info(1,"No overlap: {0} {1} max={2}".format(shape,rect,max_overlap)) + debug.info(4,"No overlap: {0} {1} max={2}".format(pin.rect,rect,max_overlap)) #debug.warning("Off-grid pin for {0}.".format(str(pin))) #debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur)) @@ -613,6 +396,97 @@ class router: return [ll,ur] + def get_pin(self,pin_name): + """ + Gets the pin shapes only. Doesn't add to grid. + """ + self.pins[pin_name] = self.find_pin(pin_name) + + def add_pin(self,pin_name,is_source=False): + """ + Mark the grids that are in the pin rectangle ranges to have the pin property. + pin can be a location or a label. + """ + + found_pin = False + for pin in self.pins[pin_name]: + (pin_in_tracks,blockage_in_tracks)=self.convert_pin_to_tracks(pin) + if (len(pin_in_tracks)>0): + found_pin=True + if is_source: + debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks)) + self.rg.add_source(pin_in_tracks) + else: + debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) + self.rg.add_target(pin_in_tracks) + self.rg.add_blockage(blockage_in_tracks) + + if not found_pin: + self.write_debug_gds() + debug.check(found_pin,"Unable to find pin on grid.") + + def write_debug_gds(self): + """ + Write out a GDS file with the routing grid and search information annotated on it. + """ + # Only add the debug info to the gds file if we have any debugging on. + # This is because we may reroute a wire with detours and don't want the debug information. + if OPTS.debug_level==0: return + + self.add_router_info() + debug.error("Writing debug_route.gds") + self.cell.gds_write("debug_route.gds") + + def add_router_info(self): + """ + Write the routing grid and router cost, blockage, pins on + the boundary layer for debugging purposes. This can only be + called once or the labels will overlap. + """ + debug.info(0,"Adding router info") + grid_keys=self.rg.map.keys() + partial_track=vector(0,self.track_width/6.0) + for g in grid_keys: + continue # for now... + shape = self.convert_full_track_to_shape(g) + self.cell.add_rect(layer="boundary", + offset=shape[0], + width=shape[1].x-shape[0].x, + height=shape[1].y-shape[0].y) + # These are the on grid pins + #rect = self.convert_track_to_pin(g) + #self.cell.add_rect(layer="boundary", + # offset=rect[0], + # width=rect[1].x-rect[0].x, + # height=rect[1].y-rect[0].y) + + t=self.rg.map[g].get_type() + + # midpoint offset + off=vector((shape[1].x+shape[0].x)/2, + (shape[1].y+shape[0].y)/2) + if g[2]==1: + # Upper layer is upper right label + type_off=off+partial_track + else: + # Lower layer is lower left label + type_off=off-partial_track + if t!=None: + self.cell.add_label(text=str(t), + layer="text", + offset=type_off) + self.cell.add_label(text="{0},{1}".format(g[0],g[1]), + layer="text", + offset=shape[0], + zoom=0.05) + + for blockage in self.blockages: + self.cell.add_rect(layer="boundary", + offset=blockage.ll(), + width=blockage.width(), + height=blockage.height()) + + # FIXME: This should be replaced with vector.snap_to_grid at some point def snap_to_grid(offset): diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py new file mode 100644 index 00000000..ebc85eb6 --- /dev/null +++ b/compiler/router/signal_router.py @@ -0,0 +1,163 @@ +import gdsMill +import tech +from contact import contact +import math +import debug +from pin_layout import pin_layout +from vector import vector +from vector3d import vector3d +from globals import OPTS +from router import router + +class signal_router(router): + """A router class to read an obstruction map from a gds and plan a + route on a given layer. This is limited to two layer routes. + """ + + def __init__(self, gds_name=None, module=None): + """Use the gds file for the blockages with the top module topName and + layers for the layers to route on + """ + router.__init__(self, gds_name, module) + + # all the paths we've routed so far (to supplement the blockages) + self.paths = [] + + + def create_routing_grid(self): + """ + Create a sprase routing grid with A* expansion functions. + """ + # We will add a halo around the boundary + # of this many tracks + size = self.ur - self.ll + debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) + + import astar_grid + self.rg = astar_grid.astar_grid() + + + def route(self, cell, layers, src, dest, detour_scale=5): + """ + Route a single source-destination net and return + the simplified rectilinear path. Cost factor is how sub-optimal to explore for a feasible route. + This is used to speed up the routing when there is not much detouring needed. + """ + debug.info(1,"Running signal router from {0} to {1}...".format(src,dest)) + self.cell = cell + self.source_pin_name = src + self.target_pin_name = dest + + # Clear the pins if we have previously routed + if (hasattr(self,'rg')): + self.clear_pins() + else: + # Set up layers and track sizes + self.set_layers(layers) + # Creat a routing grid over the entire area + # FIXME: This could be created only over the routing region, + # but this is simplest for now. + self.create_routing_grid() + # This will get all shapes as blockages + self.find_blockages() + + # Get the pin shapes + self.get_pin(src) + self.get_pin(dest) + + # Now add the blockages (all shapes except the src/tgt pins) + self.add_blockages() + # Add blockages from previous paths + self.add_path_blockages() + + # Now add the src/tgt if they are not blocked by other shapes + self.add_pin(src,True) + self.add_pin(dest,False) + + + # returns the path in tracks + (path,cost) = self.rg.route(detour_scale) + if path: + debug.info(1,"Found path: cost={0} ".format(cost)) + debug.info(2,str(path)) + self.add_route(path) + return True + else: + self.write_debug_gds() + # clean up so we can try a reroute + self.clear_pins() + + + return False + + + def add_route(self,path): + """ + Add the current wire route to the given design instance. + """ + debug.info(3,"Set path: " + str(path)) + + # Keep track of path for future blockages + self.paths.append(path) + + # This is marked for debug + self.rg.add_path(path) + + # For debugging... if the path failed to route. + if False or path==None: + self.write_debug_gds() + + + # First, simplify the path for + #debug.info(1,str(self.path)) + contracted_path = self.contract_path(path) + debug.info(1,str(contracted_path)) + + # convert the path back to absolute units from tracks + abs_path = map(self.convert_point_to_units,contracted_path) + debug.info(1,str(abs_path)) + self.cell.add_route(self.layers,abs_path) + + + def get_inertia(self,p0,p1): + """ + Sets the direction based on the previous direction we came from. + """ + # direction (index) of movement + if p0.x!=p1.x: + return 0 + elif p0.y!=p1.y: + return 1 + else: + # z direction + return 2 + + def contract_path(self,path): + """ + Remove intermediate points in a rectilinear path. + """ + newpath = [path[0]] + for i in range(1,len(path)-1): + prev_inertia=self.get_inertia(path[i-1],path[i]) + next_inertia=self.get_inertia(path[i],path[i+1]) + # if we switch directions, add the point, otherwise don't + if prev_inertia!=next_inertia: + newpath.append(path[i]) + + # always add the last path + newpath.append(path[-1]) + return newpath + + + def add_path_blockages(self): + """ + Go through all of the past paths and add them as blockages. + This is so we don't have to write/reload the GDS. + """ + for path in self.paths: + for grid in path: + self.rg.set_blocked(grid) + + + + diff --git a/compiler/router/supply_grid.py b/compiler/router/supply_grid.py new file mode 100644 index 00000000..b9e1f81b --- /dev/null +++ b/compiler/router/supply_grid.py @@ -0,0 +1,27 @@ +import debug +from vector3d import vector3d +import grid + + +class supply_grid(grid.grid): + """ + A two layer routing map. Each cell can be blocked in the vertical + or horizontal layer. + """ + + def __init__(self): + """ Create a routing map of width x height cells and 2 in the z-axis. """ + grid.grid.__init__(self) + + # list of the vdd/gnd rail cells + self.vdd_rails = [] + self.gnd_rails = [] + + def reinit(self): + """ Reinitialize everything for a new route. """ + + # Reset all the cells in the map + for p in self.map.values(): + p.reset() + + diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py new file mode 100644 index 00000000..908e8686 --- /dev/null +++ b/compiler/router/supply_router.py @@ -0,0 +1,146 @@ +import gdsMill +import tech +from contact import contact +import math +import debug +import grid +from pin_layout import pin_layout +from vector import vector +from vector3d import vector3d +from globals import OPTS +from router import router + +class supply_router(router): + """ + A router class to read an obstruction map from a gds and + routes a grid to connect the supply on the two layers. + """ + + def __init__(self, gds_name=None, module=None): + """Use the gds file for the blockages with the top module topName and + layers for the layers to route on + """ + router.__init__(self, gds_name, module) + + self.pins = {} + + + def clear_pins(self): + """ + Convert the routed path to blockages. + Keep the other blockages unchanged. + """ + self.pins = {} + self.rg.reinit() + + + def route(self, cell, layers, vdd_name="vdd", gnd_name="gnd"): + """ + Route a single source-destination net and return + the simplified rectilinear path. + """ + debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) + self.cell = cell + self.pins[vdd_name] = [] + self.pins[gnd_name] = [] + + # Clear the pins if we have previously routed + if (hasattr(self,'rg')): + self.clear_pins() + else: + # Set up layers and track sizes + self.set_layers(layers) + # Creat a routing grid over the entire area + # FIXME: This could be created only over the routing region, + # but this is simplest for now. + self.create_routing_grid() + # This will get all shapes as blockages + self.find_blockages() + + # Get the pin shapes + self.get_pin(vdd_name) + self.get_pin(gnd_name) + + # Now add the blockages (all shapes except the src/tgt pins) + self.add_blockages() + # Add blockages from previous routes + self.add_path_blockages() + + # source pin will be a specific layout pin + # target pin will be the rails only + + # returns the path in tracks + # (path,cost) = self.rg.route(detour_scale) + # if path: + # debug.info(1,"Found path: cost={0} ".format(cost)) + # debug.info(2,str(path)) + # self.add_route(path) + # return True + # else: + # self.write_debug_gds() + # # clean up so we can try a reroute + # self.clear_pins() + + self.write_debug_gds() + return False + + + def add_route(self,path): + """ + Add the current wire route to the given design instance. + """ + debug.info(3,"Set path: " + str(path)) + + # Keep track of path for future blockages + self.paths.append(path) + + # This is marked for debug + self.rg.add_path(path) + + # For debugging... if the path failed to route. + if False or path==None: + self.write_debug_gds() + + # First, simplify the path for + #debug.info(1,str(self.path)) + contracted_path = self.contract_path(path) + debug.info(1,str(contracted_path)) + + # convert the path back to absolute units from tracks + abs_path = map(self.convert_point_to_units,contracted_path) + debug.info(1,str(abs_path)) + self.cell.add_route(self.layers,abs_path) + + + def create_routing_grid(self): + """ + Create a sprase routing grid with A* expansion functions. + """ + # We will add a halo around the boundary + # of this many tracks + size = self.ur - self.ll + debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) + + import supply_grid + self.rg = supply_grid.supply_grid() + + + ########################## + # Gridded supply route functions + ########################## + def create_grid(self, ll, ur): + """ Create alternating vdd/gnd lines horizontally """ + + self.create_horizontal_grid() + self.create_vertical_grid() + + + def create_horizontal_grid(self): + """ Create alternating vdd/gnd lines horizontally """ + + pass + + def create_vertical_grid(self): + """ Create alternating vdd/gnd lines horizontally """ + pass + diff --git a/compiler/router/tests/01_no_blockages_test.py b/compiler/router/tests/01_no_blockages_test.py old mode 100644 new mode 100755 index 99e5b601..c7344a64 --- a/compiler/router/tests/01_no_blockages_test.py +++ b/compiler/router/tests/01_no_blockages_test.py @@ -1,77 +1,52 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 "Run a regresion test the library cells for DRC" import unittest -from testutils import header +from testutils import header,openram_test import sys,os sys.path.append(os.path.join(sys.path[0],"../..")) sys.path.append(os.path.join(sys.path[0],"..")) import globals import debug -import calibre OPTS = globals.OPTS -class no_blockages_test(unittest.TestCase): +class no_blockages_test(openram_test): """ Simplest two pin route test with no blockages. """ def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from signal_router import signal_router as router - import design - import router - - class gdscell(design.design): + class routing(design, openram_test): """ A generic GDS design that we can route on. """ def __init__(self, name): - #design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) - self.name = name - self.gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - self.sp_file = "{0}/{1}.sp".format(os.path.dirname(os.path.realpath(__file__)),name) - design.hierarchy_layout.layout.__init__(self, name) - design.hierarchy_spice.spice.__init__(self, name) - - class routing(design.design,unittest.TestCase): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) + design.__init__(self, "top") - cell = gdscell(name) + # Instantiate a GDS cell with the design + gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) + cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) self.connect_inst([]) - self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - r=router.router(self.gdsname) + r=router(gds_file) layer_stack =("metal1","via1","metal2") self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) - r = routing("01_no_blockages_test_{0}".format(OPTS.tech_name)) - self.local_check(r) + r=routing("01_no_blockages_test_{0}".format(OPTS.tech_name)) + self.local_drc_check(r) # fails if there are any DRC errors on any cells globals.end_openram() - - def local_check(self, r): - tempgds = OPTS.openram_temp + "temp.gds" - r.gds_write(tempgds) - self.assertFalse(calibre.run_drc(r.name, tempgds)) - os.remove(tempgds) - - - - - # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/router/tests/01_no_blockages_test_freepdk45.gds b/compiler/router/tests/01_no_blockages_test_freepdk45.gds index cbad1838..ec3c99d0 100644 Binary files a/compiler/router/tests/01_no_blockages_test_freepdk45.gds and b/compiler/router/tests/01_no_blockages_test_freepdk45.gds differ diff --git a/compiler/router/tests/01_no_blockages_test_scn3me_subm.gds b/compiler/router/tests/01_no_blockages_test_scn3me_subm.gds index a94ec07c..9d8b540e 100644 Binary files a/compiler/router/tests/01_no_blockages_test_scn3me_subm.gds and b/compiler/router/tests/01_no_blockages_test_scn3me_subm.gds differ diff --git a/compiler/router/tests/02_blockages_test.py b/compiler/router/tests/02_blockages_test.py old mode 100644 new mode 100755 index 2afefa4a..2e85b1c2 --- a/compiler/router/tests/02_blockages_test.py +++ b/compiler/router/tests/02_blockages_test.py @@ -1,74 +1,53 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 "Run a regresion test the library cells for DRC" import unittest -from testutils import header +from testutils import header,openram_test import sys,os sys.path.append(os.path.join(sys.path[0],"../..")) sys.path.append(os.path.join(sys.path[0],"..")) import globals import debug -import calibre OPTS = globals.OPTS -class blockages_test(unittest.TestCase): +class blockages_test(openram_test): """ Simple two pin route test with multilayer blockages. """ def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from signal_router import signal_router as router - import design - import router - - class gdscell(design.design): + class routing(design, openram_test): """ A generic GDS design that we can route on. """ def __init__(self, name): - #design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) - self.name = name - self.gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - self.sp_file = "{0}/{1}.sp".format(os.path.dirname(os.path.realpath(__file__)),name) - design.hierarchy_layout.layout.__init__(self, name) - design.hierarchy_spice.spice.__init__(self, name) - - class routing(design.design,unittest.TestCase): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) + design.__init__(self, "top") - cell = gdscell(name) + # Instantiate a GDS cell with the design + gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) + cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) self.connect_inst([]) - self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - r=router.router(self.gdsname) + r=router(gds_file) layer_stack =("metal1","via1","metal2") self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) - r = routing("02_blockages_test_{0}".format(OPTS.tech_name)) - self.local_check(r) + r=routing("02_blockages_test_{0}".format(OPTS.tech_name)) + self.local_drc_check(r) # fails if there are any DRC errors on any cells globals.end_openram() - def local_check(self, r): - tempgds = OPTS.openram_temp + "temp.gds" - r.gds_write(tempgds) - self.assertFalse(calibre.run_drc(r.name, tempgds)) - os.remove(tempgds) - - diff --git a/compiler/router/tests/02_blockages_test_freepdk45.gds b/compiler/router/tests/02_blockages_test_freepdk45.gds index 2e31ba2c..e6f868dc 100644 Binary files a/compiler/router/tests/02_blockages_test_freepdk45.gds and b/compiler/router/tests/02_blockages_test_freepdk45.gds differ diff --git a/compiler/router/tests/02_blockages_test_scn3me_subm.gds b/compiler/router/tests/02_blockages_test_scn3me_subm.gds index f9407ee4..aec979e4 100644 Binary files a/compiler/router/tests/02_blockages_test_scn3me_subm.gds and b/compiler/router/tests/02_blockages_test_scn3me_subm.gds differ diff --git a/compiler/router/tests/03_same_layer_pins_test.py b/compiler/router/tests/03_same_layer_pins_test.py old mode 100644 new mode 100755 index dfd849c9..98ce3a2a --- a/compiler/router/tests/03_same_layer_pins_test.py +++ b/compiler/router/tests/03_same_layer_pins_test.py @@ -1,77 +1,51 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 "Run a regresion test the library cells for DRC" import unittest -from testutils import header +from testutils import header,openram_test import sys,os sys.path.append(os.path.join(sys.path[0],"../..")) sys.path.append(os.path.join(sys.path[0],"..")) import globals import debug -import calibre OPTS = globals.OPTS -class same_layer_pins_test(unittest.TestCase): +class same_layer_pins_test(openram_test): """ Checks two pins on the same layer with positive and negative coordinates. """ def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from signal_router import signal_router as router - import design - import router - - class gdscell(design.design): + class routing(design, openram_test): """ A generic GDS design that we can route on. """ def __init__(self, name): - #design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) - self.name = name - self.gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - self.sp_file = "{0}/{1}.sp".format(os.path.dirname(os.path.realpath(__file__)),name) - design.hierarchy_layout.layout.__init__(self, name) - design.hierarchy_spice.spice.__init__(self, name) - - class routing(design.design,unittest.TestCase): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) + design.__init__(self, "top") - cell = gdscell(name) + # Instantiate a GDS cell with the design + gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) + cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) self.connect_inst([]) - self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - r=router.router(self.gdsname) + r=router(gds_file) layer_stack =("metal1","via1","metal2") self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) r = routing("03_same_layer_pins_test_{0}".format(OPTS.tech_name)) - self.local_check(r) - + self.local_drc_check(r) # fails if there are any DRC errors on any cells globals.end_openram() - - def local_check(self, r): - tempgds = OPTS.openram_temp + "temp.gds" - r.gds_write(tempgds) - self.assertFalse(calibre.run_drc(r.name, tempgds)) - os.remove(tempgds) - - - - - # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/router/tests/04_diff_layer_pins_test.py b/compiler/router/tests/04_diff_layer_pins_test.py old mode 100644 new mode 100755 index e0a54875..cbc21470 --- a/compiler/router/tests/04_diff_layer_pins_test.py +++ b/compiler/router/tests/04_diff_layer_pins_test.py @@ -1,18 +1,17 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 "Run a regresion test the library cells for DRC" import unittest -from testutils import header +from testutils import header,openram_test import sys,os sys.path.append(os.path.join(sys.path[0],"../..")) sys.path.append(os.path.join(sys.path[0],"..")) import globals import debug -import calibre OPTS = globals.OPTS -class diff_layer_pins_test(unittest.TestCase): +class diff_layer_pins_test(openram_test): """ Two pin route test with pins on different layers and blockages. Pins are smaller than grid size. @@ -20,59 +19,35 @@ class diff_layer_pins_test(unittest.TestCase): def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from signal_router import signal_router as router - import design - import router - - class gdscell(design.design): + class routing(design, openram_test): """ A generic GDS design that we can route on. """ def __init__(self, name): - #design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) - self.name = name - self.gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - self.sp_file = "{0}/{1}.sp".format(os.path.dirname(os.path.realpath(__file__)),name) - design.hierarchy_layout.layout.__init__(self, name) - design.hierarchy_spice.spice.__init__(self, name) - - class routing(design.design,unittest.TestCase): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) + design.__init__(self, "top") - cell = gdscell(name) + # Instantiate a GDS cell with the design + gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) + cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) self.connect_inst([]) - self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - r=router.router(self.gdsname) + r=router(gds_file) layer_stack =("metal1","via1","metal2") self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) r = routing("04_diff_layer_pins_test_{0}".format(OPTS.tech_name)) - self.local_check(r) + self.local_drc_check(r) # fails if there are any DRC errors on any cells globals.end_openram() - - def local_check(self, r): - tempgds = OPTS.openram_temp + "temp.gds" - r.gds_write(tempgds) - self.assertFalse(calibre.run_drc(r.name, tempgds)) - os.remove(tempgds) - - - - - # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/router/tests/05_two_nets_test.py b/compiler/router/tests/05_two_nets_test.py old mode 100644 new mode 100755 index b59dd005..166292d0 --- a/compiler/router/tests/05_two_nets_test.py +++ b/compiler/router/tests/05_two_nets_test.py @@ -1,18 +1,17 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 "Run a regresion test the library cells for DRC" import unittest -from testutils import header +from testutils import header,openram_test import sys,os sys.path.append(os.path.join(sys.path[0],"../..")) sys.path.append(os.path.join(sys.path[0],"..")) import globals import debug -import calibre OPTS = globals.OPTS -class two_nets_test(unittest.TestCase): +class two_nets_test(openram_test): """ Route two nets in the same GDS file. The routes will interact, so they must block eachother. @@ -20,60 +19,37 @@ class two_nets_test(unittest.TestCase): def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from signal_router import signal_router as router - import design - import router - - class gdscell(design.design): + class routing(design, openram_test): """ A generic GDS design that we can route on. """ def __init__(self, name): - #design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) - self.name = name - self.gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - self.sp_file = "{0}/{1}.sp".format(os.path.dirname(os.path.realpath(__file__)),name) - design.hierarchy_layout.layout.__init__(self, name) - design.hierarchy_spice.spice.__init__(self, name) - - class routing(design.design,unittest.TestCase): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) + design.__init__(self, "top") - cell = gdscell(name) + # Instantiate a GDS cell with the design + gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) + cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) self.connect_inst([]) - self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - r=router.router(self.gdsname) + r=router(gds_file) layer_stack =("metal1","via1","metal2") self.assertTrue(r.route(self,layer_stack,src="A",dest="B")) self.assertTrue(r.route(self,layer_stack,src="C",dest="D")) r = routing("05_two_nets_test_{0}".format(OPTS.tech_name)) - self.local_check(r) + self.local_drc_check(r) # fails if there are any DRC errors on any cells globals.end_openram() - def local_check(self, r): - tempgds = OPTS.openram_temp + "temp.gds" - r.gds_write(tempgds) - self.assertFalse(calibre.run_drc(r.name, tempgds)) - os.remove(tempgds) - - - - - # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/router/tests/06_pin_location_test.py b/compiler/router/tests/06_pin_location_test.py old mode 100644 new mode 100755 index 10cb854b..e67fed53 --- a/compiler/router/tests/06_pin_location_test.py +++ b/compiler/router/tests/06_pin_location_test.py @@ -1,57 +1,43 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 "Run a regresion test the library cells for DRC" import unittest -from testutils import header +from testutils import header,openram_test import sys,os sys.path.append(os.path.join(sys.path[0],"../..")) sys.path.append(os.path.join(sys.path[0],"..")) import globals import debug -import calibre OPTS = globals.OPTS -class pin_location_test(unittest.TestCase): +class pin_location_test(openram_test): """ Simplest two pin route test with no blockages using the pin locations instead of labels. """ def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from signal_router import signal_router as router - import design - import router - - class gdscell(design.design): + class routing(design, openram_test): """ A generic GDS design that we can route on. """ def __init__(self, name): - #design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) - self.name = name - self.gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - self.sp_file = "{0}/{1}.sp".format(os.path.dirname(os.path.realpath(__file__)),name) - design.hierarchy_layout.layout.__init__(self, name) - design.hierarchy_spice.spice.__init__(self, name) - - class routing(design.design,unittest.TestCase): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) + design.__init__(self, "top") - cell = gdscell(name) + # Instantiate a GDS cell with the design + gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) + cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) self.connect_inst([]) - self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - r=router.router(self.gdsname) + r=router(gds_file) layer_stack =("metal1","via1","metal2") # these are user coordinates and layers src_pin = [[0.52, 4.099],11] @@ -62,21 +48,12 @@ class pin_location_test(unittest.TestCase): # This only works for freepdk45 since the coordinates are hard coded if OPTS.tech_name == "freepdk45": r = routing("06_pin_location_test_{0}".format(OPTS.tech_name)) - self.local_check(r) + self.local_drc_check(r) else: debug.warning("This test does not support technology {0}".format(OPTS.tech_name)) - + # fails if there are any DRC errors on any cells globals.end_openram() - - - def local_check(self, r): - tempgds = OPTS.openram_temp + "temp.gds" - r.gds_write(tempgds) - self.assertFalse(calibre.run_drc(r.name, tempgds)) - os.remove(tempgds) - - diff --git a/compiler/router/tests/07_big_test.py b/compiler/router/tests/07_big_test.py old mode 100644 new mode 100755 index 4e66c07e..5f844ec5 --- a/compiler/router/tests/07_big_test.py +++ b/compiler/router/tests/07_big_test.py @@ -1,58 +1,44 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 "Run a regresion test the library cells for DRC" import unittest -from testutils import header +from testutils import header,openram_test import sys,os sys.path.append(os.path.join(sys.path[0],"../..")) sys.path.append(os.path.join(sys.path[0],"..")) import globals import debug -import calibre OPTS = globals.OPTS -class big_test(unittest.TestCase): +class big_test(openram_test): """ Simplest two pin route test with no blockages using the pin locations instead of labels. """ def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from signal_router import signal_router as router - import design - import router - - class gdscell(design.design): + class routing(design, openram_test): """ A generic GDS design that we can route on. """ def __init__(self, name): - #design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) - self.name = name - self.gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - self.sp_file = "{0}/{1}.sp".format(os.path.dirname(os.path.realpath(__file__)),name) - design.hierarchy_layout.layout.__init__(self, name) - design.hierarchy_spice.spice.__init__(self, name) - - class routing(design.design,unittest.TestCase): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) + design.__init__(self, "top") - cell = gdscell(name) + # Instantiate a GDS cell with the design + gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) + cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) self.connect_inst([]) - self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - r=router.router(self.gdsname) - layer_stack =("metal3","via2","metal2") + r=router(gds_file) + layer_stack =("metal1","via1","metal2") connections=[('out_0_2', 'a_0_0'), ('out_0_3', 'b_0_0'), ('out_0_0', 'a_0_1'), @@ -80,21 +66,12 @@ class big_test(unittest.TestCase): # This test only runs on scn3me_subm tech if OPTS.tech_name=="scn3me_subm": r = routing("07_big_test_{0}".format(OPTS.tech_name)) - self.local_check(r) + self.local_drc_check(r) else: debug.warning("This test does not support technology {0}".format(OPTS.tech_name)) # fails if there are any DRC errors on any cells globals.end_openram() - - - def local_check(self, r): - tempgds = OPTS.openram_temp + "temp.gds" - r.gds_write(tempgds) - self.assertFalse(calibre.run_drc(r.name, tempgds)) - os.remove(tempgds) - - diff --git a/compiler/router/tests/08_expand_region_test.py b/compiler/router/tests/08_expand_region_test.py old mode 100644 new mode 100755 index f7d3805b..96edab57 --- a/compiler/router/tests/08_expand_region_test.py +++ b/compiler/router/tests/08_expand_region_test.py @@ -1,57 +1,43 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 "Run a regresion test the library cells for DRC" import unittest -from testutils import header +from testutils import header,openram_test import sys,os sys.path.append(os.path.join(sys.path[0],"../..")) sys.path.append(os.path.join(sys.path[0],"..")) import globals import debug -import calibre OPTS = globals.OPTS -class expand_region_test(unittest.TestCase): +class expand_region_test(openram_test): """ Test an infeasible route followed by a feasible route with an expanded region. """ def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from signal_router import signal_router as router - import design - import router - - class gdscell(design.design): + class routing(design, openram_test): """ A generic GDS design that we can route on. """ def __init__(self, name): - #design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) - self.name = name - self.gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - self.sp_file = "{0}/{1}.sp".format(os.path.dirname(os.path.realpath(__file__)),name) - design.hierarchy_layout.layout.__init__(self, name) - design.hierarchy_spice.spice.__init__(self, name) - - class routing(design.design,unittest.TestCase): - """ - A generic GDS design that we can route on. - """ - def __init__(self, name): - design.design.__init__(self, name) - debug.info(2, "Create {0} object".format(name)) + design.__init__(self, "top") - cell = gdscell(name) + # Instantiate a GDS cell with the design + gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) + cell = gds_cell(name, gds_file) self.add_inst(name=name, mod=cell, offset=[0,0]) self.connect_inst([]) - self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),name) - r=router.router(self.gdsname) + r=router(gds_file) layer_stack =("metal1","via1","metal2") # This should be infeasible because it is blocked without a detour. self.assertFalse(r.route(self,layer_stack,src="A",dest="B",detour_scale=1)) @@ -59,22 +45,12 @@ class expand_region_test(unittest.TestCase): self.assertTrue(r.route(self,layer_stack,src="A",dest="B",detour_scale=3)) r = routing("08_expand_region_test_{0}".format(OPTS.tech_name)) - self.local_check(r) + self.local_drc_check(r) # fails if there are any DRC errors on any cells globals.end_openram() - def local_check(self, r): - tempgds = OPTS.openram_temp + "temp.gds" - r.gds_write(tempgds) - self.assertFalse(calibre.run_drc(r.name, tempgds)) - os.remove(tempgds) - - - - - # instantiate a copy of the class to actually run the test if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/router/tests/10_supply_grid_test.py b/compiler/router/tests/10_supply_grid_test.py new file mode 100755 index 00000000..485d925c --- /dev/null +++ b/compiler/router/tests/10_supply_grid_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +"Run a regresion test the library cells for DRC" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug + +OPTS = globals.OPTS + +class no_blockages_test(openram_test): + """ + Simplest two pin route test with no blockages. + """ + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from gds_cell import gds_cell + from design import design + from supply_router import supply_router as router + + class routing(design, openram_test): + """ + A generic GDS design that we can route on. + """ + def __init__(self, name): + design.__init__(self, "top") + + # Instantiate a GDS cell with the design + globals.setup_paths() + from control_logic import control_logic + cell = control_logic(16) + #gds_file = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),"control_logic") + #cell = gds_cell(name, gds_file) + self.add_inst(name=name, + mod=cell, + offset=[0,0]) + self.connect_inst(["csb","web","clk","s_en","w_en","clk_buf_bar","clk_buf","vdd","gnd"]) + + r=router(module=cell) + layer_stack =("metal3","via2","metal2") + self.assertTrue(r.route(self,layer_stack)) + + r=routing("10_supply_grid_test_{0}".format(OPTS.tech_name)) + self.local_drc_check(r) + + # fails if there are any DRC errors on any cells + globals.end_openram() + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds b/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds new file mode 100644 index 00000000..7cfbc0cb Binary files /dev/null and b/compiler/router/tests/10_supply_grid_test_scn3me_subm.gds differ diff --git a/compiler/router/tests/config_freepdk45.py b/compiler/router/tests/config_freepdk45.py old mode 100644 new mode 100755 index 1217a677..d7e1880e --- a/compiler/router/tests/config_freepdk45.py +++ b/compiler/router/tests/config_freepdk45.py @@ -3,21 +3,8 @@ num_words = 16 num_banks = 1 tech_name = "freepdk45" +process_corners = ["TT"] +supply_voltages = [1.0] +temperatures = [25] + -decoder = "hierarchical_decoder" -ms_flop = "ms_flop" -ms_flop_array = "ms_flop_array" -control_logic = "control_logic" -bitcell_array = "bitcell_array" -sense_amp = "sense_amp" -sense_amp_array = "sense_amp_array" -precharge_array = "precharge_array" -column_mux_array = "single_level_column_mux_array" -write_driver = "write_driver" -write_driver_array = "write_driver_array" -tri_gate = "tri_gate" -tri_gate_array = "tri_gate_array" -wordline_driver = "wordline_driver" -replica_bitcell = "replica_bitcell" -bitcell = "bitcell" -delay_chain = "logic_effort_dc" diff --git a/compiler/router/tests/config_scn3me_subm.py b/compiler/router/tests/config_scn3me_subm.py old mode 100644 new mode 100755 index d7618847..40addd69 --- a/compiler/router/tests/config_scn3me_subm.py +++ b/compiler/router/tests/config_scn3me_subm.py @@ -3,21 +3,7 @@ num_words = 16 num_banks = 1 tech_name = "scn3me_subm" +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] -decoder = "hierarchical_decoder" -ms_flop = "ms_flop" -ms_flop_array = "ms_flop_array" -control_logic = "control_logic" -bitcell_array = "bitcell_array" -sense_amp = "sense_amp" -sense_amp_array = "sense_amp_array" -precharge_array = "precharge_array" -column_mux_array = "single_level_column_mux_array" -write_driver = "write_driver" -write_driver_array = "write_driver_array" -tri_gate = "tri_gate" -tri_gate_array = "tri_gate_array" -wordline_driver = "wordline_driver" -replica_bitcell = "replica_bitcell" -bitcell = "bitcell" -delay_chain = "logic_effort_dc" diff --git a/compiler/router/tests/gds_cell.py b/compiler/router/tests/gds_cell.py new file mode 100644 index 00000000..5c1e0f91 --- /dev/null +++ b/compiler/router/tests/gds_cell.py @@ -0,0 +1,16 @@ +from design import design +class gds_cell(design): + """ + A generic GDS design. + """ + def __init__(self, name, gds_file): + self.name = name + self.gds_file = gds_file + self.sp_file = None + + design.__init__(self, name) + + # The dimensions will not be defined, so do this... + self.width=0 + self.height=0 + diff --git a/compiler/router/tests/regress.py b/compiler/router/tests/regress.py old mode 100644 new mode 100755 index 68ad49fe..02c077f1 --- a/compiler/router/tests/regress.py +++ b/compiler/router/tests/regress.py @@ -1,16 +1,16 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python3 import re import unittest import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -sys.path.append(os.path.join(sys.path[0],"../..")) +sys.path.append(os.path.join(sys.path[0],"../../compiler")) +print(sys.path) import globals (OPTS, args) = globals.parse_args() del sys.argv[1:] -from testutils import header +from testutils import header,openram_test header(__file__, OPTS.tech_name) # get a list of all files in the tests directory @@ -18,7 +18,7 @@ files = os.listdir(sys.path[0]) # assume any file that ends in "test.py" in it is a regression test nametest = re.compile("test\.py$", re.IGNORECASE) -tests = filter(nametest.search, files) +tests = list(filter(nametest.search, files)) tests.sort() # import all of the modules @@ -28,4 +28,13 @@ modules = map(__import__, moduleNames) suite = unittest.TestSuite() load = unittest.defaultTestLoader.loadTestsFromModule suite.addTests(map(load, modules)) -unittest.TextTestRunner(verbosity=2).run(suite) + +test_runner = unittest.TextTestRunner(verbosity=2,stream=sys.stderr) +test_result = test_runner.run(suite) + +import verify +verify.print_drc_stats() +verify.print_lvs_stats() +verify.print_pex_stats() + +sys.exit(not test_result.wasSuccessful()) diff --git a/compiler/router/tests/testutils.py b/compiler/router/tests/testutils.py old mode 100644 new mode 100755 index 7b8e2cc6..4bea5d15 --- a/compiler/router/tests/testutils.py +++ b/compiler/router/tests/testutils.py @@ -1,43 +1,258 @@ +import unittest,warnings +import sys,os,glob,copy +sys.path.append(os.path.join(sys.path[0],"../..")) +from globals import OPTS +import debug + +class openram_test(unittest.TestCase): + """ Base unit test that we have some shared classes in. """ + + def local_drc_check(self, w): + + self.reset() + + tempgds = OPTS.openram_temp + "temp.gds" + w.gds_write(tempgds) + import verify + + result=verify.run_drc(w.name, tempgds) + if result != 0: + self.fail("DRC failed: {}".format(w.name)) + + if OPTS.purge_temp: + self.cleanup() + + def local_check(self, a, final_verification=False): + + self.reset() + + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + import verify + result=verify.run_drc(a.name, tempgds) + if result != 0: + self.fail("DRC failed: {}".format(a.name)) + + + result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) + if result != 0: + self.fail("LVS mismatch: {}".format(a.name)) + + if OPTS.purge_temp: + self.cleanup() + + def cleanup(self): + """ Reset the duplicate checker and cleanup files. """ + files = glob.glob(OPTS.openram_temp + '*') + for f in files: + # Only remove the files + if os.path.isfile(f): + os.remove(f) + + def reset(self): + """ + Reset everything after each test. + """ + # Reset the static duplicate name checker for unit tests. + import hierarchy_design + hierarchy_design.hierarchy_design.name_map=[] + + def check_golden_data(self, data, golden_data, error_tolerance=1e-2): + """ + This function goes through two dictionaries, key by key and compares + each item. It uses relative comparisons for the items and returns false + if there is a mismatch. + """ + + # Check each result + data_matches = True + for k in data.keys(): + if type(data[k])==list: + for i in range(len(data[k])): + if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance): + data_matches = False + else: + self.isclose(k,data[k],golden_data[k],error_tolerance) + if not data_matches: + import pprint + data_string=pprint.pformat(data) + debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance*100)+data_string) + return data_matches + -def isclose(value1,value2,error_tolerance=1e-2): - """ This is used to compare relative values. """ - import debug - relative_diff = abs(value1 - value2) / max(value1,value2) - check = relative_diff <= error_tolerance - if not check: - debug.info(1,"NOT CLOSE {0} {1} relative diff={2}".format(value1,value2,relative_diff)) - else: - debug.info(2,"CLOSE {0} {1} relative diff={2}".format(value1,value2,relative_diff)) - return (check) + def isclose(self,key,value,actual_value,error_tolerance=1e-2): + """ This is used to compare relative values. """ + import debug + relative_diff = self.relative_diff(value,actual_value) + check = relative_diff <= error_tolerance + if check: + debug.info(2,"CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100)) + return True + else: + debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100)) + return False -def isdiff(file1,file2): - """ This is used to compare two files and display the diff if they are different.. """ - import debug - import filecmp - import difflib - check = filecmp.cmp(file1,file2) - if not check: - debug.info(2,"MISMATCH {0} {1}".format(file1,file2)) - f1 = open(file1,"r") - s1 = f1.readlines() - f2 = open(file2,"r") - s2 = f2.readlines() - for line in difflib.unified_diff(s1, s2): - debug.error(line) - else: - debug.info(2,"MATCH {0} {1}".format(file1,file2)) - return (check) + def relative_diff(self, value1, value2): + """ Compute the relative difference of two values and normalize to the largest. + If largest value is 0, just return the difference.""" + + # Edge case to avoid divide by zero + if value1==0 and value2==0: + return 0.0 + + # Don't need relative, exact compare + if value1==value2: + return 0.0 + + # Get normalization value + norm_value = abs(max(value1, value2)) + # Edge case where greater is a zero + if norm_value == 0: + min_value = abs(min(value1, value2)) + + return abs(value1 - value2) / norm_value + + + def relative_compare(self, value,actual_value,error_tolerance): + """ This is used to compare relative values. """ + if (value==actual_value): # if we don't need a relative comparison! + return True + return (abs(value - actual_value) / max(value,actual_value) <= error_tolerance) + + def isapproxdiff(self, filename1, filename2, error_tolerance=0.001): + """Compare two files. + + Arguments: + + filename1 -- First file name + + filename2 -- Second file name + + Return value: + + True if the files are the same, False otherwise. + + """ + import re + import debug + + numeric_const_pattern = r""" + [-+]? # optional sign + (?: + (?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc + | + (?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc + ) + # followed by optional exponent part if desired + (?: [Ee] [+-]? \d+ ) ? + """ + rx = re.compile(numeric_const_pattern, re.VERBOSE) + fp1 = open(filename1, 'rb') + fp2 = open(filename2, 'rb') + mismatches=0 + line_num=0 + while True: + line_num+=1 + line1 = fp1.readline().decode('utf-8') + line2 = fp2.readline().decode('utf-8') + #print("line1:",line1) + #print("line2:",line2) + + # 1. Find all of the floats using a regex + line1_floats=rx.findall(line1) + line2_floats=rx.findall(line2) + debug.info(3,"line1_floats: "+str(line1_floats)) + debug.info(3,"line2_floats: "+str(line2_floats)) + + + # 2. Remove the floats from the string + for f in line1_floats: + line1=line1.replace(f,"",1) + for f in line2_floats: + line2=line2.replace(f,"",1) + #print("line1:",line1) + #print("line2:",line2) + + # 3. Convert to floats rather than strings + line1_floats = [float(x) for x in line1_floats] + line2_floats = [float(x) for x in line1_floats] + + # 4. Check if remaining string matches + if line1 != line2: + if mismatches==0: + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + mismatches += 1 + debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num,line1.rstrip('\n'),line2.rstrip('\n'))) + + # 5. Now compare that the floats match + elif len(line1_floats)!=len(line2_floats): + if mismatches==0: + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + mismatches += 1 + debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num,len(line1_floats),len(line2_floats))) + else: + for (float1,float2) in zip(line1_floats,line2_floats): + relative_diff = self.relative_diff(float1,float2) + check = relative_diff <= error_tolerance + if not check: + if mismatches==0: + debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2)) + mismatches += 1 + debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num,float1,float2,relative_diff*100)) + + # Only show the first 10 mismatch lines + if not line1 and not line2 or mismatches>10: + fp1.close() + fp2.close() + return mismatches==0 + + # Never reached + return False + + + def isdiff(self,filename1,filename2): + """ This is used to compare two files and display the diff if they are different.. """ + import debug + import filecmp + import difflib + check = filecmp.cmp(filename1,filename2) + if not check: + debug.error("MISMATCH file1={0} file2={1}".format(filename1,filename2)) + f1 = open(filename1,"r") + s1 = f1.readlines().decode('utf-8') + f1.close() + f2 = open(filename2,"r").decode('utf-8') + s2 = f2.readlines() + f2.close() + mismatches=0 + for line in difflib.unified_diff(s1, s2): + mismatches += 1 + self.error("DIFF LINES:",line) + if mismatches>10: + return False + return False + else: + debug.info(2,"MATCH {0} {1}".format(filename1,filename2)) + return True def header(filename, technology): + # Skip the header for gitlab regression + import getpass + if getpass.getuser() == "gitlab-runner": + return + tst = "Running Test for:" - print "\n" - print " ______________________________________________________________________________ " - print "|==============================================================================|" - print "|=========" + tst.center(60) + "=========|" - print "|=========" + technology.center(60) + "=========|" - print "|=========" + filename.center(60) + "=========|" - import globals - OPTS = globals.get_opts() - print "|=========" + OPTS.openram_temp.center(60) + "=========|" - print "|==============================================================================|" + print("\n") + print(" ______________________________________________________________________________ ") + print("|==============================================================================|") + print("|=========" + tst.center(60) + "=========|") + print("|=========" + technology.center(60) + "=========|") + print("|=========" + filename.center(60) + "=========|") + from globals import OPTS + print("|=========" + OPTS.openram_temp.center(60) + "=========|") + print("|==============================================================================|") diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index e2a62e42..93903551 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -4,6 +4,7 @@ import debug from math import log,sqrt,ceil import datetime import getpass +import numpy as np from vector import vector from globals import OPTS, print_time @@ -169,7 +170,6 @@ class sram_1bank(sram_base): # the control logic to the bank self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, mid2_pos, control_clk_buf_pos]) - def route_vdd_gnd(self): """ Propagate all vdd/gnd pins up to this level for all modules """ @@ -186,6 +186,84 @@ class sram_1bank(sram_base): self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "gnd") + def new_route_vdd_gnd(self): + """ Propagate all vdd/gnd pins up to this level for all modules """ + + # These are the instances that every bank has + top_instances = [self.bank_inst, + self.row_addr_dff_inst, + self.data_dff_inst, + self.control_logic_inst] + if self.col_addr_dff: + top_instances.append(self.col_addr_dff_inst) + + + # for inst in top_instances: + # self.copy_layout_pin(inst, "vdd") + # self.copy_layout_pin(inst, "gnd") + + blockages=self.get_blockages("metal3", top_level=True) + + # Gather all of the vdd/gnd pins + vdd_pins=[] + gnd_pins=[] + for inst in top_instances: + vdd_pins.extend([x for x in inst.get_pins("vdd") if x.layer == "metal3"]) + gnd_pins.extend([x for x in inst.get_pins("gnd") if x.layer == "metal3"]) + + # Create candidate stripes on M3/M4 + lowest=self.find_lowest_coords() + highest=self.find_highest_coords() + m3_y_coords = np.arange(lowest[1],highest[1],self.m2_pitch) + + # These are the rails that will be available for vdd/gnd + m3_rects = [] + # These are the "inflated" shapes for DRC checks + m3_drc_rects = [] + for y in m3_y_coords: + # This is just what metal will be drawn + ll = vector(lowest[0], y - 0.5*self.m3_width) + ur = vector(highest[0], y + 0.5*self.m3_width) + m3_rects.append([ll, ur]) + # This is a full m3 pitch for DRC conflict checking + ll = vector(lowest[0], y - 0.5*self.m3_pitch ) + ur = vector(highest[0], y + 0.5*self.m3_pitch) + m3_drc_rects.append([ll, ur]) + + vdd_rects = [] + gnd_rects = [] + + # Now, figure how if the rails intersect a blockage, vdd, or gnd pin + # Divide the rails up alternately + # This should be done in less than n^2 using a kd-tree or something + # for drc_rect,rect in zip(m3_drc_rects,m3_rects): + # for b in blockages: + # if rect_overlaps(b,drc_rect): + # break + # else: + # gnd_rects.append(rect) + + + + # Create the vdd and gnd rails + for rect in m3_rects: + (ll,ur) = rect + + for rect in gnd_rects: + (ll,ur) = rect + self.add_layout_pin(text="gnd", + layer="metal3", + offset=ll, + width=ur.x-ll.x, + height=ur.y-ll.y) + for rect in vdd_rects: + (ll,ur) = rect + self.add_layout_pin(text="vdd", + layer="metal3", + offset=ll, + width=ur.x-ll.x, + height=ur.y-ll.y) + def route_control_logic(self): """ Route the outputs from the control logic module """ for n in self.control_logic_outputs: diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index b5938cec..2fab1c4e 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -13,9 +13,6 @@ class contact_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import contact diff --git a/compiler/tests/03_path_test.py b/compiler/tests/03_path_test.py index 88a195c4..f70d00be 100755 --- a/compiler/tests/03_path_test.py +++ b/compiler/tests/03_path_test.py @@ -13,9 +13,6 @@ class path_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import path import tech import design diff --git a/compiler/tests/03_ptx_1finger_nmos_test.py b/compiler/tests/03_ptx_1finger_nmos_test.py index 02ef0f2b..727c24f0 100755 --- a/compiler/tests/03_ptx_1finger_nmos_test.py +++ b/compiler/tests/03_ptx_1finger_nmos_test.py @@ -13,9 +13,6 @@ class ptx_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import ptx import tech diff --git a/compiler/tests/03_ptx_1finger_pmos_test.py b/compiler/tests/03_ptx_1finger_pmos_test.py index c00ccb72..04b9ab64 100755 --- a/compiler/tests/03_ptx_1finger_pmos_test.py +++ b/compiler/tests/03_ptx_1finger_pmos_test.py @@ -13,9 +13,6 @@ class ptx_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import ptx import tech diff --git a/compiler/tests/03_ptx_3finger_nmos_test.py b/compiler/tests/03_ptx_3finger_nmos_test.py index 60293266..20343b2e 100755 --- a/compiler/tests/03_ptx_3finger_nmos_test.py +++ b/compiler/tests/03_ptx_3finger_nmos_test.py @@ -13,9 +13,6 @@ class ptx_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import ptx import tech diff --git a/compiler/tests/03_ptx_3finger_pmos_test.py b/compiler/tests/03_ptx_3finger_pmos_test.py index 792de25f..37933702 100755 --- a/compiler/tests/03_ptx_3finger_pmos_test.py +++ b/compiler/tests/03_ptx_3finger_pmos_test.py @@ -13,9 +13,6 @@ class ptx_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import ptx import tech diff --git a/compiler/tests/03_ptx_4finger_nmos_test.py b/compiler/tests/03_ptx_4finger_nmos_test.py index d4cc0da5..09788a5e 100755 --- a/compiler/tests/03_ptx_4finger_nmos_test.py +++ b/compiler/tests/03_ptx_4finger_nmos_test.py @@ -13,9 +13,6 @@ class ptx_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import ptx import tech diff --git a/compiler/tests/03_ptx_4finger_pmos_test.py b/compiler/tests/03_ptx_4finger_pmos_test.py index c35390c9..f43d7dc7 100755 --- a/compiler/tests/03_ptx_4finger_pmos_test.py +++ b/compiler/tests/03_ptx_4finger_pmos_test.py @@ -13,9 +13,6 @@ class ptx_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import ptx import tech diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index cf50dc10..557fee5b 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -13,9 +13,6 @@ class wire_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import wire import tech import design diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index 41b2ab83..1db09553 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -18,9 +18,6 @@ class pbitcell_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import pbitcell import tech diff --git a/compiler/tests/04_pinv_10x_test.py b/compiler/tests/04_pinv_10x_test.py index 09eef9c3..3a7f846a 100755 --- a/compiler/tests/04_pinv_10x_test.py +++ b/compiler/tests/04_pinv_10x_test.py @@ -15,9 +15,6 @@ class pinv_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import pinv import tech diff --git a/compiler/tests/04_pinv_1x_beta_test.py b/compiler/tests/04_pinv_1x_beta_test.py index 17829e14..c1bb6aba 100755 --- a/compiler/tests/04_pinv_1x_beta_test.py +++ b/compiler/tests/04_pinv_1x_beta_test.py @@ -15,9 +15,6 @@ class pinv_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import pinv import tech diff --git a/compiler/tests/04_pinv_1x_test.py b/compiler/tests/04_pinv_1x_test.py index a0db1100..555aa0e5 100755 --- a/compiler/tests/04_pinv_1x_test.py +++ b/compiler/tests/04_pinv_1x_test.py @@ -14,10 +14,6 @@ class pinv_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - - import pinv import tech diff --git a/compiler/tests/04_pinv_2x_test.py b/compiler/tests/04_pinv_2x_test.py index 66ea15da..6882a719 100755 --- a/compiler/tests/04_pinv_2x_test.py +++ b/compiler/tests/04_pinv_2x_test.py @@ -15,9 +15,6 @@ class pinv_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import pinv import tech diff --git a/compiler/tests/04_pinvbuf_test.py b/compiler/tests/04_pinvbuf_test.py index 3b16c1c9..9c55ebe3 100755 --- a/compiler/tests/04_pinvbuf_test.py +++ b/compiler/tests/04_pinvbuf_test.py @@ -15,9 +15,6 @@ class pinvbuf_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import pinvbuf debug.info(2, "Testing inverter/buffer 4x 8x") diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index af35bae8..b6739e4e 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -17,9 +17,6 @@ class pnand2_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import pnand2 import tech diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index 6984a0e0..db3817f5 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -17,9 +17,6 @@ class pnand3_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import pnand3 import tech diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index a15f6907..978c03ad 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -17,9 +17,6 @@ class pnor2_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import pnor2 import tech diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index 54f3dabb..bcf11473 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -15,9 +15,6 @@ class precharge_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import precharge import tech diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_single_level_column_mux_test.py index d282a11a..01dc4445 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_single_level_column_mux_test.py @@ -17,9 +17,6 @@ class single_level_column_mux_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import single_level_column_mux import tech diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index 37649ad5..4ea5c65a 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -17,9 +17,6 @@ class array_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import bitcell_array debug.info(2, "Testing 4x4 array for 6t_cell") diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index 92e58673..1517c3b7 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -16,9 +16,6 @@ class pbitcell_array_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import bitcell_array debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell") diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index eb0b105a..ab4f5f90 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -15,9 +15,6 @@ class hierarchical_decoder_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import hierarchical_decoder import tech diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index 79db3665..80b95b46 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -15,9 +15,6 @@ class hierarchical_predecode2x4_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import hierarchical_predecode2x4 as pre import tech diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index b6609829..0974ced3 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -15,9 +15,6 @@ class hierarchical_predecode3x8_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import hierarchical_predecode3x8 as pre import tech diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_single_level_column_mux_array_test.py index 1becd179..ea231292 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_single_level_column_mux_array_test.py @@ -14,9 +14,6 @@ class single_level_column_mux_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import single_level_column_mux_array debug.info(1, "Testing sample for 2-way column_mux_array") diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index 236d8cf1..9592b6c5 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -15,9 +15,6 @@ class precharge_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import precharge_array import tech diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/08_wordline_driver_test.py index af97f018..9c91c7dd 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/08_wordline_driver_test.py @@ -17,9 +17,6 @@ class wordline_driver_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import wordline_driver import tech diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index ba483218..51620495 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -15,9 +15,6 @@ class sense_amp_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import sense_amp_array diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py index d7f1f7ec..27538d0b 100755 --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -15,9 +15,6 @@ class write_driver_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import write_driver_array debug.info(2, "Testing write_driver_array for columns=8, word_size=8") diff --git a/compiler/tests/11_dff_array_test.py b/compiler/tests/11_dff_array_test.py index e85056e9..a55c6407 100755 --- a/compiler/tests/11_dff_array_test.py +++ b/compiler/tests/11_dff_array_test.py @@ -15,9 +15,6 @@ class dff_array_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import dff_array debug.info(2, "Testing dff_array for 3x3") diff --git a/compiler/tests/11_dff_buf_array_test.py b/compiler/tests/11_dff_buf_array_test.py index 6c40e447..f0b75552 100755 --- a/compiler/tests/11_dff_buf_array_test.py +++ b/compiler/tests/11_dff_buf_array_test.py @@ -15,9 +15,6 @@ class dff_buf_array_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import dff_buf_array debug.info(2, "Testing dff_buf_array for 3x3") diff --git a/compiler/tests/11_dff_buf_test.py b/compiler/tests/11_dff_buf_test.py index 44aca54c..f434f768 100755 --- a/compiler/tests/11_dff_buf_test.py +++ b/compiler/tests/11_dff_buf_test.py @@ -15,9 +15,6 @@ class dff_buf_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import dff_buf debug.info(2, "Testing dff_buf 4x 8x") diff --git a/compiler/tests/11_dff_inv_array_test.py b/compiler/tests/11_dff_inv_array_test.py index 3d2b8cac..2196a3f2 100755 --- a/compiler/tests/11_dff_inv_array_test.py +++ b/compiler/tests/11_dff_inv_array_test.py @@ -15,9 +15,6 @@ class dff_inv_array_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import dff_inv_array debug.info(2, "Testing dff_inv_array for 3x3") diff --git a/compiler/tests/11_dff_inv_test.py b/compiler/tests/11_dff_inv_test.py index b09a6591..43d49246 100755 --- a/compiler/tests/11_dff_inv_test.py +++ b/compiler/tests/11_dff_inv_test.py @@ -15,9 +15,6 @@ class dff_inv_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import dff_inv debug.info(2, "Testing dff_inv 4x") diff --git a/compiler/tests/11_ms_flop_array_test.py b/compiler/tests/11_ms_flop_array_test.py index 97ef6ece..d6472a15 100755 --- a/compiler/tests/11_ms_flop_array_test.py +++ b/compiler/tests/11_ms_flop_array_test.py @@ -15,9 +15,6 @@ class dff_array_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import ms_flop_array debug.info(2, "Testing ms_flop_array for columns=8, word_size=8") diff --git a/compiler/tests/12_tri_gate_array_test.py b/compiler/tests/12_tri_gate_array_test.py index 08d1596d..4f9cfa3e 100755 --- a/compiler/tests/12_tri_gate_array_test.py +++ b/compiler/tests/12_tri_gate_array_test.py @@ -15,9 +15,6 @@ class tri_gate_array_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import tri_gate_array debug.info(1, "Testing tri_gate_array for columns=8, word_size=8") diff --git a/compiler/tests/13_delay_chain_test.py b/compiler/tests/13_delay_chain_test.py index d8a2d67c..2cc745c2 100755 --- a/compiler/tests/13_delay_chain_test.py +++ b/compiler/tests/13_delay_chain_test.py @@ -15,9 +15,6 @@ class delay_chain_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import delay_chain debug.info(2, "Testing delay_chain") diff --git a/compiler/tests/14_replica_bitline_test.py b/compiler/tests/14_replica_bitline_test.py index b393c136..6ecd612a 100755 --- a/compiler/tests/14_replica_bitline_test.py +++ b/compiler/tests/14_replica_bitline_test.py @@ -15,9 +15,6 @@ class replica_bitline_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import replica_bitline stages=4 diff --git a/compiler/tests/16_control_logic_test.py b/compiler/tests/16_control_logic_test.py index 7aeb54c1..3227a425 100755 --- a/compiler/tests/16_control_logic_test.py +++ b/compiler/tests/16_control_logic_test.py @@ -15,9 +15,6 @@ class control_logic_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import control_logic import tech diff --git a/compiler/tests/19_bank_select_test.py b/compiler/tests/19_bank_select_test.py index 955c7b4e..0f4cb05f 100755 --- a/compiler/tests/19_bank_select_test.py +++ b/compiler/tests/19_bank_select_test.py @@ -15,9 +15,6 @@ class bank_select_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - import bank_select debug.info(1, "No column mux") diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index e7e6c321..2169dcf1 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -15,9 +15,6 @@ class multi_bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - from bank import bank debug.info(1, "No column mux") diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 697051ee..25567e82 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -15,15 +15,12 @@ class single_bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - from bank import bank debug.info(1, "No column mux") a = bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1_single") self.local_check(a) - + debug.info(1, "Two way column mux") a = bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2_single") self.local_check(a) diff --git a/compiler/tests/20_sram_1bank_test.py b/compiler/tests/20_sram_1bank_test.py index 8b83f54a..7079b715 100755 --- a/compiler/tests/20_sram_1bank_test.py +++ b/compiler/tests/20_sram_1bank_test.py @@ -15,26 +15,23 @@ class sram_1bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - from sram import sram debug.info(1, "Single bank, no column mux with control logic") a = sram(word_size=4, num_words=16, num_banks=1, name="sram1") self.local_check(a, final_verification=True) - debug.info(1, "Single bank two way column mux with control logic") - a = sram(word_size=4, num_words=32, num_banks=1, name="sram2") - self.local_check(a, final_verification=True) + # debug.info(1, "Single bank two way column mux with control logic") + # a = sram(word_size=4, num_words=32, num_banks=1, name="sram2") + # self.local_check(a, final_verification=True) - debug.info(1, "Single bank, four way column mux with control logic") - a = sram(word_size=4, num_words=64, num_banks=1, name="sram3") - self.local_check(a, final_verification=True) + # debug.info(1, "Single bank, four way column mux with control logic") + # a = sram(word_size=4, num_words=64, num_banks=1, name="sram3") + # self.local_check(a, final_verification=True) - debug.info(1, "Single bank, eight way column mux with control logic") - a = sram(word_size=2, num_words=128, num_banks=1, name="sram4") - self.local_check(a, final_verification=True) + # debug.info(1, "Single bank, eight way column mux with control logic") + # a = sram(word_size=2, num_words=128, num_banks=1, name="sram4") + # self.local_check(a, final_verification=True) globals.end_openram() diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index 993cae93..00be14df 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -16,9 +16,6 @@ class sram_2bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - from sram import sram debug.info(1, "Two bank, no column mux with control logic") diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py index b94b660a..e4a28090 100755 --- a/compiler/tests/20_sram_4bank_test.py +++ b/compiler/tests/20_sram_4bank_test.py @@ -16,9 +16,6 @@ class sram_4bank_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify - import verify - from sram import sram debug.info(1, "Four bank, no column mux with control logic") diff --git a/compiler/tests/22_pex_test.py b/compiler/tests/22_pex_test.py index 24cb7733..7755a2c7 100755 --- a/compiler/tests/22_pex_test.py +++ b/compiler/tests/22_pex_test.py @@ -27,9 +27,6 @@ class sram_func_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - global verify - import verify - self.func_test(bank_num=1) self.func_test(bank_num=2) self.func_test(bank_num=4) diff --git a/compiler/tests/out.log b/compiler/tests/out.log new file mode 100644 index 00000000..e69de29b diff --git a/compiler/tests/sram1.gds b/compiler/tests/sram1.gds new file mode 100644 index 00000000..d6ed28eb Binary files /dev/null and b/compiler/tests/sram1.gds differ diff --git a/compiler/tests/sram1.lef b/compiler/tests/sram1.lef new file mode 100644 index 00000000..c0563063 --- /dev/null +++ b/compiler/tests/sram1.lef @@ -0,0 +1,19 @@ +VERSION 5.4 ; +NAMESCASESENSITIVE ON ; +BUSBITCHARS "[]" ; +DIVIDERCHAR "/" ; +UNITS + DATABASE MICRONS 1000 ; +END UNITS +SITE MacroSite + CLASS Core ; + SIZE 324000.0 by 421500.0 ; +END MacroSite +MACRO sram1 + CLASS BLOCK ; + SIZE 324000.0 BY 421500.0 ; + SYMMETRY X Y R90 ; + SITE MacroSite ; + PIN DIN[0] + DIRECTION INPUT ; + PORT diff --git a/compiler/tests/sram1.sp b/compiler/tests/sram1.sp new file mode 100644 index 00000000..622af672 --- /dev/null +++ b/compiler/tests/sram1.sp @@ -0,0 +1,602 @@ +************************************************** +* OpenRAM generated memory. +* Words: 16 +* Data bits: 4 +* Banks: 1 +* Column mux: 1:1 +************************************************** +* Positive edge-triggered FF +.subckt dff D Q clk vdd gnd +M0 vdd clk a_2_6# vdd p w=12u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M1 a_17_74# D vdd vdd p w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M2 a_22_6# clk a_17_74# vdd p w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M3 a_31_74# a_2_6# a_22_6# vdd p w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M4 vdd a_34_4# a_31_74# vdd p w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M5 a_34_4# a_22_6# vdd vdd p w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M6 a_61_74# a_34_4# vdd vdd p w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M7 a_66_6# a_2_6# a_61_74# vdd p w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M8 a_76_84# clk a_66_6# vdd p w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M9 vdd Q a_76_84# vdd p w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M10 gnd clk a_2_6# gnd n w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M11 Q a_66_6# vdd vdd p w=12u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M12 a_17_6# D gnd gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M13 a_22_6# a_2_6# a_17_6# gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M14 a_31_6# clk a_22_6# gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M15 gnd a_34_4# a_31_6# gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M16 a_34_4# a_22_6# gnd gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M17 a_61_6# a_34_4# gnd gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M18 a_66_6# clk a_61_6# gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M19 a_76_6# a_2_6# a_66_6# gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M20 gnd Q a_76_6# gnd n w=3u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +M21 Q a_66_6# gnd gnd n w=6u l=0.6u ++ ad=0p pd=0u as=0p ps=0u +.ends dff + +* ptx M{0} {1} n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p + +* ptx M{0} {1} p m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p + +.SUBCKT pinv_2 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p +Mpinv_nmos Z A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS pinv_2 + +.SUBCKT dff_inv_2 D Q Qb clk vdd gnd +Xdff_inv_dff D Q clk vdd gnd dff +Xdff_inv_inv1 Q Qb vdd gnd pinv_2 +.ENDS dff_inv_2 + +.SUBCKT dff_array_3x1 din[0] din[1] din[2] dout[0] dout_bar[0] dout[1] dout_bar[1] dout[2] dout_bar[2] clk vdd gnd +XXdff_r0_c0 din[0] dout[0] dout_bar[0] clk vdd gnd dff_inv_2 +XXdff_r1_c0 din[1] dout[1] dout_bar[1] clk vdd gnd dff_inv_2 +XXdff_r2_c0 din[2] dout[2] dout_bar[2] clk vdd gnd dff_inv_2 +.ENDS dff_array_3x1 + +* ptx M{0} {1} p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p + +.SUBCKT pnand2_1 A B Z vdd gnd +Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS pnand2_1 + +.SUBCKT pnand3_1 A B C Z vdd gnd +Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS pnand3_1 + +* ptx M{0} {1} n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p + +.SUBCKT pinv_3 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_3 + +* ptx M{0} {1} n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p + +* ptx M{0} {1} p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p + +.SUBCKT pinv_4 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p +Mpinv_nmos Z A gnd gnd n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p +.ENDS pinv_4 + +* ptx M{0} {1} n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p + +* ptx M{0} {1} p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p + +.SUBCKT pinv_5 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p +Mpinv_nmos Z A gnd gnd n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p +.ENDS pinv_5 + +.SUBCKT pinvbuf_4_16 A Zb Z vdd gnd +Xbuf_inv1 A zb_int vdd gnd pinv_3 +Xbuf_inv2 zb_int z_int vdd gnd pinv_4 +Xbuf_inv3 z_int Zb vdd gnd pinv_5 +Xbuf_inv4 zb_int Z vdd gnd pinv_5 +.ENDS pinvbuf_4_16 + +.SUBCKT pinv_6 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_6 + +.SUBCKT pinv_7 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p +Mpinv_nmos Z A gnd gnd n m=1 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p +.ENDS pinv_7 + +.SUBCKT pinv_8 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=4 w=9.6u l=0.6u pd=20.4u ps=20.4u as=14.399999999999999p ad=14.399999999999999p +Mpinv_nmos Z A gnd gnd n m=4 w=4.8u l=0.6u pd=10.799999999999999u ps=10.799999999999999u as=7.199999999999999p ad=7.199999999999999p +.ENDS pinv_8 + +*********************** "cell_6t" ****************************** +.SUBCKT replica_cell_6t bl br wl vdd gnd +M_1 gnd net_2 vdd vdd p W='0.9u' L=1.2u +M_2 net_2 gnd vdd vdd p W='0.9u' L=1.2u +M_3 br wl net_2 gnd n W='1.2u' L=0.6u +M_4 bl wl gnd gnd n W='1.2u' L=0.6u +M_5 net_2 gnd gnd gnd n W='2.4u' L=0.6u +M_6 gnd net_2 gnd gnd n W='2.4u' L=0.6u +.ENDS $ replica_cell_6t + +*********************** "cell_6t" ****************************** +.SUBCKT cell_6t bl br wl vdd gnd +M_1 net_1 net_2 vdd vdd p W='0.9u' L=1.2u +M_2 net_2 net_1 vdd vdd p W='0.9u' L=1.2u +M_3 br wl net_2 gnd n W='1.2u' L=0.6u +M_4 bl wl net_1 gnd n W='1.2u' L=0.6u +M_5 net_2 net_1 gnd gnd n W='2.4u' L=0.6u +M_6 net_1 net_2 gnd gnd n W='2.4u' L=0.6u +.ENDS $ cell_6t + +.SUBCKT bitline_load bl[0] br[0] wl[0] wl[1] wl[2] wl[3] vdd gnd +Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t +Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t +Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t +Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t +.ENDS bitline_load + +.SUBCKT pinv_9 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_9 + +.SUBCKT delay_chain in out vdd gnd +Xdinv0 in dout_1 vdd gnd pinv_9 +Xdload_0_0 dout_1 n_0_0 vdd gnd pinv_9 +Xdload_0_1 dout_1 n_0_1 vdd gnd pinv_9 +Xdload_0_2 dout_1 n_0_2 vdd gnd pinv_9 +Xdinv1 dout_1 dout_2 vdd gnd pinv_9 +Xdload_1_0 dout_2 n_1_0 vdd gnd pinv_9 +Xdload_1_1 dout_2 n_1_1 vdd gnd pinv_9 +Xdload_1_2 dout_2 n_1_2 vdd gnd pinv_9 +Xdinv2 dout_2 out vdd gnd pinv_9 +Xdload_2_0 out n_2_0 vdd gnd pinv_9 +Xdload_2_1 out n_2_1 vdd gnd pinv_9 +Xdload_2_2 out n_2_2 vdd gnd pinv_9 +.ENDS delay_chain + +.SUBCKT pinv_10 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_10 + +* ptx M{0} {1} p m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p + +.SUBCKT replica_bitline en out vdd gnd +Xrbl_inv bl[0] out vdd gnd pinv_10 +Mrbl_access_tx vdd delayed_en bl[0] vdd p m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +Xdelay_chain en delayed_en vdd gnd delay_chain +Xbitcell bl[0] br[0] delayed_en vdd gnd replica_cell_6t +Xload bl[0] br[0] gnd gnd gnd gnd vdd gnd bitline_load +.ENDS replica_bitline + +.SUBCKT control_logic csb web oeb clk s_en w_en tri_en tri_en_bar clk_buf_bar clk_buf vdd gnd +Xctrl_dffs csb web oeb cs_bar cs we_bar we oe_bar oe clk_buf vdd gnd dff_array_3x1 +Xclkbuf clk clk_buf_bar clk_buf vdd gnd pinvbuf_4_16 +Xnand3_w_en_bar clk_buf_bar cs we w_en_bar vdd gnd pnand3_1 +Xinv_pre_w_en w_en_bar pre_w_en vdd gnd pinv_6 +Xinv_pre_w_en_bar pre_w_en pre_w_en_bar vdd gnd pinv_7 +Xinv_w_en2 pre_w_en_bar w_en vdd gnd pinv_8 +Xinv_tri_en1 pre_tri_en_bar pre_tri_en1 vdd gnd pinv_7 +Xtri_en_buf1 pre_tri_en1 pre_tri_en_bar1 vdd gnd pinv_7 +Xtri_en_buf2 pre_tri_en_bar1 tri_en vdd gnd pinv_8 +Xnand2_tri_en clk_buf_bar oe pre_tri_en_bar vdd gnd pnand2_1 +Xtri_en_bar_buf1 pre_tri_en_bar pre_tri_en2 vdd gnd pinv_7 +Xtri_en_bar_buf2 pre_tri_en2 tri_en_bar vdd gnd pinv_8 +Xnand3_rblk_bar clk_buf_bar oe cs rblk_bar vdd gnd pnand3_1 +Xinv_rblk rblk_bar rblk vdd gnd pinv_6 +Xinv_s_en pre_s_en_bar s_en vdd gnd pinv_8 +Xinv_pre_s_en_bar pre_s_en pre_s_en_bar vdd gnd pinv_7 +Xreplica_bitline rblk pre_s_en vdd gnd replica_bitline +.ENDS control_logic + +.SUBCKT dff_array din[0] din[1] din[2] din[3] dout[0] dout[1] dout[2] dout[3] clk vdd gnd +XXdff_r0_c0 din[0] dout[0] clk vdd gnd dff +XXdff_r1_c0 din[1] dout[1] clk vdd gnd dff +XXdff_r2_c0 din[2] dout[2] clk vdd gnd dff +XXdff_r3_c0 din[3] dout[3] clk vdd gnd dff +.ENDS dff_array + +.SUBCKT bitcell_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd +Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t +Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t +Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t +Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t +Xbit_r4_c0 bl[0] br[0] wl[4] vdd gnd cell_6t +Xbit_r5_c0 bl[0] br[0] wl[5] vdd gnd cell_6t +Xbit_r6_c0 bl[0] br[0] wl[6] vdd gnd cell_6t +Xbit_r7_c0 bl[0] br[0] wl[7] vdd gnd cell_6t +Xbit_r8_c0 bl[0] br[0] wl[8] vdd gnd cell_6t +Xbit_r9_c0 bl[0] br[0] wl[9] vdd gnd cell_6t +Xbit_r10_c0 bl[0] br[0] wl[10] vdd gnd cell_6t +Xbit_r11_c0 bl[0] br[0] wl[11] vdd gnd cell_6t +Xbit_r12_c0 bl[0] br[0] wl[12] vdd gnd cell_6t +Xbit_r13_c0 bl[0] br[0] wl[13] vdd gnd cell_6t +Xbit_r14_c0 bl[0] br[0] wl[14] vdd gnd cell_6t +Xbit_r15_c0 bl[0] br[0] wl[15] vdd gnd cell_6t +Xbit_r0_c1 bl[1] br[1] wl[0] vdd gnd cell_6t +Xbit_r1_c1 bl[1] br[1] wl[1] vdd gnd cell_6t +Xbit_r2_c1 bl[1] br[1] wl[2] vdd gnd cell_6t +Xbit_r3_c1 bl[1] br[1] wl[3] vdd gnd cell_6t +Xbit_r4_c1 bl[1] br[1] wl[4] vdd gnd cell_6t +Xbit_r5_c1 bl[1] br[1] wl[5] vdd gnd cell_6t +Xbit_r6_c1 bl[1] br[1] wl[6] vdd gnd cell_6t +Xbit_r7_c1 bl[1] br[1] wl[7] vdd gnd cell_6t +Xbit_r8_c1 bl[1] br[1] wl[8] vdd gnd cell_6t +Xbit_r9_c1 bl[1] br[1] wl[9] vdd gnd cell_6t +Xbit_r10_c1 bl[1] br[1] wl[10] vdd gnd cell_6t +Xbit_r11_c1 bl[1] br[1] wl[11] vdd gnd cell_6t +Xbit_r12_c1 bl[1] br[1] wl[12] vdd gnd cell_6t +Xbit_r13_c1 bl[1] br[1] wl[13] vdd gnd cell_6t +Xbit_r14_c1 bl[1] br[1] wl[14] vdd gnd cell_6t +Xbit_r15_c1 bl[1] br[1] wl[15] vdd gnd cell_6t +Xbit_r0_c2 bl[2] br[2] wl[0] vdd gnd cell_6t +Xbit_r1_c2 bl[2] br[2] wl[1] vdd gnd cell_6t +Xbit_r2_c2 bl[2] br[2] wl[2] vdd gnd cell_6t +Xbit_r3_c2 bl[2] br[2] wl[3] vdd gnd cell_6t +Xbit_r4_c2 bl[2] br[2] wl[4] vdd gnd cell_6t +Xbit_r5_c2 bl[2] br[2] wl[5] vdd gnd cell_6t +Xbit_r6_c2 bl[2] br[2] wl[6] vdd gnd cell_6t +Xbit_r7_c2 bl[2] br[2] wl[7] vdd gnd cell_6t +Xbit_r8_c2 bl[2] br[2] wl[8] vdd gnd cell_6t +Xbit_r9_c2 bl[2] br[2] wl[9] vdd gnd cell_6t +Xbit_r10_c2 bl[2] br[2] wl[10] vdd gnd cell_6t +Xbit_r11_c2 bl[2] br[2] wl[11] vdd gnd cell_6t +Xbit_r12_c2 bl[2] br[2] wl[12] vdd gnd cell_6t +Xbit_r13_c2 bl[2] br[2] wl[13] vdd gnd cell_6t +Xbit_r14_c2 bl[2] br[2] wl[14] vdd gnd cell_6t +Xbit_r15_c2 bl[2] br[2] wl[15] vdd gnd cell_6t +Xbit_r0_c3 bl[3] br[3] wl[0] vdd gnd cell_6t +Xbit_r1_c3 bl[3] br[3] wl[1] vdd gnd cell_6t +Xbit_r2_c3 bl[3] br[3] wl[2] vdd gnd cell_6t +Xbit_r3_c3 bl[3] br[3] wl[3] vdd gnd cell_6t +Xbit_r4_c3 bl[3] br[3] wl[4] vdd gnd cell_6t +Xbit_r5_c3 bl[3] br[3] wl[5] vdd gnd cell_6t +Xbit_r6_c3 bl[3] br[3] wl[6] vdd gnd cell_6t +Xbit_r7_c3 bl[3] br[3] wl[7] vdd gnd cell_6t +Xbit_r8_c3 bl[3] br[3] wl[8] vdd gnd cell_6t +Xbit_r9_c3 bl[3] br[3] wl[9] vdd gnd cell_6t +Xbit_r10_c3 bl[3] br[3] wl[10] vdd gnd cell_6t +Xbit_r11_c3 bl[3] br[3] wl[11] vdd gnd cell_6t +Xbit_r12_c3 bl[3] br[3] wl[12] vdd gnd cell_6t +Xbit_r13_c3 bl[3] br[3] wl[13] vdd gnd cell_6t +Xbit_r14_c3 bl[3] br[3] wl[14] vdd gnd cell_6t +Xbit_r15_c3 bl[3] br[3] wl[15] vdd gnd cell_6t +.ENDS bitcell_array + +* ptx M{0} {1} p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p + +.SUBCKT precharge bl br en vdd +Mlower_pmos bl en br vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mupper_pmos1 bl en vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mupper_pmos2 br en vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS precharge + +.SUBCKT precharge_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] en vdd +Xpre_column_0 bl[0] br[0] en vdd precharge +Xpre_column_1 bl[1] br[1] en vdd precharge +Xpre_column_2 bl[2] br[2] en vdd precharge +Xpre_column_3 bl[3] br[3] en vdd precharge +.ENDS precharge_array +*********************** "sense_amp" ****************************** + +.SUBCKT sense_amp bl br dout en vdd gnd +M_1 dout net_1 vdd vdd p W='5.4*1u' L=0.6u +M_2 dout net_1 net_2 gnd n W='2.7*1u' L=0.6u +M_3 net_1 dout vdd vdd p W='5.4*1u' L=0.6u +M_4 net_1 dout net_2 gnd n W='2.7*1u' L=0.6u +M_5 bl en dout vdd p W='7.2*1u' L=0.6u +M_6 br en net_1 vdd p W='7.2*1u' L=0.6u +M_7 net_2 en gnd gnd n W='2.7*1u' L=0.6u +.ENDS sense_amp + + +.SUBCKT sense_amp_array data[0] bl[0] br[0] data[1] bl[1] br[1] data[2] bl[2] br[2] data[3] bl[3] br[3] en vdd gnd +Xsa_d0 bl[0] br[0] data[0] en vdd gnd sense_amp +Xsa_d1 bl[1] br[1] data[1] en vdd gnd sense_amp +Xsa_d2 bl[2] br[2] data[2] en vdd gnd sense_amp +Xsa_d3 bl[3] br[3] data[3] en vdd gnd sense_amp +.ENDS sense_amp_array +*********************** Write_Driver ****************************** +.SUBCKT write_driver din bl br en vdd gnd + +**** Inverter to conver Data_in to data_in_bar ****** +M_1 net_3 din gnd gnd n W='1.2*1u' L=0.6u +M_2 net_3 din vdd vdd p W='2.1*1u' L=0.6u + +**** 2input nand gate follwed by inverter to drive BL ****** +M_3 net_2 en net_7 gnd n W='2.1*1u' L=0.6u +M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u +M_5 net_2 en vdd vdd p W='2.1*1u' L=0.6u +M_6 net_2 din vdd vdd p W='2.1*1u' L=0.6u + + +M_7 net_1 net_2 vdd vdd p W='2.1*1u' L=0.6u +M_8 net_1 net_2 gnd gnd n W='1.2*1u' L=0.6u + +**** 2input nand gate follwed by inverter to drive BR****** + +M_9 net_4 en vdd vdd p W='2.1*1u' L=0.6u +M_10 net_4 en net_8 gnd n W='2.1*1u' L=0.6u +M_11 net_8 net_3 gnd gnd n W='2.1*1u' L=0.6u +M_12 net_4 net_3 vdd vdd p W='2.1*1u' L=0.6u + +M_13 net_6 net_4 vdd vdd p W='2.1*1u' L=0.6u +M_14 net_6 net_4 gnd gnd n W='1.2*1u' L=0.6u + +************************************************ + +M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u +M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u +M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u + + + +.ENDS $ write_driver + + +.SUBCKT write_driver_array data[0] data[1] data[2] data[3] bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] en vdd gnd +XXwrite_driver0 data[0] bl[0] br[0] en vdd gnd write_driver +XXwrite_driver1 data[1] bl[1] br[1] en vdd gnd write_driver +XXwrite_driver2 data[2] bl[2] br[2] en vdd gnd write_driver +XXwrite_driver3 data[3] bl[3] br[3] en vdd gnd write_driver +.ENDS write_driver_array + +.SUBCKT pinv_11 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_11 + +.SUBCKT pnand2_2 A B Z vdd gnd +Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS pnand2_2 + +.SUBCKT pnand3_2 A B C Z vdd gnd +Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS pnand3_2 + +.SUBCKT pinv_12 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_12 + +.SUBCKT pnand2_3 A B Z vdd gnd +Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS pnand2_3 + +.SUBCKT pre2x4 in[0] in[1] out[0] out[1] out[2] out[3] vdd gnd +XXpre_inv[0] in[0] inbar[0] vdd gnd pinv_12 +XXpre_inv[1] in[1] inbar[1] vdd gnd pinv_12 +XXpre_nand_inv[0] Z[0] out[0] vdd gnd pinv_12 +XXpre_nand_inv[1] Z[1] out[1] vdd gnd pinv_12 +XXpre_nand_inv[2] Z[2] out[2] vdd gnd pinv_12 +XXpre_nand_inv[3] Z[3] out[3] vdd gnd pinv_12 +XXpre2x4_nand[0] inbar[0] inbar[1] Z[0] vdd gnd pnand2_3 +XXpre2x4_nand[1] in[0] inbar[1] Z[1] vdd gnd pnand2_3 +XXpre2x4_nand[2] inbar[0] in[1] Z[2] vdd gnd pnand2_3 +XXpre2x4_nand[3] in[0] in[1] Z[3] vdd gnd pnand2_3 +.ENDS pre2x4 + +.SUBCKT pinv_13 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_13 + +.SUBCKT pnand3_3 A B C Z vdd gnd +Mpnand3_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_pmos3 Z C vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos1 Z C net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos2 net1 B net2 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand3_nmos3 net2 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS pnand3_3 + +.SUBCKT pre3x8 in[0] in[1] in[2] out[0] out[1] out[2] out[3] out[4] out[5] out[6] out[7] vdd gnd +XXpre_inv[0] in[0] inbar[0] vdd gnd pinv_13 +XXpre_inv[1] in[1] inbar[1] vdd gnd pinv_13 +XXpre_inv[2] in[2] inbar[2] vdd gnd pinv_13 +XXpre_nand_inv[0] Z[0] out[0] vdd gnd pinv_13 +XXpre_nand_inv[1] Z[1] out[1] vdd gnd pinv_13 +XXpre_nand_inv[2] Z[2] out[2] vdd gnd pinv_13 +XXpre_nand_inv[3] Z[3] out[3] vdd gnd pinv_13 +XXpre_nand_inv[4] Z[4] out[4] vdd gnd pinv_13 +XXpre_nand_inv[5] Z[5] out[5] vdd gnd pinv_13 +XXpre_nand_inv[6] Z[6] out[6] vdd gnd pinv_13 +XXpre_nand_inv[7] Z[7] out[7] vdd gnd pinv_13 +XXpre3x8_nand[0] inbar[0] inbar[1] inbar[2] Z[0] vdd gnd pnand3_3 +XXpre3x8_nand[1] in[0] inbar[1] inbar[2] Z[1] vdd gnd pnand3_3 +XXpre3x8_nand[2] inbar[0] in[1] inbar[2] Z[2] vdd gnd pnand3_3 +XXpre3x8_nand[3] in[0] in[1] inbar[2] Z[3] vdd gnd pnand3_3 +XXpre3x8_nand[4] inbar[0] inbar[1] in[2] Z[4] vdd gnd pnand3_3 +XXpre3x8_nand[5] in[0] inbar[1] in[2] Z[5] vdd gnd pnand3_3 +XXpre3x8_nand[6] inbar[0] in[1] in[2] Z[6] vdd gnd pnand3_3 +XXpre3x8_nand[7] in[0] in[1] in[2] Z[7] vdd gnd pnand3_3 +.ENDS pre3x8 + +.SUBCKT hierarchical_decoder_16rows A[0] A[1] A[2] A[3] decode[0] decode[1] decode[2] decode[3] decode[4] decode[5] decode[6] decode[7] decode[8] decode[9] decode[10] decode[11] decode[12] decode[13] decode[14] decode[15] vdd gnd +Xpre[0] A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd pre2x4 +Xpre[1] A[2] A[3] out[4] out[5] out[6] out[7] vdd gnd pre2x4 +XDEC_NAND[0] out[0] out[4] Z[0] vdd gnd pnand2_2 +XDEC_NAND[1] out[0] out[5] Z[1] vdd gnd pnand2_2 +XDEC_NAND[2] out[0] out[6] Z[2] vdd gnd pnand2_2 +XDEC_NAND[3] out[0] out[7] Z[3] vdd gnd pnand2_2 +XDEC_NAND[4] out[1] out[4] Z[4] vdd gnd pnand2_2 +XDEC_NAND[5] out[1] out[5] Z[5] vdd gnd pnand2_2 +XDEC_NAND[6] out[1] out[6] Z[6] vdd gnd pnand2_2 +XDEC_NAND[7] out[1] out[7] Z[7] vdd gnd pnand2_2 +XDEC_NAND[8] out[2] out[4] Z[8] vdd gnd pnand2_2 +XDEC_NAND[9] out[2] out[5] Z[9] vdd gnd pnand2_2 +XDEC_NAND[10] out[2] out[6] Z[10] vdd gnd pnand2_2 +XDEC_NAND[11] out[2] out[7] Z[11] vdd gnd pnand2_2 +XDEC_NAND[12] out[3] out[4] Z[12] vdd gnd pnand2_2 +XDEC_NAND[13] out[3] out[5] Z[13] vdd gnd pnand2_2 +XDEC_NAND[14] out[3] out[6] Z[14] vdd gnd pnand2_2 +XDEC_NAND[15] out[3] out[7] Z[15] vdd gnd pnand2_2 +XDEC_INV_[0] Z[0] decode[0] vdd gnd pinv_11 +XDEC_INV_[1] Z[1] decode[1] vdd gnd pinv_11 +XDEC_INV_[2] Z[2] decode[2] vdd gnd pinv_11 +XDEC_INV_[3] Z[3] decode[3] vdd gnd pinv_11 +XDEC_INV_[4] Z[4] decode[4] vdd gnd pinv_11 +XDEC_INV_[5] Z[5] decode[5] vdd gnd pinv_11 +XDEC_INV_[6] Z[6] decode[6] vdd gnd pinv_11 +XDEC_INV_[7] Z[7] decode[7] vdd gnd pinv_11 +XDEC_INV_[8] Z[8] decode[8] vdd gnd pinv_11 +XDEC_INV_[9] Z[9] decode[9] vdd gnd pinv_11 +XDEC_INV_[10] Z[10] decode[10] vdd gnd pinv_11 +XDEC_INV_[11] Z[11] decode[11] vdd gnd pinv_11 +XDEC_INV_[12] Z[12] decode[12] vdd gnd pinv_11 +XDEC_INV_[13] Z[13] decode[13] vdd gnd pinv_11 +XDEC_INV_[14] Z[14] decode[14] vdd gnd pinv_11 +XDEC_INV_[15] Z[15] decode[15] vdd gnd pinv_11 +.ENDS hierarchical_decoder_16rows +*********************** tri_gate ****************************** + +.SUBCKT tri_gate in out en en_bar vdd gnd + +M_1 net_2 in_inv gnd gnd n W='1.2*1u' L=0.6u +M_2 net_3 in_inv vdd vdd p W='2.4*1u' L=0.6u +M_3 out en_bar net_3 vdd p W='2.4*1u' L=0.6u +M_4 out en net_2 gnd n W='1.2*1u' L=0.6u +M_5 in_inv in vdd vdd p W='2.4*1u' L=0.6u +M_6 in_inv in gnd gnd n W='1.2*1u' L=0.6u + + +.ENDS + +.SUBCKT tri_gate_array in[0] in[1] in[2] in[3] out[0] out[1] out[2] out[3] en en_bar vdd gnd +XXtri_gate0 in[0] out[0] en en_bar vdd gnd tri_gate +XXtri_gate1 in[1] out[1] en en_bar vdd gnd tri_gate +XXtri_gate2 in[2] out[2] en en_bar vdd gnd tri_gate +XXtri_gate3 in[3] out[3] en en_bar vdd gnd tri_gate +.ENDS tri_gate_array + +.SUBCKT pinv_14 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_14 + +.SUBCKT pinv_15 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_15 + +.SUBCKT pnand2_4 A B Z vdd gnd +Mpnand2_pmos1 vdd A Z vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_pmos2 Z B vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_nmos1 Z B net1 gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpnand2_nmos2 net1 A gnd gnd n m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +.ENDS pnand2_4 + +.SUBCKT wordline_driver in[0] in[1] in[2] in[3] in[4] in[5] in[6] in[7] in[8] in[9] in[10] in[11] in[12] in[13] in[14] in[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] en vdd gnd +Xwl_driver_inv_en0 en en_bar[0] vdd gnd pinv_15 +Xwl_driver_nand0 en_bar[0] in[0] net[0] vdd gnd pnand2_4 +Xwl_driver_inv0 net[0] wl[0] vdd gnd pinv_14 +Xwl_driver_inv_en1 en en_bar[1] vdd gnd pinv_15 +Xwl_driver_nand1 en_bar[1] in[1] net[1] vdd gnd pnand2_4 +Xwl_driver_inv1 net[1] wl[1] vdd gnd pinv_14 +Xwl_driver_inv_en2 en en_bar[2] vdd gnd pinv_15 +Xwl_driver_nand2 en_bar[2] in[2] net[2] vdd gnd pnand2_4 +Xwl_driver_inv2 net[2] wl[2] vdd gnd pinv_14 +Xwl_driver_inv_en3 en en_bar[3] vdd gnd pinv_15 +Xwl_driver_nand3 en_bar[3] in[3] net[3] vdd gnd pnand2_4 +Xwl_driver_inv3 net[3] wl[3] vdd gnd pinv_14 +Xwl_driver_inv_en4 en en_bar[4] vdd gnd pinv_15 +Xwl_driver_nand4 en_bar[4] in[4] net[4] vdd gnd pnand2_4 +Xwl_driver_inv4 net[4] wl[4] vdd gnd pinv_14 +Xwl_driver_inv_en5 en en_bar[5] vdd gnd pinv_15 +Xwl_driver_nand5 en_bar[5] in[5] net[5] vdd gnd pnand2_4 +Xwl_driver_inv5 net[5] wl[5] vdd gnd pinv_14 +Xwl_driver_inv_en6 en en_bar[6] vdd gnd pinv_15 +Xwl_driver_nand6 en_bar[6] in[6] net[6] vdd gnd pnand2_4 +Xwl_driver_inv6 net[6] wl[6] vdd gnd pinv_14 +Xwl_driver_inv_en7 en en_bar[7] vdd gnd pinv_15 +Xwl_driver_nand7 en_bar[7] in[7] net[7] vdd gnd pnand2_4 +Xwl_driver_inv7 net[7] wl[7] vdd gnd pinv_14 +Xwl_driver_inv_en8 en en_bar[8] vdd gnd pinv_15 +Xwl_driver_nand8 en_bar[8] in[8] net[8] vdd gnd pnand2_4 +Xwl_driver_inv8 net[8] wl[8] vdd gnd pinv_14 +Xwl_driver_inv_en9 en en_bar[9] vdd gnd pinv_15 +Xwl_driver_nand9 en_bar[9] in[9] net[9] vdd gnd pnand2_4 +Xwl_driver_inv9 net[9] wl[9] vdd gnd pinv_14 +Xwl_driver_inv_en10 en en_bar[10] vdd gnd pinv_15 +Xwl_driver_nand10 en_bar[10] in[10] net[10] vdd gnd pnand2_4 +Xwl_driver_inv10 net[10] wl[10] vdd gnd pinv_14 +Xwl_driver_inv_en11 en en_bar[11] vdd gnd pinv_15 +Xwl_driver_nand11 en_bar[11] in[11] net[11] vdd gnd pnand2_4 +Xwl_driver_inv11 net[11] wl[11] vdd gnd pinv_14 +Xwl_driver_inv_en12 en en_bar[12] vdd gnd pinv_15 +Xwl_driver_nand12 en_bar[12] in[12] net[12] vdd gnd pnand2_4 +Xwl_driver_inv12 net[12] wl[12] vdd gnd pinv_14 +Xwl_driver_inv_en13 en en_bar[13] vdd gnd pinv_15 +Xwl_driver_nand13 en_bar[13] in[13] net[13] vdd gnd pnand2_4 +Xwl_driver_inv13 net[13] wl[13] vdd gnd pinv_14 +Xwl_driver_inv_en14 en en_bar[14] vdd gnd pinv_15 +Xwl_driver_nand14 en_bar[14] in[14] net[14] vdd gnd pnand2_4 +Xwl_driver_inv14 net[14] wl[14] vdd gnd pinv_14 +Xwl_driver_inv_en15 en en_bar[15] vdd gnd pinv_15 +Xwl_driver_nand15 en_bar[15] in[15] net[15] vdd gnd pnand2_4 +Xwl_driver_inv15 net[15] wl[15] vdd gnd pinv_14 +.ENDS wordline_driver + +.SUBCKT pinv_16 A Z vdd gnd +Mpinv_pmos Z A vdd vdd p m=1 w=2.4u l=0.6u pd=6.0u ps=6.0u as=3.5999999999999996p ad=3.5999999999999996p +Mpinv_nmos Z A gnd gnd n m=1 w=1.2u l=0.6u pd=3.5999999999999996u ps=3.5999999999999996u as=1.7999999999999998p ad=1.7999999999999998p +.ENDS pinv_16 + +.SUBCKT bank DOUT[0] DOUT[1] DOUT[2] DOUT[3] DIN[0] DIN[1] DIN[2] DIN[3] A[0] A[1] A[2] A[3] s_en w_en tri_en_bar tri_en clk_buf_bar clk_buf vdd gnd +Xbitcell_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd bitcell_array +Xprecharge_array bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] clk_buf_bar vdd precharge_array +Xsense_amp_array sa_out[0] bl[0] br[0] sa_out[1] bl[1] br[1] sa_out[2] bl[2] br[2] sa_out[3] bl[3] br[3] s_en vdd gnd sense_amp_array +Xwrite_driver_array DIN[0] DIN[1] DIN[2] DIN[3] bl[0] br[0] bl[1] br[1] bl[2] br[2] bl[3] br[3] w_en vdd gnd write_driver_array +Xtri_gate_array sa_out[0] sa_out[1] sa_out[2] sa_out[3] DOUT[0] DOUT[1] DOUT[2] DOUT[3] tri_en tri_en_bar vdd gnd tri_gate_array +Xrow_decoder A[0] A[1] A[2] A[3] dec_out[0] dec_out[1] dec_out[2] dec_out[3] dec_out[4] dec_out[5] dec_out[6] dec_out[7] dec_out[8] dec_out[9] dec_out[10] dec_out[11] dec_out[12] dec_out[13] dec_out[14] dec_out[15] vdd gnd hierarchical_decoder_16rows +Xwordline_driver dec_out[0] dec_out[1] dec_out[2] dec_out[3] dec_out[4] dec_out[5] dec_out[6] dec_out[7] dec_out[8] dec_out[9] dec_out[10] dec_out[11] dec_out[12] dec_out[13] dec_out[14] dec_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk_buf vdd gnd wordline_driver +.ENDS bank + +.SUBCKT sram1 DIN[0] DIN[1] DIN[2] DIN[3] ADDR[0] ADDR[1] ADDR[2] ADDR[3] csb web oeb clk DOUT[0] DOUT[1] DOUT[2] DOUT[3] vdd gnd +Xbank0 DOUT[0] DOUT[1] DOUT[2] DOUT[3] DIN[0] DIN[1] DIN[2] DIN[3] A[0] A[1] A[2] A[3] s_en w_en tri_en_bar tri_en clk_buf_bar clk_buf vdd gnd bank +Xcontrol csb_s web_s oeb_s clk s_en w_en tri_en tri_en_bar clk_buf_bar clk_buf vdd gnd control_logic +Xaddress ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A[1] A[2] A[3] clk_buf vdd gnd dff_array +Xaddress ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A[1] A[2] A[3] clk_buf vdd gnd dff_array +.ENDS sram1 diff --git a/compiler/tests/sram1_TT_5p0V_25C.lib b/compiler/tests/sram1_TT_5p0V_25C.lib new file mode 100644 index 00000000..ddf17785 --- /dev/null +++ b/compiler/tests/sram1_TT_5p0V_25C.lib @@ -0,0 +1,347 @@ +library (sram1_TT_5p0V_25C_lib){ + delay_model : "table_lookup"; + time_unit : "1ns" ; + voltage_unit : "1v" ; + current_unit : "1mA" ; + resistance_unit : "1kohm" ; + capacitive_load_unit(1 ,fF) ; + leakage_power_unit : "1mW" ; + pulling_resistance_unit :"1kohm" ; + operating_conditions(OC){ + process : 1.0 ; + voltage : 5.0 ; + temperature : 25; + } + + input_threshold_pct_fall : 50.0 ; + output_threshold_pct_fall : 50.0 ; + input_threshold_pct_rise : 50.0 ; + output_threshold_pct_rise : 50.0 ; + slew_lower_threshold_pct_fall : 10.0 ; + slew_upper_threshold_pct_fall : 90.0 ; + slew_lower_threshold_pct_rise : 10.0 ; + slew_upper_threshold_pct_rise : 90.0 ; + + nom_voltage : 5.0; + nom_temperature : 25; + nom_process : 1.0; + default_cell_leakage_power : 0.0 ; + default_leakage_power_density : 0.0 ; + default_input_pin_cap : 1.0 ; + default_inout_pin_cap : 1.0 ; + default_output_pin_cap : 0.0 ; + default_max_transition : 0.5 ; + default_fanout_load : 1.0 ; + default_max_fanout : 4.0 ; + default_connection_class : universal ; + + lu_table_template(CELL_TABLE){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1("0.0125, 0.05, 0.4"); + index_2("2.45605, 9.8242, 78.5936"); + } + + lu_table_template(CONSTRAINT_TABLE){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1("0.0125, 0.05, 0.4"); + index_2("0.0125, 0.05, 0.4"); + } + + default_operating_conditions : OC; + + + type (DATA){ + base_type : array; + data_type : bit; + bit_width : 4; + bit_from : 0; + bit_to : 3; + } + + type (ADDR){ + base_type : array; + data_type : bit; + bit_width : 4; + bit_from : 0; + bit_to : 3; + } + +cell (sram1){ + memory(){ + type : ram; + address_width : 4; + word_width : 4; + } + interface_timing : true; + dont_use : true; + map_only : true; + dont_touch : true; + area : 136566.0; + + leakage_power () { + when : "CSb"; + value : 0.000202; + } + cell_leakage_power : 0; + bus(DATA){ + bus_type : DATA; + direction : inout; + max_capacitance : 78.5936; + min_capacitance : 2.45605; + three_state : "!OEb & !clk"; + memory_write(){ + address : ADDR; + clocked_on : clk; + } + memory_read(){ + address : ADDR; + } + pin(DATA[3:0]){ + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + } + timing(){ + timing_sense : non_unate; + related_pin : "clk"; + timing_type : falling_edge; + cell_rise(CELL_TABLE) { + values("0.612, 0.66, 1.1",\ + "0.612, 0.66, 1.1",\ + "0.612, 0.66, 1.1"); + } + cell_fall(CELL_TABLE) { + values("0.612, 0.66, 1.1",\ + "0.612, 0.66, 1.1",\ + "0.612, 0.66, 1.1"); + } + rise_transition(CELL_TABLE) { + values("0.024, 0.081, 0.61",\ + "0.024, 0.081, 0.61",\ + "0.024, 0.081, 0.61"); + } + fall_transition(CELL_TABLE) { + values("0.024, 0.081, 0.61",\ + "0.024, 0.081, 0.61",\ + "0.024, 0.081, 0.61"); + } + } + } + } + + bus(ADDR){ + bus_type : ADDR; + direction : input; + capacitance : 9.8242; + max_transition : 0.4; + pin(ADDR[3:0]){ + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + } + } + } + + pin(CSb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + } + } + + pin(OEb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + } + } + + pin(WEb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009",\ + "0.009, 0.009, 0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + fall_constraint(CONSTRAINT_TABLE) { + values("0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001",\ + "0.001, 0.001, 0.001"); + } + } + } + + pin(clk){ + clock : true; + direction : input; + capacitance : 9.8242; + internal_power(){ + when : "!CSb & clk & !WEb"; + rise_power(scalar){ + values("10.812808757533329"); + } + fall_power(scalar){ + values("10.812808757533329"); + } + } + internal_power(){ + when : "!CSb & !clk & WEb"; + rise_power(scalar){ + values("10.812808757533329"); + } + fall_power(scalar){ + values("10.812808757533329"); + } + } + internal_power(){ + when : "CSb"; + rise_power(scalar){ + values("0"); + } + fall_power(scalar){ + values("0"); + } + } + timing(){ + timing_type :"min_pulse_width"; + related_pin : clk; + rise_constraint(scalar) { + values("0.0"); + } + fall_constraint(scalar) { + values("0.0"); + } + } + timing(){ + timing_type :"minimum_period"; + related_pin : clk; + rise_constraint(scalar) { + values("0"); + } + fall_constraint(scalar) { + values("0"); + } + } + } + } +} diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index cf3508f4..63e8bdec 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -42,6 +42,12 @@ def write_magic_script(cell_name, gds_name, extract=False): f.write("gds warning default\n") f.write("gds read {}\n".format(gds_name)) f.write("load {}\n".format(cell_name)) + # Flatten the cell to get rid of DRCs spanning multiple layers + # (e.g. with routes) + f.write("flatten {}_new\n".format(cell_name)) + f.write("load {}_new\n".format(cell_name)) + f.write("cellname rename {0}_new {0}\n".format(cell_name)) + f.write("load {}\n".format(cell_name)) f.write("writeall force\n") f.write("drc check\n") f.write("drc catchup\n")