Merge branch 'dev' into datasheet_gen

This commit is contained in:
Jesse Cirimelli-Low 2018-11-07 10:08:45 -08:00
commit 781bd13cc1
128 changed files with 2171 additions and 1300 deletions

View File

@ -20,7 +20,7 @@ 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
# Internal 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
@ -32,21 +32,21 @@ All unit tests should pass first.
1. One time, create a GitHub account at http://github.com
2. Create a fork of the OpenRAM project on the github web page:
https://github.com/mguthaus/OpenRAM
https://github.com/vlsida/openram
It is on the upper right and says "Fork": This will make your own
OpenRAM repository on GitHub in your account.
3. Clone your repository (or use an existing cloned copy if you've
already done this once):
```
git clone https://github.com/<youruser>/OpenRAM.git
cd OpenRAM
git clone https://github.com/<youruser>/oepnram.git
cd openram
```
4. Set up a new upstream that points to MY OpenRAM repository that you
forked (only first time):
```
git remote add upstream https://github.com/mguthaus/OpenRAM.git
git remote add upstream https://github.com/vlsida/openram.git
```
You now have two remotes for this project:
* origin which points to your GitHub fork of the project. You can read

View File

@ -147,8 +147,12 @@ class instance(geometry):
self.width = 0
self.height = 0
else:
self.width = round_to_grid(mod.width)
self.height = round_to_grid(mod.height)
if mirror in ["R90","R270"] or rotate in [90,270]:
self.width = round_to_grid(mod.height)
self.height = round_to_grid(mod.width)
else:
self.width = round_to_grid(mod.width)
self.height = round_to_grid(mod.height)
self.compute_boundary(offset,mirror,rotate)
debug.info(4, "creating instance: " + self.name)

View File

@ -2,6 +2,7 @@ import debug
from tech import GDS, drc
from vector import vector
from tech import layer
import math
class pin_layout:
"""
@ -18,6 +19,10 @@ class pin_layout:
self.rect = [vector(rect[0]),vector(rect[1])]
# snap the rect to the grid
self.rect = [x.snap_to_grid() for x in self.rect]
debug.check(self.width()>0,"Zero width pin.")
debug.check(self.height()>0,"Zero height pin.")
# if it's a layer number look up the layer name. this assumes a unique layer number.
if type(layer_name_num)==int:
self.layer = list(layer.keys())[list(layer.values()).index(layer_name_num)]
@ -113,24 +118,45 @@ class pin_layout:
return y_overlaps
def xcontains(self, other):
""" Check if shape contains the x overlap """
(ll,ur) = self.rect
(oll,our) = other.rect
return (oll.x >= ll.x and our.x <= ur.x)
def ycontains(self, other):
""" Check if shape contains the y overlap """
(ll,ur) = self.rect
(oll,our) = other.rect
return (oll.y >= ll.y and our.y <= ur.y)
def contains(self, other):
""" Check if a shape contains another rectangle """
# If it is the same shape entirely, it is contained!
if self == other:
return True
# Can only overlap on the same layer
if self.layer != other.layer:
return False
(ll,ur) = self.rect
(oll,our) = other.rect
if not (oll.y >= ll.y and oll.y <= ur.y):
if not self.xcontains(other):
return False
if not (oll.x >= ll.x and oll.x <= ur.x):
if not self.ycontains(other):
return False
return True
def contained_by_any(self, shape_list):
""" Checks if shape is contained by any in the list """
for shape in shape_list:
if shape.contains(self):
return True
return False
def overlaps(self, other):
""" Check if a shape overlaps with a rectangle """
@ -274,3 +300,127 @@ class pin_layout:
magnification=GDS["zoom"],
rotate=None)
def compute_overlap(self, other):
""" Calculate the rectangular overlap of two rectangles. """
(r1_ll,r1_ur) = self.rect
(r2_ll,r2_ur) = other.rect
#ov_ur = vector(min(r1_ur.x,r2_ur.x),min(r1_ur.y,r2_ur.y))
#ov_ll = vector(max(r1_ll.x,r2_ll.x),max(r1_ll.y,r2_ll.y))
dy = min(r1_ur.y,r2_ur.y)-max(r1_ll.y,r2_ll.y)
dx = min(r1_ur.x,r2_ur.x)-max(r1_ll.x,r2_ll.x)
if dx>=0 and dy>=0:
return [dx,dy]
else:
return [0,0]
def overlap_length(self, other):
"""
Calculate the intersection segment and determine its length
"""
if self.contains(other):
return math.inf
elif other.contains(self):
return math.inf
else:
intersections = self.compute_overlap_segment(other)
# This is the common case where two pairs of edges overlap
# at two points, so just find the distance between those two points
if len(intersections)==2:
(p1,p2) = intersections
return math.sqrt(pow(p1[0]-p2[0],2) + pow(p1[1]-p2[1],2))
else:
# This is where we had a corner intersection or none
return 0
def compute_overlap_segment(self, other):
"""
Calculate the intersection segment of two rectangles
(if any)
"""
(r1_ll,r1_ur) = self.rect
(r2_ll,r2_ur) = other.rect
# The other corners besides ll and ur
r1_ul = vector(r1_ll.x, r1_ur.y)
r1_lr = vector(r1_ur.x, r1_ll.y)
r2_ul = vector(r2_ll.x, r2_ur.y)
r2_lr = vector(r2_ur.x, r2_ll.y)
from itertools import tee
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
# R1 edges CW
r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll]
r1_edges = []
for (p,q) in pairwise(r1_cw_points):
r1_edges.append([p,q])
# R2 edges CW
r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll]
r2_edges = []
for (p,q) in pairwise(r2_cw_points):
r2_edges.append([p,q])
# There are 4 edges on each rectangle
# so just brute force check intersection of each
# Two pairs of them should intersect
intersections = []
for r1e in r1_edges:
for r2e in r2_edges:
i = self.segment_intersection(r1e, r2e)
if i:
intersections.append(i)
return intersections
def on_segment(self, p, q, r):
"""
Given three co-linear points, determine if q lies on segment pr
"""
if q[0] <= max(p[0], r[0]) and \
q[0] >= min(p[0], r[0]) and \
q[1] <= max(p[1], r[1]) and \
q[1] >= min(p[1], r[1]):
return True
return False
def segment_intersection(self, s1, s2):
"""
Determine the intersection point of two segments
Return the a segment if they overlap.
Return None if they don't.
"""
(a,b) = s1
(c,d) = s2
# Line AB represented as a1x + b1y = c1
a1 = b.y - a.y
b1 = a.x - b.x
c1 = a1*a.x + b1*a.y
# Line CD represented as a2x + b2y = c2
a2 = d.y - c.y
b2 = c.x - d.x
c2 = a2*c.x + b2*c.y
determinant = a1*b2 - a2*b1
if determinant!=0:
x = (b2*c1 - b1*c2)/determinant
y = (a1*c2 - a2*c1)/determinant
r = [x,y]
if self.on_segment(a, r, b) and self.on_segment(c, r, d):
return [x, y]
return None

View File

@ -62,14 +62,10 @@ class route(design):
plist = list(pairwise(self.path))
for p0,p1 in plist:
if p0.z != p1.z: # via
# offset if not rotated
#via_offset = vector(p0.x+0.5*self.c.width,p0.y+0.5*self.c.height)
# offset if rotated
via_offset = vector(p0.x+0.5*self.c.height,p0.y-0.5*self.c.width)
via_size = [self.num_vias]*2
self.obj.add_via(self.layer_stack,via_offset,size=via_size,rotate=90)
self.obj.add_via_center(self.layer_stack,vector(p0.x,p0.y),size=via_size,rotate=90)
elif p0.x != p1.x and p0.y != p1.y: # diagonal!
debug.error("Non-changing direction!")
debug.error("Diagonal route! {}".format(self.path),-3)
else:
# this will draw an extra corner at the end but that is ok
self.draw_corner_wire(p1)

View File

@ -66,7 +66,7 @@ def get_gds_size(name, gds_filename, units, layer):
Open a GDS file and return the size from either the
bounding box or a border layer.
"""
debug.info(2,"Creating VLSI layout for {}".format(name))
debug.info(4,"Creating VLSI layout for {}".format(name))
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_filename)
@ -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))

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

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

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

