mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into multiport_characterization
This commit is contained in:
commit
73388e9797
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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=""
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
python tests/regress.py -t freepdk45
|
||||
python tests/regress.py -t scn3me_subm
|
||||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
Binary file not shown.
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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("|==============================================================================|")
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Reference in New Issue