Merge branch 'dev' into multiport_characterization

This commit is contained in:
Hunter Nichols 2018-08-30 01:20:23 -07:00
commit 73388e9797
93 changed files with 3378 additions and 1194 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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):
"""

View File

@ -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()

View File

@ -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):

View File

@ -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))

View File

@ -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))

View File

@ -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=""
self.coordinates=""

View File

@ -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.

View File

@ -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))

View File

@ -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

View File

@ -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())

View File

@ -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<self.map[n].min_cost):
self.map[n].visited=True
self.map[n].min_path = newpath
self.map[n].min_cost = predicted_cost
debug.info(3,"Enqueuing: cost=" + str(current_cost) + "+" + str(target_cost) + " " + str(newpath))
# add the cost to get to this point if we haven't reached it yet
heappush(self.q,(predicted_cost,self.counter,newpath))
self.counter += 1
debug.warning("Unable to route path. Expand the detour_scale to allow detours.")
return (None,None)
def expand_dirs(self,path):
"""
Expand each of the four cardinal directions plus up or down
but not expanding to blocked cells. Expands in all directions
regardless of preferred directions.
"""
# expand from the last point
point = path[-1]
neighbors = []
east = point + vector3d(1,0,0)
if not self.is_blocked(east) and not east in path:
neighbors.append(east)
west= point + vector3d(-1,0,0)
if not self.is_blocked(west) and not west in path:
neighbors.append(west)
up = point + vector3d(0,0,1)
if up.z<2 and not self.is_blocked(up) and not up in path:
neighbors.append(up)
north = point + vector3d(0,1,0)
if not self.is_blocked(north) and not north in path:
neighbors.append(north)
south = point + vector3d(0,-1,0)
if not self.is_blocked(south) and not south in path:
neighbors.append(south)
down = point + vector3d(0,0,-1)
if down.z>=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

View File

@ -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<self.map[n].min_cost):
self.map[n].visited=True
self.map[n].min_path = newpath
self.map[n].min_cost = predicted_cost
debug.info(3,"Enqueuing: cost=" + str(current_cost) + "+" + str(target_cost) + " " + str(newpath))
# add the cost to get to this point if we haven't reached it yet
self.q.put((predicted_cost,newpath))
debug.warning("Unable to route path. Expand the detour_scale to allow detours.")
return (None,None)
def is_target(self,point):
"""
Point is in the target set, so we are done.
"""
return point in self.target
def expand_dirs(self,path):
"""
Expand each of the four cardinal directions plus up or down
but not expanding to blocked cells. Expands in all directions
regardless of preferred directions.
"""
# expand from the last point
point = path[-1]
neighbors = []
east = point + vector3d(1,0,0)
if not self.is_blocked(east) and not east in path:
neighbors.append(east)
west= point + vector3d(-1,0,0)
if not self.is_blocked(west) and not west in path:
neighbors.append(west)
up = point + vector3d(0,0,1)
if up.z<2 and not self.is_blocked(up) and not up in path:
neighbors.append(up)
north = point + vector3d(0,1,0)
if not self.is_blocked(north) and not north in path:
neighbors.append(north)
south = point + vector3d(0,-1,0)
if not self.is_blocked(south) and not south in path:
neighbors.append(south)
down = point + vector3d(0,0,-1)
if down.z>=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

View File

@ -1,3 +0,0 @@
#!/bin/bash
python tests/regress.py -t freepdk45
python tests/regress.py -t scn3me_subm

View File

@ -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):

View File

@ -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)

View File

@ -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()

View File

@ -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

53
compiler/router/tests/01_no_blockages_test.py Normal file → Executable file
View File

@ -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()

49
compiler/router/tests/02_blockages_test.py Normal file → Executable file
View File

@ -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)

52
compiler/router/tests/03_same_layer_pins_test.py Normal file → Executable file
View File

@ -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()

51
compiler/router/tests/04_diff_layer_pins_test.py Normal file → Executable file
View File

@ -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()

50
compiler/router/tests/05_two_nets_test.py Normal file → Executable file
View File

@ -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()

51
compiler/router/tests/06_pin_location_test.py Normal file → Executable file
View File

@ -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)

51
compiler/router/tests/07_big_test.py Normal file → Executable file
View File

@ -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)

50
compiler/router/tests/08_expand_region_test.py Normal file → Executable file
View File

@ -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()

View File

@ -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()

21
compiler/router/tests/config_freepdk45.py Normal file → Executable file
View File

@ -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"

20
compiler/router/tests/config_scn3me_subm.py Normal file → Executable file
View File

@ -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"

View File

@ -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

21
compiler/router/tests/regress.py Normal file → Executable file
View File

@ -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())

289
compiler/router/tests/testutils.py Normal file → Executable file
View File

@ -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("|==============================================================================|")

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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")

View File

@ -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)

View File

@ -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()

View File

@ -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")

View File

@ -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")

View File

@ -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)

0
compiler/tests/out.log Normal file
View File

BIN
compiler/tests/sram1.gds Normal file

Binary file not shown.

19
compiler/tests/sram1.lef Normal file
View File

@ -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

602
compiler/tests/sram1.sp vendored Normal file
View File

@ -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

View File

@ -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");
}
}
}
}
}

View File

@ -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")