View File

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

View File

@ -20,6 +20,10 @@ class functional(simulation):
def __init__(self, sram, spfile, corner):
simulation.__init__(self, sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests
if OPTS.is_unit_test:
random.seed(12345)
self.set_corner(corner)
self.set_spice_constants()
@ -31,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()

View File

@ -3,7 +3,6 @@ from design import design
import debug
import math
import tech
import random
from .stimuli import *
from .trim_spice import *
from .charutils import *
@ -209,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),

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

View File

@ -3,16 +3,9 @@ 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

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:

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

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)

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

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

View File

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

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

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)

View File

@ -1,4 +1,5 @@
from enum import Enum
from vector3d import vector3d
class direction(Enum):
NORTH = 1
@ -7,3 +8,60 @@ class direction(Enum):
WEST = 4
UP = 5
DOWN = 6
NORTHEAST = 7
NORTHWEST = 8
SOUTHEAST = 9
SOUTHWEST = 10
def get_offset(direct):
"""
Returns the vector offset for a given direction.
"""
if direct==direction.NORTH:
offset = vector3d(0,1,0)
elif direct==direction.SOUTH:
offset = vector3d(0,-1,0)
elif direct==direction.EAST:
offset = vector3d(1,0,0)
elif direct==direction.WEST:
offset = vector3d(-1,0,0)
elif direct==direction.UP:
offset = vector3d(0,0,1)
elif direct==direction.DOWN:
offset = vector3d(0,0,-1)
elif direct==direction.NORTHEAST:
offset = vector3d(1,1,0)
elif direct==direction.NORTHWEST:
offset = vector3d(-1,1,0)
elif direct==direction.SOUTHEAST:
offset = vector3d(1,-1,0)
elif direct==direction.SOUTHWEST:
offset = vector3d(-1,-1,0)
else:
debug.error("Invalid direction {}".format(direct))
return offset
def cardinal_directions(up_down_too=False):
temp_dirs = [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST]
if up_down_too:
temp_dirs.extend([direction.UP, direction.DOWN])
return temp_dirs
def cardinal_offsets(up_down_too=False):
return [direction.get_offset(d) for d in direction.cardinal_directions(up_down_too)]
def all_directions():
return [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST,
direction.NORTHEAST, direction.NORTHWEST, direction.SOUTHEAST, direction.SOUTHWEST]
def all_offsets():
return [direction.get_offset(d) for d in direction.all_directions()]
def all_neighbors(cell):
return [cell+x for x in direction.all_offsets()]
def cardinal_neighbors(cell):
return [cell+x for x in direction.cardinal_offsets()]

View File

@ -22,6 +22,11 @@ class grid_cell:
self.source=False
self.target=False
def get_cost(self):
# We can display the cost of the frontier
if self.min_cost > 0:
return self.min_cost
def get_type(self):
if self.blocked:
@ -36,8 +41,4 @@ class grid_cell:
if self.path:
return "P"
# We can display the cost of the frontier
if self.min_cost > 0:
return self.min_cost
return None

View File

@ -150,7 +150,7 @@ class grid_path:
return cost
def expand_dirs(self,up_down_too=True):
def expand_dirs(self):
"""
Expand from the end in each of the four cardinal directions plus up
or down but not expanding to blocked cells. Expands in all
@ -162,9 +162,7 @@ class grid_path:
"""
neighbors = []
for d in list(direction):
if not up_down_too and (d==direction.UP or d==direction.DOWN):
continue
for d in direction.cardinal_directions(True):
n = self.neighbor(d)
if n:
neighbors.append(n)
@ -172,20 +170,7 @@ class grid_path:
return neighbors
def neighbor(self, d):
if d==direction.EAST:
offset = vector3d(1,0,0)
elif d==direction.WEST:
offset = vector3d(-1,0,0)
elif d==direction.NORTH:
offset = vector3d(0,1,0)
elif d==direction.SOUTH:
offset = vector3d(0,-1,0)
elif d==direction.UP:
offset = vector3d(0,0,1)
elif d==direction.DOWN:
offset = vector3d(0,0,-1)
else:
debug.error("Invalid direction {}".format(d),-1)
offset = direction.get_offset(d)
newwave = [point + offset for point in self.pathlist[-1]]

View File

