mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into datasheet_gen
This commit is contained in:
commit
781bd13cc1
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class replica_bitcell(design.design):
|
|||
|
||||
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
|
||||
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
|
||||
|
||||
def __init__(self):
|
||||
design.design.__init__(self, "replica_cell_6t")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
import design
|
||||
import debug
|
||||
import utils
|
||||
from tech import GDS,layer
|
||||
|
||||
class replica_bitcell_1rw_1r(design.design):
|
||||
"""
|
||||
A single bit cell which is forced to store a 0.
|
||||
This module implements the single memory cell used in the design. It
|
||||
is a hand-made cell, so the layout and netlist should be available in
|
||||
the technology library. """
|
||||
|
||||
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
|
||||
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
|
||||
|
||||
def __init__(self):
|
||||
design.design.__init__(self, "replica_cell_1rw_1r")
|
||||
debug.info(2, "Create replica bitcell 1rw+1r object")
|
||||
|
||||
self.width = replica_bitcell_1rw_1r.width
|
||||
self.height = replica_bitcell_1rw_1r.height
|
||||
self.pin_map = replica_bitcell_1rw_1r.pin_map
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 """
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class sense_amp(design.design):
|
|||
|
||||
pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"]
|
||||
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"], layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"])
|
||||
|
||||
def __init__(self, name):
|
||||
design.design.__init__(self, name)
|
||||
|
|
@ -25,6 +25,7 @@ class sense_amp(design.design):
|
|||
|
||||
def input_load(self):
|
||||
#Input load for the bitlines which are connected to the source/drain of a TX. Not the selects.
|
||||
from tech import spice, parameter
|
||||
bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file.
|
||||
return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ class single_level_column_mux_array(design.design):
|
|||
rotate=90)
|
||||
|
||||
def analytical_delay(self, vdd, slew, load=0.0):
|
||||
from tech import spice
|
||||
from tech import spice, parameter
|
||||
r = spice["min_tx_r"]/(self.mux.ptx_width/parameter["min_tx_size"])
|
||||
#Drains of mux transistors make up capacitance.
|
||||
c_para = spice["min_tx_drain_c"]*(self.mux.ptx_width/parameter["min_tx_size"])*self.words_per_row#ff
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -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])
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue