Merge branch 'dev' of https://github.com/VLSIDA/PrivateRAM into multiport

This commit is contained in:
Michael Timothy Grimes
2018-11-08 09:59:52 -08:00
50 changed files with 675 additions and 447 deletions
+10 -10
View File
@@ -88,9 +88,9 @@ def get_libcell_size(name, units, layer):
return(get_gds_size(name, cell_gds, units, layer))
def get_gds_pins(pin_list, name, gds_filename, units, layer):
def get_gds_pins(pin_names, name, gds_filename, units):
"""
Open a GDS file and find the pins in pin_list as text on a given layer.
Open a GDS file and find the pins in pin_names as text on a given layer.
Return these as a rectangle layer pair for each pin.
"""
cell_vlsi = gdsMill.VlsiLayout(units=units)
@@ -98,23 +98,23 @@ def get_gds_pins(pin_list, name, gds_filename, units, layer):
reader.loadFromFile(gds_filename)
cell = {}
for pin in pin_list:
cell[str(pin)]=[]
label_list=cell_vlsi.getPinShapeByLabel(str(pin))
for label in label_list:
(name,layer,boundary)=label
for pin_name in pin_names:
cell[str(pin_name)]=[]
pin_list=cell_vlsi.getPinShape(str(pin_name))
for pin_shape in pin_list:
(layer,boundary)=pin_shape
rect=[vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])]
# this is a list because other cells/designs may have must-connect pins
cell[str(pin)].append(pin_layout(pin, rect, layer))
cell[str(pin_name)].append(pin_layout(pin_name, rect, layer))
return cell
def get_libcell_pins(pin_list, name, units, layer):
def get_libcell_pins(pin_list, name, units):
"""
Open a GDS file and find the pins in pin_list as text on a given layer.
Return these as a rectangle layer pair for each pin.
"""
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
return(get_gds_pins(pin_list, name, cell_gds, units, layer))
return(get_gds_pins(pin_list, name, cell_gds, units))
+1 -1
View File
@@ -13,7 +13,7 @@ class bitcell(design.design):
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
def __init__(self):
design.design.__init__(self, "cell_6t")
+1 -1
View File
@@ -13,7 +13,7 @@ class bitcell_1rw_1r(design.design):
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"])
def __init__(self):
design.design.__init__(self, "cell_1rw_1r")
+1 -1
View File
@@ -12,7 +12,7 @@ class replica_bitcell(design.design):
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
def __init__(self):
design.design.__init__(self, "replica_cell_6t")
@@ -0,0 +1,23 @@
import design
import debug
import utils
from tech import GDS,layer
class replica_bitcell_1rw_1r(design.design):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
def __init__(self):
design.design.__init__(self, "replica_cell_1rw_1r")
debug.info(2, "Create replica bitcell 1rw+1r object")
self.width = replica_bitcell_1rw_1r.width
self.height = replica_bitcell_1rw_1r.height
self.pin_map = replica_bitcell_1rw_1r.pin_map
+9 -1
View File
@@ -35,7 +35,15 @@ class functional(simulation):
self.stored_words = {}
self.write_check = []
self.read_check = []
def set_spice_constants(self):
"""Spice constants for functional test"""
simulation.set_spice_constants(self)
#Heuristic increase for functional period. Base feasible period typically does not pass the functional test
#for column mux of this size. Increase the feasible period by 20% for this case.
if self.sram.words_per_row >= 4:
self.period = self.period*1.2
def run(self):
# Generate a random sequence of reads and writes
self.write_random_memory_sequence()
+2 -2
View File
@@ -208,14 +208,14 @@ class simulation():
t_current,
t_current+self.period)
elif op == "write":
comment = "\tWriting {0} to address {1} (from port {2}) during cylce {3} ({4}ns - {5}ns)".format(word,
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr,
port,
int(t_current/self.period),
t_current,
t_current+self.period)
else:
comment = "\tReading {0} from address {1} (from port {2}) during cylce {3} ({4}ns - {5}ns)".format(word,
comment = "\tReading {0} from address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr,
port,
int(t_current/self.period),
-7
View File
@@ -9,10 +9,3 @@ temperatures = [25]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
#Setting for multiport
# netlist_only = True
# bitcell = "pbitcell"
# replica_bitcell="replica_pbitcell"
# num_rw_ports = 1
# num_r_ports = 0
# num_w_ports = 1
+4 -8
View File
@@ -3,16 +3,12 @@ num_words = 16
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [ 5.0 ]
supply_voltages = [ 3.3 ]
temperatures = [ 25 ]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
#Setting for multiport
netlist_only = True
bitcell = "pbitcell"
replica_bitcell="replica_pbitcell"
num_rw_ports = 1
num_r_ports = 1
num_w_ports = 1
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"
+85 -139
View File
@@ -60,6 +60,11 @@ class VlsiLayout:
self.tempCoordinates=None
self.tempPassFail = True
# This is a dict indexed by the pin labels.
# It contains a list of list of shapes, one for each occurance of the label.
# Multiple labels may be disconnected.
self.pins = {}
def rotatedCoordinates(self,coordinatesToRotate,rotateAngle):
#helper method to rotate a list of coordinates
angle=math.radians(float(0))
@@ -206,7 +211,11 @@ class VlsiLayout:
def initialize(self):
self.deduceHierarchy()
#self.traverseTheHierarchy()
self.populateCoordinateMap()
self.populateCoordinateMap()
for layerNumber in self.layerNumbersInUse:
self.processLabelPins(layerNumber)
def populateCoordinateMap(self):
def addToXyTree(startingStructureName = None,transformPath = None):
@@ -478,6 +487,10 @@ class VlsiLayout:
return False #these shapes are ok
def isPointInsideOfBox(self,pointCoordinates,boxCoordinates):
"""
Check if a point is contained in the shape
"""
debug.check(len(boxCoordinates)==4,"Invalid number of coordinates for box.")
leftBound = boxCoordinates[0][0]
rightBound = boxCoordinates[0][0]
topBound = boxCoordinates[0][1]
@@ -499,7 +512,9 @@ class VlsiLayout:
return True
def isShapeInsideOfBox(self,shapeCoordinates, boxCoordinates):
#go through every point in the shape to test if they are all inside the box
"""
Go through every point in the shape to test if they are all inside the box.
"""
for point in shapeCoordinates:
if not self.isPointInsideOfBox(point,boxCoordinates):
return False
@@ -634,160 +649,89 @@ class VlsiLayout:
return cellBoundary
def getLabelDBInfo(self,label_name):
def getTexts(self, layer):
"""
Return the coordinates in DB units and layer of all matching labels
Get all of the labels on a given layer only at the root level.
"""
label_list = []
label_layer = None
label_coordinate = [None, None]
# Why must this be the last one found? It breaks if we return the first.
text_list = []
for Text in self.structures[self.rootStructureName].texts:
if Text.textString == label_name or Text.textString == label_name+"\x00":
label_layer = Text.drawingLayer
label_coordinate = Text.coordinates[0]
if label_layer!=None:
label_list.append((label_coordinate,label_layer))
debug.check(len(label_list)>0,"Did not find labels {0}.".format(label_name))
return label_list
def getLabelInfo(self,label_name):
"""
Return the coordinates in USER units and layer of a label
"""
label_list=self.getLabelDBInfo(label_name)
new_list=[]
for label in label_list:
(label_coordinate,label_layer)=label
user_coordinates = [x*self.units[0] for x in label_coordinate]
new_list.append(user_coordinates,label_layer)
return new_list
if Text.drawingLayer == layer:
text_list.append(Text)
return text_list
def getPinShapeByLocLayer(self, coordinate, layer):
"""
Return the largest enclosing rectangle on a layer and at a location.
Coordinates should be in USER units.
"""
db_coordinate = [x/self.units[0] for x in coordinate]
return self.getPinShapeByDBLocLayer(db_coordinate, layer)
def getPinShapeByDBLocLayer(self, coordinate, layer):
"""
Return the largest enclosing rectangle on a layer and at a location.
Coordinates should be in DB units.
"""
pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer)
if len(pin_boundaries) == 0:
debug.warning("Did not find pin on layer {0} at coordinate {1}".format(layer, coordinate))
# sort the boundaries, return the max area pin boundary
pin_boundaries.sort(key=boundaryArea,reverse=True)
pin_boundary=pin_boundaries[0]
# Convert to USER units
pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],
pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]
# Make a name if we don't have the pin name
return ["p"+str(coordinate)+"_"+str(layer), layer, pin_boundary]
def getAllPinShapesByLocLayer(self, coordinate, layer):
"""
Return ALL the enclosing rectangles on the same layer
at the given coordinate. Coordinates should be in USER units.
"""
db_coordinate = [int(x/self.units[0]) for x in coordinate]
return self.getAllPinShapesByDBLocLayer(db_coordinate, layer)
def getAllPinShapesByDBLocLayer(self, coordinate, layer):
"""
Return ALL the enclosing rectangles on the same layer
at the given coordinate. Input coordinates should be in DB units.
Returns user unit shapes.
"""
pin_boundaries=self.getAllPinShapesInStructureList(coordinate, layer)
# Convert to user units
new_boundaries = []
for pin_boundary in pin_boundaries:
new_pin_boundary = [pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],
pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]
new_boundaries.append(["p"+str(coordinate)+"_"+str(layer), layer, new_pin_boundary])
return new_boundaries
def getPinShapeByLabel(self,label_name):
def getPinShape(self, pin_name):
"""
Search for a pin label and return the largest enclosing rectangle
on the same layer as the pin label.
If there are multiple pin lists, return the max of each.
"""
label_list=self.getLabelDBInfo(label_name)
shape_list=[]
for label in label_list:
(label_coordinate,label_layer)=label
shape = self.getPinShapeByDBLocLayer(label_coordinate, label_layer)
shape_list.append(shape)
return shape_list
pin_map = self.pins[pin_name]
max_pins = []
for pin_list in pin_map:
max_pin = None
max_area = 0
for pin in pin_list:
(layer,boundary) = pin
new_area = boundaryArea(boundary)
if max_pin == None or new_area>max_area:
max_pin = pin
max_area = new_area
max_pins.append(max_pin)
def getAllPinShapesByLabel(self,label_name):
return max_pins
def getAllPinShapes(self, pin_name):
"""
Search for a pin label and return ALL the enclosing rectangles on the same layer
as the pin label.
"""
label_list=self.getLabelDBInfo(label_name)
shape_list=[]
for label in label_list:
(label_coordinate,label_layer)=label
shape_list.extend(self.getAllPinShapesByDBLocLayer(label_coordinate, label_layer))
shape_list = []
pin_map = self.pins[pin_name]
for pin_list in pin_map:
for pin in pin_list:
(pin_layer, boundary) = pin
shape_list.append(pin)
return shape_list
def getAllPinShapesInStructureList(self,coordinates,layer):
def processLabelPins(self, layer):
"""
Given a coordinate, search for enclosing structures on the given layer.
Return all pin shapes.
Find all text labels and create a map to a list of shapes that
they enclose on the given layer.
"""
boundaries = []
for TreeUnit in self.xyTree:
boundaries.extend(self.getPinInStructure(coordinates,layer,TreeUnit))
# Get the labels on a layer in the root level
labels = self.getTexts(layer)
# Get all of the shapes on the layer at all levels
# and transform them to the current level
shapes = self.getAllShapes(layer)
return boundaries
for label in labels:
label_coordinate = label.coordinates[0]
user_coordinate = [x*self.units[0] for x in label_coordinate]
pin_shapes = []
for boundary in shapes:
if self.labelInRectangle(user_coordinate,boundary):
pin_shapes.append((layer, boundary))
label_text = label.textString
# Remove the padding if it exists
if label_text[-1] == "\x00":
label_text = label_text[0:-1]
def getPinInStructure(self,coordinates,layer,structure):
"""
Go through all the shapes in a structure and return the list of shapes
that the label coordinates are inside.
try:
self.pins[label_text]
except KeyError:
self.pins[label_text] = []
self.pins[label_text].append(pin_shapes)
def getAllShapes(self,layer):
"""
(structureName,structureOrigin,structureuVector,structurevVector)=structure
boundaries = []
for boundary in self.structures[str(structureName)].boundaries:
# Pin enclosures only work on rectangular pins so ignore any non rectangle
# This may report not finding pins, but the user should fix this by adding a rectangle.
if len(boundary.coordinates)!=5:
continue
if layer==boundary.drawingLayer:
left_bottom=boundary.coordinates[0]
right_top=boundary.coordinates[2]
# Rectangle is [leftx, bottomy, rightx, topy].
boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]]
boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector)
boundaryRect=[boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(),
boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item()]
if self.labelInRectangle(coordinates,boundaryRect):
boundaries.append(boundaryRect)
return boundaries
def getAllShapesInStructureList(self,layer):
"""
Return all pin shapes on a given layer.
Return all gshapes on a given layer in [llx, lly, urx, ury] format and
user units.
"""
boundaries = []
for TreeUnit in self.xyTree:
@@ -812,7 +756,8 @@ class VlsiLayout:
def getShapesInStructure(self,layer,structure):
"""
Go through all the shapes in a structure and return the list of shapes.
Go through all the shapes in a structure and return the list of shapes in
the form [llx, lly, urx, ury]
"""
(structureName,structureOrigin,structureuVector,structurevVector)=structure
@@ -820,6 +765,7 @@ class VlsiLayout:
boundaries = []
for boundary in self.structures[str(structureName)].boundaries:
# FIXME: Right now, this only supports rectangular shapes!
#debug.check(len(boundary.coordinates)==5,"Non-rectangular shape.")
if len(boundary.coordinates)!=5:
continue
if layer==boundary.drawingLayer:
@@ -874,8 +820,8 @@ class VlsiLayout:
"""
Checks if a coordinate is within a given rectangle. Rectangle is [leftx, bottomy, rightx, topy].
"""
coordinate_In_Rectangle_x_range=(coordinate[0]>=int(rectangle[0]))&(coordinate[0]<=int(rectangle[2]))
coordinate_In_Rectangle_y_range=(coordinate[1]>=int(rectangle[1]))&(coordinate[1]<=int(rectangle[3]))
coordinate_In_Rectangle_x_range=(coordinate[0]>=rectangle[0])&(coordinate[0]<=rectangle[2])
coordinate_In_Rectangle_y_range=(coordinate[1]>=rectangle[1])&(coordinate[1]<=rectangle[3])
if coordinate_In_Rectangle_x_range & coordinate_In_Rectangle_y_range:
return True
else:
+17 -5
View File
@@ -18,13 +18,14 @@ class control_logic(design.design):
Dynamically generated Control logic for the total SRAM circuit.
"""
def __init__(self, num_rows, port_type="rw"):
def __init__(self, num_rows, words_per_row, port_type="rw"):
""" Constructor """
name = "control_logic_" + port_type
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(name))
self.num_rows = num_rows
self.words_per_row = words_per_row
self.port_type = port_type
if self.port_type == "rw":
@@ -92,14 +93,25 @@ class control_logic(design.design):
from importlib import reload
c = reload(__import__(OPTS.replica_bitline))
replica_bitline = getattr(c, OPTS.replica_bitline)
# FIXME: These should be tuned according to the size!
delay_stages = 4 # Must be non-inverting
delay_fanout = 3 # This can be anything >=2
delay_stages, delay_fanout = self.get_delay_chain_size()
bitcell_loads = int(math.ceil(self.num_rows / 2.0))
self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads, name="replica_bitline_"+self.port_type)
self.add_mod(self.replica_bitline)
def get_delay_chain_size(self):
"""Determine the size of the delay chain used for the Sense Amp Enable """
# FIXME: These should be tuned according to the additional size parameters
delay_fanout = 3 # This can be anything >=2
# Delay stages Must be non-inverting
if self.words_per_row >= 8:
delay_stages = 8
elif self.words_per_row == 4:
delay_stages = 6
else:
delay_stages = 4
return (delay_stages, delay_fanout)
def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """
+1 -1
View File
@@ -12,7 +12,7 @@ class dff(design.design):
pin_names = ["D", "Q", "clk", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
def __init__(self, name="dff"):
design.design.__init__(self, name)
+65 -9
View File
@@ -189,16 +189,66 @@ class replica_bitline(design.design):
if pin.layer != "metal1":
continue
self.add_path("metal1", [pin_right, pin_extension])
pin_width_ydir = pin.uy()-pin.by()
#Width is set to pin y width to avoid DRC issues with m1 gaps
self.add_path("metal1", [pin_right, pin_extension], pin_width_ydir)
self.add_power_pin("gnd", pin_extension)
# for multiport, need to short wordlines to each other so they all connect to gnd
# for multiport, need to short wordlines to each other so they all connect to gnd.
wl_last = self.wl_list[self.total_ports-1]+"_{}".format(row)
pin_last = self.rbl_inst.get_pin(wl_last)
self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0))
def short_wordlines(self, wl_pin_a, wl_pin_b, pin_side, is_replica_cell, cell_row=0, offset_x_vec=None):
"""Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins."""
#Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord.
#This is my (Hunter) first time editing layout in openram so this function is likely not optimal.
if self.total_ports > 1:
#1. Create vertical metal for all the bitlines to connect to
#m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped
correct_y = vector(0, 0.5*drc("minwidth_metal1"))
#x spacing depends on the side being drawn. Unknown to me (Hunter) why the size of the space differs by the side.
#I assume this is related to how a wire is draw, but I have not investigated the issue.
if pin_side == "right":
correct_x = vector(0.5*drc("minwidth_metal1"), 0)
if offset_x_vec != None:
correct_x = offset_x_vec
else:
correct_x = vector(1.5*drc("minwidth_metal1"), 0)
if wl_pin_a.uy() > wl_pin_b.uy():
self.add_path("metal1", [wl_pin_a.rc()+correct_x+correct_y, wl_pin_b.rc()+correct_x-correct_y])
else:
self.add_path("metal1", [wl_pin_a.rc()+correct_x-correct_y, wl_pin_b.rc()+correct_x+correct_y])
elif pin_side == "left":
if offset_x_vec != None:
correct_x = offset_x_vec
else:
correct_x = vector(1.5*drc("minwidth_metal1"), 0)
if wl_pin_a.uy() > wl_pin_b.uy():
self.add_path("metal1", [wl_pin_a.lc()-correct_x+correct_y, wl_pin_b.lc()-correct_x-correct_y])
else:
self.add_path("metal1", [wl_pin_a.lc()-correct_x-correct_y, wl_pin_b.lc()-correct_x+correct_y])
else:
debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1)
correct = vector(0.5*drc("minwidth_metal1"), 0)
self.add_path("metal1", [pin.rc()-correct, pin_last.rc()-correct])
#2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this.
for port in range(self.total_ports):
if is_replica_cell:
wl = self.wl_list[port]
pin = self.rbc_inst.get_pin(wl)
else:
wl = self.wl_list[port]+"_{}".format(cell_row)
pin = self.rbl_inst.get_pin(wl)
if pin_side == "left":
self.add_path("metal1", [pin.lc()-correct_x, pin.lc()])
elif pin_side == "right":
self.add_path("metal1", [pin.rc()+correct_x, pin.rc()])
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
@@ -217,8 +267,13 @@ 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(), rotate=0)
# Don't rotate this via to vit in FreePDK45. In the custom cell, the pin cannot be placed
# directly on vdd or there will be a drc error with a wordline. Place the pin slightly farther
# away then route to it. A better solution would be to rotate the m1 in the via or move the pin
# a m1_pitch below the entire cell.
pin_extension = pin.center() - vector(0,self.m1_pitch)
self.add_power_pin("vdd", pin_extension, rotate=0)
self.add_path("metal1", [pin.center(), pin_extension])
for pin in self.rbc_inst.get_pins("gnd"):
self.add_power_pin("gnd", pin.center())
@@ -267,9 +322,10 @@ class replica_bitline(design.design):
wl_last = self.wl_list[self.total_ports-1]
pin = self.rbc_inst.get_pin(wl)
pin_last = self.rbc_inst.get_pin(wl_last)
x_offset = self.short_wordlines(pin, pin_last, "left", True)
correct = vector(0.5*drc("minwidth_metal1"), 0)
self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct])
#correct = vector(0.5*drc("minwidth_metal1"), 0)
#self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct])
# DRAIN ROUTE
# Route the drain to the vdd rail
+2 -1
View File
@@ -13,7 +13,7 @@ class sense_amp(design.design):
pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"])
def __init__(self, name):
design.design.__init__(self, name)
@@ -25,6 +25,7 @@ class sense_amp(design.design):
def input_load(self):
#Input load for the bitlines which are connected to the source/drain of a TX. Not the selects.
from tech import spice, parameter
bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file.
return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff
@@ -218,7 +218,7 @@ class single_level_column_mux_array(design.design):
rotate=90)
def analytical_delay(self, vdd, slew, load=0.0):
from tech import spice
from tech import spice, parameter
r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"])
#Drains of mux transistors make up capacitance.
c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff
+1 -1
View File
@@ -12,7 +12,7 @@ class tri_gate(design.design):
pin_names = ["in", "en", "en_bar", "out", "gnd", "vdd"]
(width,height) = utils.get_libcell_size("tri_gate", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "tri_gate", GDS["unit"])
unique_id = 1
+1 -1
View File
@@ -13,7 +13,7 @@ class write_driver(design.design):
pin_names = ["din", "bl", "br", "en", "gnd", "vdd"]
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"])
def __init__(self, name):
design.design.__init__(self, name)
+29 -21
View File
@@ -1,6 +1,7 @@
import sys
import gdsMill
from tech import drc,GDS
from tech import layer as techlayer
import math
import debug
from router_tech import router_tech
@@ -39,7 +40,7 @@ class router(router_tech):
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(gds_filename)
self.top_name = self.layout.rootStructureName
### The pin data structures
# A map of pin names to a set of pin_layout structures
self.pins = {}
@@ -66,7 +67,8 @@ class router(router_tech):
self.boundary = self.layout.measureBoundary(self.top_name)
# These must be un-indexed to get rid of the matrix type
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
# Pad the UR by a few tracks to add an extra rail possibly
self.ur = vector(self.boundary[1][0], self.boundary[1][1]) + self.track_widths.scale(5,5)
def clear_pins(self):
"""
@@ -94,12 +96,13 @@ class router(router_tech):
def retrieve_pins(self,pin_name):
"""
Retrieve the pin shapes from the layout.
Retrieve the pin shapes on metal 3 from the layout.
"""
shape_list=self.layout.getAllPinShapesByLabel(str(pin_name))
debug.info(2,"Retrieving pins for {}.".format(pin_name))
shape_list=self.layout.getAllPinShapes(str(pin_name))
pin_set = set()
for shape in shape_list:
(name,layer,boundary)=shape
(layer,boundary)=shape
# GDSMill boundaries are in (left, bottom, right, top) order
# so repack and snap to the grid
ll = vector(boundary[0],boundary[1]).snap_to_grid()
@@ -114,7 +117,7 @@ class router(router_tech):
self.all_pins.update(pin_set)
for pin in self.pins[pin_name]:
debug.info(2,"Retrieved pin {}".format(str(pin)))
debug.info(3,"Retrieved pin {}".format(str(pin)))
@@ -123,16 +126,17 @@ class router(router_tech):
Finds the pin shapes and converts to tracks.
Pin can either be a label or a location,layer pair: [[x,y],layer].
"""
debug.info(1,"Finding pins for {}.".format(pin_name))
self.retrieve_pins(pin_name)
self.analyze_pins(pin_name)
def find_blockages(self):
"""
Iterate through all the layers and write the obstacles to the routing grid.
This doesn't consider whether the obstacles will be pins or not. They get reset later
if they are not actually a blockage.
"""
debug.info(1,"Finding blockages.")
for layer in [self.vert_layer_number,self.horiz_layer_number]:
self.retrieve_blockages(layer)
@@ -203,15 +207,15 @@ class router(router_tech):
if pg1.adjacent(pg2):
combined = pin_group(pin_name, [], self)
combined.combine_groups(pg1, pg2)
debug.info(2,"Combining {0} {1} {2}:".format(pin_name, index1, index2))
debug.info(2, " {0}\n {1}".format(pg1.pins, pg2.pins))
debug.info(2," --> {0}\n {1}".format(combined.pins,combined.grids))
debug.info(3,"Combining {0} {1} {2}:".format(pin_name, index1, index2))
debug.info(3, " {0}\n {1}".format(pg1.pins, pg2.pins))
debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids))
remove_indices.update([index1,index2])
pin_groups.append(combined)
break
# Remove them in decreasing order to not invalidate the indices
debug.info(2,"Removing {}".format(sorted(remove_indices)))
debug.info(4,"Removing {}".format(sorted(remove_indices)))
for i in sorted(remove_indices, reverse=True):
del pin_groups[i]
@@ -228,7 +232,7 @@ class router(router_tech):
Make multiple passes of the combine adjacent pins until we have no
more combinations or hit an iteration limit.
"""
debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
# Start as None to signal the first iteration
num_removed_pairs = None
@@ -245,6 +249,7 @@ class router(router_tech):
This will try to separate all grid pins by the supplied number of separation
tracks (default is to prevent adjacency).
"""
debug.info(1,"Separating adjacent pins.")
# Commented out to debug with SCMOS
#if separation==0:
# return
@@ -270,7 +275,7 @@ class router(router_tech):
grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation)
# These should have the same length, so...
if len(grids_g1)>0:
debug.info(1,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2))
debug.info(3,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2))
self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2)
def remove_adjacent_grid(self, pg1, grids1, pg2, grids2):
@@ -292,12 +297,12 @@ class router(router_tech):
# First, see if we can remove grids that are in the secondary grids
# i.e. they aren't necessary to the pin grids
if bigger_grids.issubset(bigger.secondary_grids):
debug.info(1,"Removing {} from bigger {}".format(str(bigger_grids), bigger))
debug.info(3,"Removing {} from bigger {}".format(str(bigger_grids), bigger))
bigger.grids.difference_update(bigger_grids)
self.blocked_grids.update(bigger_grids)
return
elif smaller_grids.issubset(smaller.secondary_grids):
debug.info(1,"Removing {} from smaller {}".format(str(smaller_grids), smaller))
debug.info(3,"Removing {} from smaller {}".format(str(smaller_grids), smaller))
smaller.grids.difference_update(smaller_grids)
self.blocked_grids.update(smaller_grids)
return
@@ -426,7 +431,7 @@ class router(router_tech):
def convert_blockages(self):
""" Convert blockages to grid tracks. """
debug.info(1,"Converting blockages.")
for blockage in self.blockages:
debug.info(3,"Converting blockage {}".format(str(blockage)))
blockage_list = self.convert_blockage(blockage)
@@ -438,7 +443,7 @@ class router(router_tech):
Recursive find boundaries as blockages to the routing grid.
"""
shapes = self.layout.getAllShapesInStructureList(layer_num)
shapes = self.layout.getAllShapes(layer_num)
for boundary in shapes:
ll = vector(boundary[0],boundary[1])
ur = vector(boundary[2],boundary[3])
@@ -621,6 +626,8 @@ class router(router_tech):
"""
Analyze the shapes of a pin and combine them into groups which are connected.
"""
debug.info(2,"Analyzing pin groups for {}.".format(pin_name))
pin_set = self.pins[pin_name]
local_debug = False
@@ -684,6 +691,7 @@ class router(router_tech):
"""
Convert the pin groups into pin tracks and blockage tracks.
"""
debug.info(1,"Converting pins for {}.".format(pin_name))
for pg in self.pin_groups[pin_name]:
pg.convert_pin()
@@ -733,7 +741,7 @@ class router(router_tech):
debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.")
pin_in_tracks = self.pin_groups[pin_name][index].grids
debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks))
debug.info(2,"Set source: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_source(pin_in_tracks)
def add_path_target(self, paths):
@@ -752,7 +760,7 @@ class router(router_tech):
debug.check(index<self.num_pin_grids(pin_name),"Pin component index too large.")
pin_in_tracks = self.pin_groups[pin_name][index].grids
debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
debug.info(2,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks)
@@ -760,7 +768,7 @@ class router(router_tech):
"""
Block all of the pin components.
"""
debug.info(2,"Setting blockages {0} {1}".format(pin_name,value))
debug.info(3,"Setting blockages {0} {1}".format(pin_name,value))
for pg in self.pin_groups[pin_name]:
self.set_blockages(pg.grids, value)
@@ -796,7 +804,7 @@ class router(router_tech):
"""
path=self.prepare_path(path)
debug.info(1,"Adding route: {}".format(str(path)))
debug.info(2,"Adding route: {}".format(str(path)))
# If it is only a square, add an enclosure to the track
if len(path)==1:
self.add_single_enclosure(path[0][0])
+4 -3
View File
@@ -1,6 +1,7 @@
from tech import drc,layer
from contact import contact
from pin_group import pin_group
from vector import vector
import debug
class router_tech:
@@ -35,9 +36,9 @@ class router_tech:
self.track_width = max(self.horiz_track_width,self.vert_track_width)
debug.info(1,"Track width: "+str(self.track_width))
self.track_widths = [self.track_width] * 2
self.track_factor = [1/self.track_width] * 2
debug.info(1,"Track factor: {0}".format(self.track_factor))
self.track_widths = vector([self.track_width] * 2)
self.track_factor = vector([1/self.track_width] * 2)
debug.info(2,"Track factor: {0}".format(self.track_factor))
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
self.layer_widths = [self.track_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing]
+7 -12
View File
@@ -25,12 +25,6 @@ class supply_router(router):
"""
router.__init__(self, layers, design, gds_filename)
# We over-ride the regular router costs to allow
# more off-direction router in the supply grid
grid.VIA_COST = 1
grid.NONPREFERRED_COST = 1
grid.PREFERRED_COST = 1
# The list of supply rails (grid sets) that may be routed
self.supply_rails = {}
self.supply_rail_wires = {}
@@ -224,7 +218,7 @@ class supply_router(router):
ur = grid_utils.get_upper_right(rail)
z = ll.z
pin = self.compute_wide_enclosure(ll, ur, z, name)
debug.info(1,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin))
debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin))
self.cell.add_layout_pin(text=name,
layer=pin.layer,
offset=pin.ll(),
@@ -393,6 +387,7 @@ class supply_router(router):
Route the horizontal and vertical supply rails across the entire design.
Must be done with lower left at 0,0
"""
debug.info(1,"Routing supply rail {0}.".format(name))
# Compute the grid locations of the supply rails
self.compute_supply_rails(name, supply_number)
@@ -426,14 +421,14 @@ class supply_router(router):
"""
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
debug.info(1,"Pin {0} has {1} remaining components to route.".format(pin_name,
remaining_components))
debug.info(1,"Routing {0} with {1} pin components to route.".format(pin_name,
remaining_components))
for index,pg in enumerate(self.pin_groups[pin_name]):
if pg.is_routed():
continue
debug.info(2,"Routing component {0} {1}".format(pin_name, index))
debug.info(3,"Routing component {0} {1}".format(pin_name, index))
# Clear everything in the routing grid.
self.rg.reinit()
@@ -459,7 +454,7 @@ class supply_router(router):
"""
Add the supply rails of given name as a routing target.
"""
debug.info(2,"Add supply rail target {}".format(pin_name))
debug.info(4,"Add supply rail target {}".format(pin_name))
# Add the wire itself as the target
self.rg.set_target(self.supply_rail_wire_tracks[pin_name])
# But unblock all the rail tracks including the space
@@ -470,7 +465,7 @@ class supply_router(router):
"""
Add the supply rails of given name as a routing target.
"""
debug.info(3,"Blocking supply rail")
debug.info(4,"Blocking supply rail")
for rail_name in self.supply_rail_tracks:
self.rg.set_blocked(self.supply_rail_tracks[rail_name])
+3 -3
View File
@@ -223,13 +223,13 @@ class sram_base(design):
from control_logic import control_logic
# Create the control logic module for each port type
if OPTS.num_rw_ports>0:
self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, port_type="rw")
self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="rw")
self.add_mod(self.control_logic_rw)
if OPTS.num_w_ports>0:
self.control_logic_w = control_logic(num_rows=self.num_rows, port_type="w")
self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="w")
self.add_mod(self.control_logic_w)
if OPTS.num_r_ports>0:
self.control_logic_r = control_logic(num_rows=self.num_rows, port_type="r")
self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="r")
self.add_mod(self.control_logic_r)
# Create the address and control flops (but not the clk)
+4 -1
View File
@@ -16,22 +16,25 @@ class library_lvs_test(openram_test):
import verify
(gds_dir, sp_dir, allnames) = setup_files()
drc_errors = 0
lvs_errors = 0
debug.info(1, "Performing LVS on: " + ", ".join(allnames))
for f in allnames:
gds_name = "{0}/{1}.gds".format(gds_dir, f)
sp_name = "{0}/{1}.sp".format(sp_dir, f)
name = re.sub('\.gds$', '', f)
if not os.path.isfile(gds_name):
lvs_errors += 1
debug.error("Missing GDS file {}".format(gds_name))
if not os.path.isfile(sp_name):
lvs_errors += 1
debug.error("Missing SPICE file {}".format(gds_name))
drc_errors += verify.run_drc(name, gds_name)
lvs_errors += verify.run_lvs(f, gds_name, sp_name)
# fail if the error count is not zero
self.assertEqual(lvs_errors, 0)
self.assertEqual(drc_errors+lvs_errors, 0)
globals.end_openram()
def setup_files():
+22 -2
View File
@@ -24,6 +24,26 @@ class replica_bitline_test(openram_test):
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
#debug.error("Exiting...", 1)
stages=8
rows=100
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
#check replica bitline in handmade multi-port 1rw+1r cell
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
stages=4
fanout=4
rows=13
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
stages=8
rows=100
@@ -31,7 +51,7 @@ class replica_bitline_test(openram_test):
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
# check replica bitline in multi-port
# check replica bitline in pbitcell multi-port
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.num_rw_ports = 1
@@ -61,7 +81,7 @@ class replica_bitline_test(openram_test):
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
stages=8
rows=100
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
+5 -5
View File
@@ -20,7 +20,7 @@ class control_logic_test(openram_test):
# check control logic for single port
debug.info(1, "Testing sample for control_logic")
a = control_logic.control_logic(num_rows=128)
a = control_logic.control_logic(num_rows=128, words_per_row=1)
self.local_check(a)
# check control logic for multi-port
@@ -31,7 +31,7 @@ class control_logic_test(openram_test):
OPTS.num_r_ports = 0
debug.info(1, "Testing sample for control_logic for multiport")
a = control_logic.control_logic(num_rows=128)
a = control_logic.control_logic(num_rows=128, words_per_row=1)
self.local_check(a)
# Check port specific control logic
@@ -40,15 +40,15 @@ class control_logic_test(openram_test):
OPTS.num_r_ports = 1
debug.info(1, "Testing sample for control_logic for multiport, only write control logic")
a = control_logic.control_logic(num_rows=128, port_type="rw")
a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="rw")
self.local_check(a)
debug.info(1, "Testing sample for control_logic for multiport, only write control logic")
a = control_logic.control_logic(num_rows=128, port_type="w")
a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="w")
self.local_check(a)
debug.info(1, "Testing sample for control_logic for multiport, only read control logic")
a = control_logic.control_logic(num_rows=128, port_type="r")
a = control_logic.control_logic(num_rows=128, words_per_row=1, port_type="r")
self.local_check(a)
globals.end_openram()
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
class psram_1bank_2mux_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
# testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent)
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.num_words=32
c.words_per_row=2
debug.info(1, "Single bank two way column mux with control logic")
a = sram(c, "sram")
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()
-136
View File
@@ -1,136 +0,0 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
class sram_1bank_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
# testing layout of sram using pbitcell with 1 RW port (a 6T-cell equivalent)
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
c = sram_config(word_size=4,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Single bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
c.num_words=32
c.words_per_row=2
debug.info(1, "Single bank two way column mux with control logic")
a = sram(c, "sram2")
self.local_check(a, final_verification=True)
"""
c.num_words=64
c.words_per_row=4
debug.info(1, "Single bank, four way column mux with control logic")
a = sram(c, "sram3")
self.local_check(a, final_verification=True)
c.word_size=2
c.num_words=128
c.words_per_row=8
debug.info(1, "Single bank, eight way column mux with control logic")
a = sram(c, "sram4")
self.local_check(a, final_verification=True)
# testing sram using pbitcell in various port combinations
# layout for multiple ports does not work yet
"""
OPTS.netlist_only = True
c.num_words=16
c.words_per_row=1
OPTS.num_rw_ports = 2
OPTS.num_w_ports = 2
OPTS.num_r_ports = 2
debug.info(1, "Single bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
"""
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 2
OPTS.num_r_ports = 2
debug.info(1, "Single bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
OPTS.num_rw_ports = 2
OPTS.num_w_ports = 0
OPTS.num_r_ports = 2
debug.info(1, "Single bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
OPTS.num_rw_ports = 2
OPTS.num_w_ports = 2
OPTS.num_r_ports = 0
debug.info(1, "Single bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
OPTS.num_rw_ports = 2
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
debug.info(1, "Single bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
# testing with various column muxes
OPTS.num_rw_ports = c.num_rw_ports = 2
OPTS.num_w_ports = c.num_w_ports = 2
OPTS.num_r_ports = c.num_r_ports = 2
c.num_words=32
c.words_per_row=2
debug.info(1, "Single bank two way column mux with control logic")
a = sram(c, "sram2")
self.local_check(a, final_verification=True)
c.num_words=64
c.words_per_row=4
debug.info(1, "Single bank, four way column mux with control logic")
a = sram(c, "sram3")
self.local_check(a, final_verification=True)
c.word_size=2
c.num_words=128
c.words_per_row=8
debug.info(1, "Single bank, eight way column mux with control logic")
a = sram(c, "sram4")
self.local_check(a, final_verification=True)
"""
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main()
@@ -20,6 +20,9 @@ class psram_1bank_2mux_func_test(openram_test):
OPTS.netlist_only = True
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@@ -20,6 +20,9 @@ class psram_1bank_4mux_func_test(openram_test):
OPTS.netlist_only = True
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test")
#@unittest.skip("SKIPPING 22_psram_1bank_8mux_func_test")
class psram_1bank_8mux_func_test(openram_test):
def runTest(self):
@@ -20,6 +20,9 @@ class psram_1bank_8mux_func_test(openram_test):
OPTS.netlist_only = True
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@@ -29,7 +32,7 @@ class psram_1bank_8mux_func_test(openram_test):
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=512,
num_words=256,
num_banks=1)
c.words_per_row=8
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
@@ -20,6 +20,9 @@ class psram_1bank_nomux_func_test(openram_test):
OPTS.netlist_only = True
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@@ -30,7 +30,7 @@ class sram_1bank_8mux_func_test(openram_test):
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=512,
num_words=256,
num_banks=1)
c.words_per_row=8
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
@@ -38,6 +38,7 @@ class sram_1bank_8mux_func_test(openram_test):
c.words_per_row,
c.num_banks))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
Run a functioal test on 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 22_sram_1rw_1r_1bank_nomux_func_test")
class psram_1bank_nomux_func_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import functional
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.words_per_row=1
debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner)
f.num_cycles = 10
(fail, error) = f.run()
self.assertTrue(fail,error)
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()
+1 -2
View File
@@ -100,7 +100,7 @@ def write_netgen_script(cell_name, sp_name):
os.system("chmod u+x {}".format(run_file))
def run_drc(cell_name, gds_name, extract=False, final_verification=False):
def run_drc(cell_name, gds_name, extract=True, final_verification=False):
"""Run DRC check on a cell which is implemented in gds_name."""
global num_drc_runs
@@ -166,7 +166,6 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
global num_lvs_runs
num_lvs_runs += 1
run_drc(cell_name, gds_name, extract=True, final_verification=final_verification)
write_netgen_script(cell_name, sp_name)
# run LVS