@ -6,31 +6,20 @@ import debug
from direction import direction
from vector3d import vector3d
def increment_set(curset, direct):
"""
Return the cells incremented in given direction
"""
if direct==direction.NORTH:
offset = vector3d(0,1,0)
elif direct==direction.SOUTH:
offset = vector3d(0,-1,0)
elif direct==direction.EAST:
offset = vector3d(1,0,0)
elif direct==direction.WEST:
offset = vector3d(-1,0,0)
elif direct==direction.UP:
offset = vector3d(0,0,1)
elif direct==direction.DOWN:
offset = vector3d(0,0,-1)
else:
debug.error("Invalid direction {}".format(dirct))
newset = set()
for c in curset:
newc = c+offset
newset.add(newc)
return newset
def increment_set(curset, direct):
"""
Return the cells incremented in given direction
"""
offset = direction.get_offset(direct)
newset = set()
for c in curset:
newc = c+offset
newset.add(newc)
return newset
def remove_border(curset, direct):
"""
@ -38,7 +27,7 @@ def remove_border(curset, direct):
"""
border = get_border(curset, direct)
curset.difference_update(border)
def get_upper_right(curset):
ur = None
@ -55,48 +44,48 @@ def get_lower_left(curset):
return ll
def get_border( curset, direct):
"""
Return the furthest cell(s) in a given direction.
"""
# find direction-most cell(s)
maxc = []
if direct==direction.NORTH:
for c in curset:
if len(maxc)==0 or c.y>maxc[0].y:
maxc = [c]
elif c.y==maxc[0].y:
maxc.append(c)
elif direct==direct.SOUTH:
for c in curset:
if len(maxc)==0 or c.y<maxc[0].y:
maxc = [c]
elif c.y==maxc[0].y:
maxc.append(c)
elif direct==direct.EAST:
for c in curset:
if len(maxc)==0 or c.x>maxc[0].x:
maxc = [c]
elif c.x==maxc[0].x:
maxc.append(c)
elif direct==direct.WEST:
for c in curset:
if len(maxc)==0 or c.x<maxc[0].x:
maxc = [c]
elif c.x==maxc[0].x:
maxc.append(c)
"""
Return the furthest cell(s) in a given direction.
"""
# find direction-most cell(s)
maxc = []
if direct==direction.NORTH:
for c in curset:
if len(maxc)==0 or c.y>maxc[0].y:
maxc = [c]
elif c.y==maxc[0].y:
maxc.append(c)
elif direct==direct.SOUTH:
for c in curset:
if len(maxc)==0 or c.y<maxc[0].y:
maxc = [c]
elif c.y==maxc[0].y:
maxc.append(c)
elif direct==direct.EAST:
for c in curset:
if len(maxc)==0 or c.x>maxc[0].x:
maxc = [c]
elif c.x==maxc[0].x:
maxc.append(c)
elif direct==direct.WEST:
for c in curset:
if len(maxc)==0 or c.x<maxc[0].x:
maxc = [c]
elif c.x==maxc[0].x:
maxc.append(c)
newset = set(maxc)
return newset
newset = set(maxc)
return newset
def expand_border(curset, direct):
"""
Expand the current set of sells in a given direction.
Only return the contiguous cells.
"""
border_set = get_border(curset, direct)
next_border_set = increment_set(border_set, direct)
return next_border_set
"""
Expand the current set of sells in a given direction.
Only return the contiguous cells.
"""
border_set = get_border(curset, direct)
next_border_set = increment_set(border_set, direct)
return next_border_set
def expand_borders(curset):
"""
@ -106,6 +95,47 @@ def expand_borders(curset):
south_set=expand_border(curset,direction.SOUTH)
east_set=expand_border(curset,direction.EAST)
west_set=expand_border(curset,direction.WEST)
return(north_set, east_set, south_set, west_set)
def inflate_cell(cell, distance):
"""
Expand the current cell in all directions and return the set.
"""
newset = set(cell)
if distance==0:
return(newset)
# recursively call this based on the distance
for offset in direction.all_offsets():
# FIXME: If distance is large this will be inefficient, but it is like 1 or 2
newset.update(inflate_cell(cell+offset,distance-1))
return newset
def inflate_set(curset, distance):
"""
Expand the set in all directions by the given number of grids.
"""
if distance<=0:
return curset
newset = curset.copy()
# Add all my neighbors
for c in curset:
newset.update(direction.all_neighbors(c))
# Recurse with less depth
return inflate_set(newset,distance-1)
def flatten_set(curset):
"""
Flatten until we have a set of vector3d objects.
"""
newset = set()
for c in curset:
if isinstance(c,vector3d):
newset.add(c)
else:
newset.update(flatten_set(c))
return newset

View File

@ -0,0 +1,657 @@
from direction import direction
from pin_layout import pin_layout
from vector3d import vector3d
from vector import vector
import grid_utils
from tech import drc
import debug
class pin_group:
"""
A class to represent a group of rectangular design pin.
It requires a router to define the track widths and blockages which
determine how pin shapes get mapped to tracks.
It is initially constructed with a single set of (touching) pins.
"""
def __init__(self, name, pin_set, router):
self.name = name
# Flag for when it is routed
self.routed = False
# Flag for when it is enclosed
self.enclosed = False
# Remove any redundant pins (i.e. contained in other pins)
irredundant_pin_set = self.remove_redundant_shapes(list(pin_set))
# This is a list because we can have a pin group of disconnected sets of pins
# and these are represented by separate lists
self.pins = [set(irredundant_pin_set)]
self.router = router
# These are the corresponding pin grids for each pin group.
self.grids = set()
# These are the secondary grids that could or could not be part of the pin
self.secondary_grids = set()
# The corresponding set of partially blocked grids for each pin group.
# These are blockages for other nets but unblocked for routing this group.
# These are also blockages if we used a simple enclosure to route to a rail.
self.blockages = set()
# This is a set of pin_layout shapes to cover the grids
self.enclosures = set()
def __str__(self):
""" override print function output """
total_string = "(pg {} ".format(self.name)
pin_string = "\n pins={}".format(self.pins)
total_string += pin_string
grids_string = "\n grids={}".format(self.grids)
total_string += grids_string
grids_string = "\n secondary={}".format(self.secondary_grids)
total_string += grids_string
if self.enclosed:
enlosure_string = "\n enclose={}".format(self.enclosures)
total_string += enclosure_string
total_string += ")"
return total_string
def __repr__(self):
""" override repr function output """
return str(self)
def size(self):
return len(self.grids)
def set_routed(self, value=True):
self.routed = value
def is_routed(self):
return self.routed
def pins_enclosed(self):
"""
Check if all of the pin shapes are enclosed.
Does not check if the DRC is correct, but just touching.
"""
for pin_list in self.pins:
pin_is_enclosed=False
for pin in pin_list:
if pin_is_enclosed:
break
for encosure in self.enclosures:
if pin.overlaps(enclosure):
pin_is_enclosed=True
break
else:
return False
return True
def remove_redundant_shapes(self, pin_list):
"""
Remove any pin layout that is contained within another.
Returns a new list without modifying pin_list.
"""
local_debug = False
if local_debug:
debug.info(0,"INITIAL: {}".format(pin_list))
# Make a copy of the list to start
new_pin_list = pin_list.copy()
remove_indices = set()
# This is n^2, but the number is small
for index1,pin1 in enumerate(pin_list):
# If we remove this pin, it can't contain other pins
if index1 in remove_indices:
continue
for index2,pin2 in enumerate(pin_list):
# Can't contain yourself, but compare the indices and not the pins
# so you can remove duplicate copies.
if index1==index2:
continue
# If we already removed it, can't remove it again...
if index2 in remove_indices:
continue
if pin1.contains(pin2):
if local_debug:
debug.info(0,"{0} contains {1}".format(pin1,pin2))
remove_indices.add(index2)
# Remove them in decreasing order to not invalidate the indices
for i in sorted(remove_indices, reverse=True):
del new_pin_list[i]
if local_debug:
debug.info(0,"FINAL : {}".format(new_pin_list))
return new_pin_list
# FIXME: This relies on some technology parameters from router which is not clean.
def compute_enclosures(self):
"""
Find the minimum rectangle enclosures of the given tracks.
"""
# Enumerate every possible enclosure
pin_list = []
for seed in self.grids:
(ll, ur) = self.enclose_pin_grids(seed, direction.NORTH, direction.EAST)
enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z)
pin_list.append(enclosure)
(ll, ur) = self.enclose_pin_grids(seed, direction.EAST, direction.NORTH)
enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z)
pin_list.append(enclosure)
# Now simplify the enclosure list
new_pin_list = self.remove_redundant_shapes(pin_list)
return new_pin_list
def compute_connector(self, pin, enclosure):
"""
Compute a shape to connect the pin to the enclosure shape.
This assumes the shape will be the dimension of the pin.
"""
if pin.xoverlaps(enclosure):
# Is it vertical overlap, extend pin shape to enclosure
plc = pin.lc()
prc = pin.rc()
elc = enclosure.lc()
erc = enclosure.rc()
ymin = min(plc.y,elc.y)
ymax = max(plc.y,elc.y)
ll = vector(plc.x, ymin)
ur = vector(prc.x, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
elif pin.yoverlaps(enclosure):
# Is it horizontal overlap, extend pin shape to enclosure
pbc = pin.bc()
puc = pin.uc()
ebc = enclosure.bc()
euc = enclosure.uc()
xmin = min(pbc.x,ebc.x)
xmax = max(pbc.x,ebc.x)
ll = vector(xmin, pbc.y)
ur = vector(xmax, puc.y)
p = pin_layout(pin.name, [ll, ur], pin.layer)
else:
# Neither, so we must do a corner-to corner
pc = pin.center()
ec = enclosure.center()
xmin = min(pc.x, ec.x)
xmax = max(pc.x, ec.x)
ymin = min(pc.y, ec.y)
ymax = max(pc.y, ec.y)
ll = vector(xmin, ymin)
ur = vector(xmax, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
return p
def find_above_connector(self, pin, enclosures):
"""
Find the enclosure that is to above the pin
and make a connector to it's upper edge.
"""
# Create the list of shapes that contain the pin edge
edge_list = []
for shape in enclosures:
if shape.xcontains(pin):
edge_list.append(shape)
# Sort them by their bottom edge
edge_list.sort(key=lambda x: x.by(), reverse=True)
# Find the bottom edge that is next to the pin's top edge
above_item = None
for item in edge_list:
if item.by()>=pin.uy():
above_item = item
else:
break
# There was nothing
if above_item==None:
return None
# If it already overlaps, no connector needed
if above_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, above_item)
return p
def find_below_connector(self, pin, enclosures):
"""
Find the enclosure that is below the pin
and make a connector to it's upper edge.
"""
# Create the list of shapes that contain the pin edge
edge_list = []
for shape in enclosures:
if shape.xcontains(pin):
edge_list.append(shape)
# Sort them by their upper edge
edge_list.sort(key=lambda x: x.uy())
# Find the upper edge that is next to the pin's bottom edge
bottom_item = None
for item in edge_list:
if item.uy()<=pin.by():
bottom_item = item
else:
break
# There was nothing to the left
if bottom_item==None:
return None
# If it already overlaps, no connector needed
if bottom_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, bottom_item)
return p
def find_left_connector(self, pin, enclosures):
"""
Find the enclosure that is to the left of the pin
and make a connector to it's right edge.
"""
# Create the list of shapes that contain the pin edge
edge_list = []
for shape in enclosures:
if shape.ycontains(pin):
edge_list.append(shape)
# Sort them by their right edge
edge_list.sort(key=lambda x: x.rx())
# Find the right edge that is to the pin's left edge
left_item = None
for item in edge_list:
if item.rx()<=pin.lx():
left_item = item
else:
break
# There was nothing to the left
if left_item==None:
return None
# If it already overlaps, no connector needed
if left_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, left_item)
return p
def find_right_connector(self, pin, enclosures):
"""
Find the enclosure that is to the right of the pin
and make a connector to it's left edge.
"""
# Create the list of shapes that contain the pin edge
edge_list = []
for shape in enclosures:
if shape.ycontains(pin):
edge_list.append(shape)
# Sort them by their right edge
edge_list.sort(key=lambda x: x.lx(), reverse=True)
# Find the left edge that is next to the pin's right edge
right_item = None
for item in edge_list:
if item.lx()>=pin.rx():
right_item = item
else:
break
# There was nothing to the right
if right_item==None:
return None
# If it already overlaps, no connector needed
if right_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, right_item)
return p
def find_smallest_connector(self, pin_list, shape_list):
"""
Compute all of the connectors between the overlapping pins and enclosure shape list..
Return the smallest.
"""
smallest = None
for pin in pin_list:
for enclosure in shape_list:
new_enclosure = self.compute_connector(pin, enclosure)
if smallest == None or new_enclosure.area()<smallest.area():
smallest = new_enclosure
return smallest
def find_smallest_overlapping(self, pin_list, shape_list):
"""
Find the smallest area shape in shape_list that overlaps with any
pin in pin_list by a min width.
"""
smallest_shape = None
for pin in pin_list:
overlap_shape = self.find_smallest_overlapping_pin(pin,shape_list)
if overlap_shape:
overlap_length = pin.overlap_length(overlap_shape)
if smallest_shape == None or overlap_shape.area()<smallest_shape.area():
smallest_shape = overlap_shape
return smallest_shape
def find_smallest_overlapping_pin(self, pin, shape_list):
"""
Find the smallest area shape in shape_list that overlaps with any
pin in pin_list by a min width.
"""
smallest_shape = None
zindex=self.router.get_zindex(pin.layer_num)
(min_width,min_space) = self.router.get_layer_width_space(zindex)
# Now compare it with every other shape to check how much they overlap
for other in shape_list:
overlap_length = pin.overlap_length(other)
if overlap_length > min_width:
if smallest_shape == None or other.area()<smallest_shape.area():
smallest_shape = other
return smallest_shape
def overlap_any_shape(self, pin_list, shape_list):
"""
Does the given pin overlap any of the shapes in the pin list.
"""
for pin in pin_list:
for other in shape_list:
if pin.overlaps(other):
return True
return False
def max_pin_layout(self, pin_list):
"""
Return the max area pin_layout
"""
biggest = pin_list[0]
for pin in pin_list:
if pin.area() > biggest.area():
biggest = pin
return pin
def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST):
"""
This encloses a single pin component with a rectangle
starting with the seed and expanding right until blocked
and then up until blocked.
dir1 and dir2 should be two orthogonal directions.
"""
offset1= direction.get_offset(dir1)
offset2= direction.get_offset(dir2)
# We may have started with an empty set
if not self.grids:
return None
# Start with the ll and make the widest row
row = [ll]
# Move in dir1 while we can
while True:
next_cell = row[-1] + offset1
# Can't move if not in the pin shape
if next_cell in self.grids and next_cell not in self.router.blocked_grids:
row.append(next_cell)
else:
break
# Move in dir2 while we can
while True:
next_row = [x+offset2 for x in row]
for cell in next_row:
# Can't move if any cell is not in the pin shape
if cell not in self.grids or cell in self.router.blocked_grids:
break
else:
row = next_row
# Skips the second break
continue
# Breaks from the nested break
break
# Add a shape from ll to ur
ur = row[-1]
return (ll,ur)
def enclose_pin(self):
"""
If there is one set of connected pin shapes,
this will find the smallest rectangle enclosure that overlaps with any pin.
If there is not, it simply returns all the enclosures.
"""
self.enclosed = True
# Compute the enclosure pin_layout list of the set of tracks
self.enclosures = self.compute_enclosures()
for pin_list in self.pins:
for pin in pin_list:
# If it is contained, it won't need a connector
if pin.contained_by_any(self.enclosures):
continue
left_connector = self.find_left_connector(pin, self.enclosures)
right_connector = self.find_right_connector(pin, self.enclosures)
above_connector = self.find_above_connector(pin, self.enclosures)
below_connector = self.find_below_connector(pin, self.enclosures)
for connector in [left_connector, right_connector, above_connector, below_connector]:
if connector:
self.enclosures.append(connector)
# Now, make sure each pin touches an enclosure. If not, add a connector.
# This could only happen when there was no enclosure in any cardinal direction from a pin
for pin_list in self.pins:
if not self.overlap_any_shape(pin_list, self.enclosures):
connector = self.find_smallest_connector(pin_list, self.enclosures)
debug.check(connector!=None, "Could not find a connector for {} with {}".format(pin_list, self.enclosures))
self.enclosures.append(connector)
debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
self.pins,
self.grids,
self.enclosures))
def combine_groups(self, pg1, pg2):
"""
Combine two pin groups into one.
"""
self.pins = [*pg1.pins, *pg2.pins] # Join the two lists of pins
self.grids = pg1.grids | pg2.grids # OR the set of grid locations
self.secondary_grids = pg1.secondary_grids | pg2.secondary_grids
def add_enclosure(self, cell):
"""
Add the enclosure shape to the given cell.
"""
for enclosure in self.enclosures:
debug.info(2,"Adding enclosure {0} {1}".format(self.name, enclosure))
cell.add_rect(layer=enclosure.layer,
offset=enclosure.ll(),
width=enclosure.width(),
height=enclosure.height())
def perimeter_grids(self):
"""
Return a list of the grids on the perimeter.
This assumes that we have a single contiguous shape.
"""
perimeter_set = set()
cardinal_offsets = direction.cardinal_offsets()
for g1 in self.grids:
neighbor_grids = [g1 + offset for offset in cardinal_offsets]
neighbor_count = sum([x in self.grids for x in neighbor_grids])
# If we aren't completely enclosed, we are on the perimeter
if neighbor_count < 4:
perimeter_set.add(g1)
return perimeter_set
def adjacent(self, other):
"""
Chck if the two pin groups have at least one adjacent pin grid.
"""
# We could optimize this to just check the boundaries
for g1 in self.perimeter_grids():
for g2 in other.perimeter_grids():
if g1.adjacent(g2):
return True
return False
def adjacent_grids(self, other, separation):
"""
Determine the sets of grids that are within a separation distance
of any grid in the other set.
"""
# We could optimize this to just check the boundaries
g1_grids = set()
g2_grids = set()
for g1 in self.grids:
for g2 in other.grids:
if g1.distance(g2) <= separation:
g1_grids.add(g1)
g2_grids.add(g2)
return g1_grids,g2_grids
def convert_pin(self):
"""
Convert the list of pin shapes into sets of routing grids.
The secondary set of grids are "optional" pin shapes that could be
should be either blocked or part of the pin.
"""
pin_set = set()
blockage_set = set()
for pin_list in self.pins:
for pin in pin_list:
debug.info(2," Converting {0}".format(pin))
# Determine which tracks the pin overlaps
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin)
pin_set.update(pin_in_tracks)
# Blockages will be a super-set of pins since it uses the inflated pin shape.
blockage_in_tracks = self.router.convert_blockage(pin)
blockage_set.update(blockage_in_tracks)
# If we have a blockage, we must remove the grids
# Remember, this excludes the pin blockages already
shared_set = pin_set & self.router.blocked_grids
if len(shared_set)>0:
debug.info(2,"Removing pins {}".format(shared_set))
pin_set.difference_update(self.router.blocked_grids)
shared_set = blockage_set & self.router.blocked_grids
if len(shared_set)>0:
debug.info(2,"Removing blocks {}".format(shared_set))
blockage_set.difference_update(self.router.blocked_grids)
# At least one of the groups must have some valid tracks
if (len(pin_set)==0 and len(blockage_set)==0):
self.write_debug_gds("blocked_pin.gds")
debug.error("Unable to find unblocked pin on grid.")
# We need to route each of the components, so don't combine the groups
self.grids = pin_set | blockage_set
# Remember the secondary grids for removing adjacent pins in wide metal spacing
self.secondary_grids = blockage_set - pin_set
debug.info(2," pins {}".format(self.grids))
debug.info(2," secondary {}".format(self.secondary_grids))
def recurse_simple_overlap_enclosure(self, start_set, direct):
"""
Recursive function to return set of tracks that connects to
the actual supply rail wire in a given direction (or terminating
when any track is no longer in the supply rail.
"""
next_set = grid_utils.expand_border(start_set, direct)
supply_tracks = self.router.supply_rail_tracks[self.name]
supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
supply_overlap = next_set & supply_tracks
wire_overlap = next_set & supply_wire_tracks
# If the rail overlap is the same, we are done, since we connected to the actual wire
if len(wire_overlap)==len(start_set):
new_set = start_set | wire_overlap
# If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region
elif len(supply_overlap)==len(start_set):
recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
new_set = start_set | supply_overlap | recurse_set
else:
# If we got no next set, we are done, can't expand!
new_set = set()
return new_set
def create_simple_overlap_enclosure(self, start_set):
"""
This takes a set of tracks that overlap a supply rail and creates an enclosure
that is ensured to overlap the supply rail wire.
It then adds rectangle(s) for the enclosure.
"""
additional_set = set()
# Check the layer of any element in the pin to determine which direction to route it
e = next(iter(start_set))
new_set = start_set.copy()
if e.z==0:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
else:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
# Expand the pin grid set to include some extra grids that connect the supply rail
self.grids.update(new_set)
# Add the inflated set so we don't get wide metal spacing issues (if it exists)
self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width))
# Add the polygon enclosures and set this pin group as routed
self.set_routed()
self.enclosures = self.compute_enclosures()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
from tech import drc,layer
from contact import contact
from pin_group import pin_group
from vector import vector
import debug
class router_tech:
"""
This is a class to hold the router tech constants.
"""
def __init__(self, layers):
"""
Allows us to change the layers that we are routing on. First layer
is always horizontal, middle is via, and last is always
vertical.
"""
self.layers = layers
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
# This is the minimum routed track spacing
via_connect = contact(self.layers, (1, 1))
self.max_via_size = max(via_connect.width,via_connect.height)
self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name))
self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name))
self.vert_layer_number = layer[self.vert_layer_name]
self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name))
self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name))
self.horiz_layer_number = layer[self.horiz_layer_name]
self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing
self.vert_track_width = self.max_via_size + self.vert_layer_spacing
# We'll keep horizontal and vertical tracks the same for simplicity.
self.track_width = max(self.horiz_track_width,self.vert_track_width)
debug.info(1,"Track width: "+str(self.track_width))
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]
def get_zindex(self,layer_num):
if layer_num==self.horiz_layer_number:
return 0
else:
return 1
def get_layer(self, zindex):
if zindex==1:
return self.vert_layer_name
elif zindex==0:
return self.horiz_layer_name
else:
debug.error("Invalid zindex {}".format(zindex),-1)
def get_layer_width_space(self, zindex, width=0, length=0):
"""
Return the width and spacing of a given layer
and wire of a given width and length.
"""
if zindex==1:
layer_name = self.vert_layer_name
elif zindex==0:
layer_name = self.horiz_layer_name
else:
debug.error("Invalid zindex for track", -1)
min_width = drc("minwidth_{0}".format(layer_name), width, length)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length)
return (min_width,min_spacing)

View File

@ -1,9 +1,10 @@
import gdsMill
import tech
from contact import contact
import math
import debug
from globals import OPTS
from contact import contact
from pin_group import pin_group
from pin_layout import pin_layout
from vector3d import vector3d
from router import router
@ -24,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 = {}
@ -40,6 +35,7 @@ class supply_router(router):
# Power rail width in grid units.
self.rail_track_width = 2
def create_routing_grid(self):
"""
@ -68,9 +64,12 @@ class supply_router(router):
# but this is simplest for now.
self.create_routing_grid()
# Compute the grid dimensions
self.compute_supply_rail_dimensions()
# Get the pin shapes
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
#self.write_debug_gds("pin_enclosures.gds",stop_program=False)
#self.write_debug_gds("pin_enclosures.gds",stop_program=True)
# Add the supply rails in a mesh network and connect H/V with vias
# Block everything
@ -83,100 +82,51 @@ class supply_router(router):
# Determine the rail locations
self.route_supply_rails(self.vdd_name,1)
#self.write_debug_gds("debug_rails.gds",stop_program=True)
remaining_vdd_pin_indices = self.route_simple_overlaps(vdd_name)
remaining_gnd_pin_indices = self.route_simple_overlaps(gnd_name)
#self.write_debug_gds("debug_simple_route.gds",stop_program=True)
self.route_simple_overlaps(vdd_name)
self.route_simple_overlaps(gnd_name)
#self.write_debug_gds("debug_simple_route.gds",stop_program=False)
# Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter
self.route_pins_to_rails(vdd_name, remaining_vdd_pin_indices)
self.route_pins_to_rails(gnd_name, remaining_gnd_pin_indices)
self.route_pins_to_rails(vdd_name)
self.route_pins_to_rails(gnd_name)
#self.write_debug_gds("debug_pin_routes.gds",stop_program=True)
#self.write_debug_gds("final.gds")
#self.write_debug_gds("final.gds",False)
return True
def route_simple_overlaps(self, pin_name):
"""
This checks for simple cases where a pin component already overlaps a supply rail.
It will add an enclosure to ensure the overlap in wide DRC rule cases.
"""
num_components = self.num_pin_components(pin_name)
remaining_pins = []
debug.info(1,"Routing simple overlap pins for {0}".format(pin_name))
# These are the wire tracks
wire_tracks = self.supply_rail_wire_tracks[pin_name]
# These are the wire and space tracks
supply_tracks = self.supply_rail_tracks[pin_name]
for index in range(num_components):
pin_in_tracks = self.pin_grids[pin_name][index]
common_set = supply_tracks & pin_in_tracks
if len(common_set)==0:
# if no overlap, add it to the complex route pins
remaining_pins.append(index)
else:
self.create_simple_overlap_enclosure(pin_name, common_set)
return remaining_pins
def recurse_simple_overlap_enclosure(self, pin_name, start_set, direct):
"""
Recursive function to return set of tracks that connects to
the actual supply rail wire in a given direction (or terminating
when any track is no longer in the supply rail.
"""
next_set = grid_utils.expand_border(start_set, direct)
supply_tracks = self.supply_rail_tracks[pin_name]
supply_wire_tracks = self.supply_rail_wire_tracks[pin_name]
supply_overlap = next_set & supply_tracks
wire_overlap = next_set & supply_wire_tracks
# If the rail overlap is the same, we are done, since we connected to the actual wire
if len(wire_overlap)==len(start_set):
new_set = start_set | wire_overlap
# If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region
elif len(supply_overlap)==len(start_set):
recurse_set = self.recurse_simple_overlap_enclosure(pin_name, supply_overlap, direct)
new_set = start_set | supply_overlap | recurse_set
else:
# If we got no next set, we are done, can't expand!
new_set = set()
for pg in self.pin_groups[pin_name]:
if pg.is_routed():
continue
return new_set
# First, check if we just overlap, if so, we are done.
overlap_grids = wire_tracks & pg.grids
if len(overlap_grids)>0:
pg.set_routed()
continue
# Else, if we overlap some of the space track, we can patch it with an enclosure
common_set = supply_tracks & pg.grids
if len(common_set)>0:
pg.create_simple_overlap_enclosure(common_set)
pg.add_enclosure(self.cell)
def create_simple_overlap_enclosure(self, pin_name, start_set):
"""
This takes a set of tracks that overlap a supply rail and creates an enclosure
that is ensured to overlap the supply rail wire.
It then adds rectangle(s) for the enclosure.
"""
additional_set = set()
# Check the layer of any element in the pin to determine which direction to route it
e = next(iter(start_set))
new_set = start_set.copy()
if e.z==0:
new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.NORTH)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.SOUTH)
else:
new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.EAST)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.WEST)
enclosure_list = self.compute_enclosures(new_set)
for pin in enclosure_list:
debug.info(2,"Adding simple overlap enclosure {0} {1}".format(pin_name, pin))
self.cell.add_rect(layer=pin.layer,
offset=pin.ll(),
width=pin.width(),
height=pin.height())
@ -228,9 +178,8 @@ class supply_router(router):
# the overlap area for placement of a via
overlap = new_r1 & new_r2
if len(overlap) >= self.supply_rail_wire_width**2:
debug.info(2,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap))
connections.add(i1)
connections.add(i2)
debug.info(3,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap))
connections.update([i1,i2])
via_areas.append(overlap)
# Go through and add the vias at the center of the intersection
@ -241,11 +190,12 @@ class supply_router(router):
self.add_via(center,self.rail_track_width)
# Determien which indices were not connected to anything above
all_indices = set([x for x in range(len(self.supply_rails[name]))])
missing_indices = all_indices ^ connections
missing_indices = set([x for x in range(len(self.supply_rails[name]))])
missing_indices.difference_update(connections)
# Go through and remove those disconnected indices
# (No via was added, so that doesn't need to be removed)
for rail_index in missing_indices:
for rail_index in sorted(missing_indices, reverse=True):
ll = grid_utils.get_lower_left(all_rails[rail_index])
ur = grid_utils.get_upper_right(all_rails[rail_index])
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur))
@ -268,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(),
@ -331,11 +281,12 @@ class supply_router(router):
# While we can keep expanding east in this horizontal track
while wave and wave[0].x < self.max_xoffset:
added_rail = self.find_supply_rail(name, wave, direction.EAST)
if added_rail:
wave = added_rail.neighbor(direction.EAST)
if not added_rail:
# Just seed with the next one
wave = [x+vector3d(1,0,0) for x in wave]
else:
wave = None
# Seed with the neighbor of the end of the last rail
wave = added_rail.neighbor(direction.EAST)
# Vertical supply rails
max_offset = self.rg.ur.x
@ -345,10 +296,12 @@ class supply_router(router):
# While we can keep expanding north in this vertical track
while wave and wave[0].y < self.max_yoffset:
added_rail = self.find_supply_rail(name, wave, direction.NORTH)
if added_rail:
wave = added_rail.neighbor(direction.NORTH)
if not added_rail:
# Just seed with the next one
wave = [x+vector3d(0,1,0) for x in wave]
else:
wave = None
# Seed with the neighbor of the end of the last rail
wave = added_rail.neighbor(direction.NORTH)
def find_supply_rail(self, name, seed_wave, direct):
"""
@ -356,15 +309,18 @@ class supply_router(router):
to contain a via, and, if so, add it.
"""
start_wave = self.find_supply_rail_start(name, seed_wave, direct)
# This means there were no more unblocked grids in the row/col
if not start_wave:
return None
wave_path = self.probe_supply_rail(name, start_wave, direct)
if self.approve_supply_rail(name, wave_path):
return wave_path
else:
return None
self.approve_supply_rail(name, wave_path)
# Return the rail whether we approved it or not,
# as it will be used to find the next start location
return wave_path
def find_supply_rail_start(self, name, seed_wave, direct):
"""
@ -431,10 +387,8 @@ 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 dimensions
self.compute_supply_rail_dimensions()
# Compute the grid locations of the supply rails
self.compute_supply_rails(name, supply_number)
@ -460,23 +414,27 @@ class supply_router(router):
self.supply_rail_wire_tracks[pin_name] = wire_set
def route_pins_to_rails(self, pin_name, remaining_component_indices):
def route_pins_to_rails(self, pin_name):
"""
This will route each of the remaining pin components to the supply rails.
After it is done, the cells are added to the pin blockage list.
"""
debug.info(1,"Pin {0} has {1} remaining components to route.".format(pin_name,
len(remaining_component_indices)))
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
debug.info(1,"Routing {0} with {1} pin components to route.".format(pin_name,
remaining_components))
recent_paths = []
# For every component
for index in remaining_component_indices:
debug.info(2,"Routing component {0} {1}".format(pin_name, index))
for index,pg in enumerate(self.pin_groups[pin_name]):
if pg.is_routed():
continue
debug.info(3,"Routing component {0} {1}".format(pin_name, index))
# Clear everything in the routing grid.
self.rg.reinit()
# This is inefficient since it is non-incremental, but it was
# easier to debug.
self.prepare_blockages(pin_name)
# Add the single component of the pin as the source
@ -487,23 +445,16 @@ class supply_router(router):
# Don't add the other pins, but we could?
self.add_supply_rail_target(pin_name)
# Add the previous paths as targets too
#self.add_path_target(recent_paths)
#print(self.rg.target)
# Actually run the A* router
if not self.run_router(detour_scale=5):
self.write_debug_gds()
recent_paths.append(self.paths[-1])
def add_supply_rail_target(self, pin_name):
"""
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
@ -514,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])

View File

@ -163,3 +163,21 @@ class vector3d():
""" Min of both values """
return vector3d(min(self.x,other.x),min(self.y,other.y),min(self.z,other.z))
def distance(self, other):
""" Return the planar distance between two values """
return abs(self.x-other.x)+abs(self.y-other.y)
def adjacent(self, other):
""" Is the one grid adjacent in any planar direction to the other """
if self == other + vector3d(1,0,0):
return True
elif self == other + vector3d(-1,0,0):
return True
elif self == other + vector3d(0,1,0):
return True
elif self == other + vector3d(0,-1,0):
return True
else:
return False

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)

View File

@ -116,7 +116,7 @@ def check_print_output(file_name):
return(count)
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -39,7 +39,7 @@ def setup_files():
return (gds_dir, gds_files)
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[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():
@ -59,7 +62,7 @@ def setup_files():
return (gds_dir, sp_dir, allnames)
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -43,7 +43,7 @@ class contact_test(openram_test):
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -84,7 +84,7 @@ class path_test(openram_test):
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -25,7 +25,7 @@ class ptx_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -25,7 +25,7 @@ class ptx_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -27,7 +27,7 @@ class ptx_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -27,7 +27,7 @@ class ptx_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -27,7 +27,7 @@ class ptx_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -27,7 +27,7 @@ class ptx_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -121,7 +121,7 @@ class wire_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -94,7 +94,7 @@ class pbitcell_test(openram_test):
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -25,7 +25,7 @@ class pinv_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -24,7 +24,7 @@ class pinv_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -23,7 +23,7 @@ class pinv_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -25,7 +25,7 @@ class pinv_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -23,7 +23,7 @@ class pinvbuf_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -27,7 +27,7 @@ class pnand2_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -27,7 +27,7 @@ class pnand3_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -26,7 +26,7 @@ class pnor2_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -39,7 +39,7 @@ class precharge_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -37,7 +37,7 @@ class replica_pbitcell_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -41,7 +41,7 @@ class single_level_column_mux_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -29,7 +29,7 @@ class bitcell_1rw_1r_array_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -25,7 +25,7 @@ class array_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -44,7 +44,7 @@ class pbitcell_array_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -69,7 +69,7 @@ class hierarchical_decoder_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -35,7 +35,7 @@ class hierarchical_predecode2x4_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -35,7 +35,7 @@ class hierarchical_predecode3x8_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -54,7 +54,7 @@ class single_level_column_mux_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -39,7 +39,7 @@ class precharge_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -37,7 +37,7 @@ class wordline_driver_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -42,7 +42,7 @@ class sense_amp_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -42,7 +42,7 @@ class write_driver_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -31,7 +31,7 @@ class dff_array_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -31,7 +31,7 @@ class dff_buf_array_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -23,7 +23,7 @@ class dff_buf_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -31,7 +31,7 @@ class dff_inv_array_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -23,7 +23,7 @@ class dff_inv_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -27,7 +27,7 @@ class tri_gate_array_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -23,7 +23,7 @@ class delay_chain_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

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))
@ -70,7 +90,7 @@ class replica_bitline_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

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,20 +40,20 @@ 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()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -40,7 +40,7 @@ class bank_select_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -49,7 +49,7 @@ class multi_bank_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -54,7 +54,7 @@ class multi_bank_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -137,7 +137,7 @@ class psingle_bank_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -48,7 +48,7 @@ class single_bank_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

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

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

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 20_sram_1bank_2mux_test")
#@unittest.skip("SKIPPING 20_sram_1bank_2mux_test")
class sram_1bank_2mux_test(openram_test):
def runTest(self):
@ -29,7 +29,7 @@ class sram_1bank_2mux_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 20_sram_1bank_2mux_test")
#@unittest.skip("SKIPPING 20_sram_1bank_4mux_test")
class sram_1bank_4mux_test(openram_test):
def runTest(self):
@ -29,7 +29,7 @@ class sram_1bank_4mux_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 20_sram_1bank_8mux_test")
#@unittest.skip("SKIPPING 20_sram_1bank_8mux_test")
class sram_1bank_8mux_test(openram_test):
def runTest(self):
@ -29,7 +29,7 @@ class sram_1bank_8mux_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -11,7 +11,8 @@ import globals
from globals import OPTS
import debug
class sram_1bank_test(openram_test):
#@unittest.skip("SKIPPING 20_sram_1bank_nomux_test")
class sram_1bank_nomux_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
@ -28,7 +29,7 @@ class sram_1bank_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -48,7 +48,7 @@ class sram_2bank_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -47,7 +47,7 @@ class sram_4bank_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -81,7 +81,7 @@ class timing_sram_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -52,7 +52,7 @@ class timing_setup_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -81,7 +81,7 @@ class timing_sram_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -53,7 +53,7 @@ class timing_setup_test(openram_test):
reload(characterizer)
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -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
@ -49,7 +52,7 @@ class psram_1bank_2mux_func_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test")
#@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test")
class psram_1bank_4mux_func_test(openram_test):
def runTest(self):
@ -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
@ -49,7 +52,7 @@ class psram_1bank_4mux_func_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -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,
@ -37,6 +40,7 @@ class psram_1bank_8mux_func_test(openram_test):
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])
@ -48,7 +52,7 @@ class psram_1bank_8mux_func_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -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
@ -49,7 +52,7 @@ class psram_1bank_nomux_func_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -47,7 +47,7 @@ class sram_1bank_2mux_func_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test")
#@unittest.skip("SKIPPING 22_sram_1bank_4mux_func_test")
class sram_1bank_4mux_func_test(openram_test):
def runTest(self):
@ -47,7 +47,7 @@ class sram_1bank_4mux_func_test(openram_test):
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test")
#@unittest.skip("SKIPPING 22_sram_1bank_8mux_func_test")
class sram_1bank_8mux_func_test(openram_test):
def runTest(self):
@ -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,8 @@ 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)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
@ -49,7 +51,7 @@ class sram_1bank_8mux_func_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

View File

@ -39,7 +39,7 @@ class sram_1bank_nomux_func_test(openram_test):
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()
@ -47,7 +47,7 @@ class sram_1bank_nomux_func_test(openram_test):
globals.end_openram()
# instantiate a copy of the class to actually run the test
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

Some files were not shown because too many files have changed in this diff Show More