mirror of https://github.com/VLSIDA/OpenRAM.git
OpenRAM v1.1.3
Mucho PEP8 corrections. Add layer-purpose to tech files. Enable colon delimited OPENRAM_TECH list. Add general config files for unit tests.
This commit is contained in:
commit
7556872a75
|
|
@ -167,7 +167,7 @@ class instance(geometry):
|
|||
|
||||
debug.info(4, "creating instance: " + self.name)
|
||||
|
||||
def get_blockages(self, layer, top=False):
|
||||
def get_blockages(self, lpp, top=False):
|
||||
""" Retrieve blockages of all modules in this instance.
|
||||
Apply the transform of the instance placement to give absolute blockages."""
|
||||
angle = math.radians(float(self.rotate))
|
||||
|
|
@ -191,11 +191,11 @@ class instance(geometry):
|
|||
if self.mod.is_library_cell:
|
||||
# Writes library cell blockages as shapes instead of a large metal blockage
|
||||
blockages = []
|
||||
blockages = self.mod.gds.getBlockages(layer)
|
||||
blockages = self.mod.gds.getBlockages(lpp)
|
||||
for b in blockages:
|
||||
new_blockages.append(self.transform_coords(b,self.offset, mirr, angle))
|
||||
else:
|
||||
blockages = self.mod.get_blockages(layer)
|
||||
blockages = self.mod.get_blockages(lpp)
|
||||
for b in blockages:
|
||||
new_blockages.append(self.transform_coords(b,self.offset, mirr, angle))
|
||||
return new_blockages
|
||||
|
|
@ -266,11 +266,12 @@ class instance(geometry):
|
|||
class path(geometry):
|
||||
"""Represents a Path"""
|
||||
|
||||
def __init__(self, layerNumber, coordinates, path_width):
|
||||
def __init__(self, lpp, coordinates, path_width):
|
||||
"""Initializes a path for the specified layer"""
|
||||
geometry.__init__(self)
|
||||
self.name = "path"
|
||||
self.layerNumber = layerNumber
|
||||
self.layerNumber = lpp[0]
|
||||
self.layerPurpose = lpp[1]
|
||||
self.coordinates = map(lambda x: [x[0], x[1]], coordinates)
|
||||
self.coordinates = vector(self.coordinates).snap_to_grid()
|
||||
self.path_width = path_width
|
||||
|
|
@ -283,7 +284,7 @@ class path(geometry):
|
|||
"""Writes the path to GDS"""
|
||||
debug.info(4, "writing path (" + str(self.layerNumber) + "): " + self.coordinates)
|
||||
new_layout.addPath(layerNumber=self.layerNumber,
|
||||
purposeNumber=0,
|
||||
purposeNumber=self.layerPurpose,
|
||||
coordinates=self.coordinates,
|
||||
width=self.path_width)
|
||||
|
||||
|
|
@ -303,12 +304,13 @@ class path(geometry):
|
|||
class label(geometry):
|
||||
"""Represents a text label"""
|
||||
|
||||
def __init__(self, text, layerNumber, offset, zoom=-1):
|
||||
def __init__(self, text, lpp, offset, zoom=-1):
|
||||
"""Initializes a text label for specified layer"""
|
||||
geometry.__init__(self)
|
||||
self.name = "label"
|
||||
self.text = text
|
||||
self.layerNumber = layerNumber
|
||||
self.layerNumber = lpp[0]
|
||||
self.layerPurpose = lpp[1]
|
||||
self.offset = vector(offset).snap_to_grid()
|
||||
|
||||
if zoom<0:
|
||||
|
|
@ -325,7 +327,7 @@ class label(geometry):
|
|||
debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text)
|
||||
new_layout.addText(text=self.text,
|
||||
layerNumber=self.layerNumber,
|
||||
purposeNumber=0,
|
||||
purposeNumber=self.layerPurpose,
|
||||
offsetInMicrons=self.offset,
|
||||
magnification=self.zoom,
|
||||
rotate=None)
|
||||
|
|
@ -346,11 +348,12 @@ class label(geometry):
|
|||
class rectangle(geometry):
|
||||
"""Represents a rectangular shape"""
|
||||
|
||||
def __init__(self, layerNumber, offset, width, height):
|
||||
def __init__(self, lpp, offset, width, height):
|
||||
"""Initializes a rectangular shape for specified layer"""
|
||||
geometry.__init__(self)
|
||||
self.name = "rect"
|
||||
self.layerNumber = layerNumber
|
||||
self.layerNumber = lpp[0]
|
||||
self.layerPurpose = lpp[1]
|
||||
self.offset = vector(offset).snap_to_grid()
|
||||
self.size = vector(width, height).snap_to_grid()
|
||||
self.width = round_to_grid(self.size.x)
|
||||
|
|
@ -374,7 +377,7 @@ class rectangle(geometry):
|
|||
debug.info(4, "writing rectangle (" + str(self.layerNumber) + "):"
|
||||
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
|
||||
new_layout.addBox(layerNumber=self.layerNumber,
|
||||
purposeNumber=0,
|
||||
purposeNumber=self.layerPurpose,
|
||||
offsetInMicrons=self.offset,
|
||||
width=self.width,
|
||||
height=self.height,
|
||||
|
|
|
|||
|
|
@ -150,9 +150,9 @@ class layout():
|
|||
if not height:
|
||||
height=drc["minwidth_{}".format(layer)]
|
||||
# negative layers indicate "unused" layers in a given technology
|
||||
layer_num = techlayer[layer]
|
||||
if layer_num >= 0:
|
||||
self.objs.append(geometry.rectangle(layer_num, offset, width, height))
|
||||
lpp = techlayer[layer]
|
||||
if lpp[0] >= 0:
|
||||
self.objs.append(geometry.rectangle(lpp, offset, width, height))
|
||||
return self.objs[-1]
|
||||
return None
|
||||
|
||||
|
|
@ -165,10 +165,10 @@ class layout():
|
|||
if not height:
|
||||
height=drc["minwidth_{}".format(layer)]
|
||||
# negative layers indicate "unused" layers in a given technology
|
||||
layer_num = techlayer[layer]
|
||||
lpp = techlayer[layer]
|
||||
corrected_offset = offset - vector(0.5*width,0.5*height)
|
||||
if layer_num >= 0:
|
||||
self.objs.append(geometry.rectangle(layer_num, corrected_offset, width, height))
|
||||
if lpp[0] >= 0:
|
||||
self.objs.append(geometry.rectangle(lpp, corrected_offset, width, height))
|
||||
return self.objs[-1]
|
||||
return None
|
||||
|
||||
|
|
@ -334,9 +334,9 @@ class layout():
|
|||
"""Adds a text label on the given layer,offset, and zoom level"""
|
||||
# negative layers indicate "unused" layers in a given technology
|
||||
debug.info(5,"add label " + str(text) + " " + layer + " " + str(offset))
|
||||
layer_num = techlayer[layer]
|
||||
if layer_num >= 0:
|
||||
self.objs.append(geometry.label(text, layer_num, offset, zoom))
|
||||
lpp = techlayer[layer]
|
||||
if lpp[0] >= 0:
|
||||
self.objs.append(geometry.label(text, lpp, offset, zoom))
|
||||
return self.objs[-1]
|
||||
return None
|
||||
|
||||
|
|
@ -347,9 +347,9 @@ class layout():
|
|||
import wire_path
|
||||
# NOTE: (UNTESTED) add_path(...) is currently not used
|
||||
# negative layers indicate "unused" layers in a given technology
|
||||
#layer_num = techlayer[layer]
|
||||
#if layer_num >= 0:
|
||||
# self.objs.append(geometry.path(layer_num, coordinates, width))
|
||||
#lpp = techlayer[layer]
|
||||
#if lpp[0] >= 0:
|
||||
# self.objs.append(geometry.path(lpp, coordinates, width))
|
||||
|
||||
wire_path.wire_path(obj=self,
|
||||
layer=layer,
|
||||
|
|
@ -539,21 +539,21 @@ class layout():
|
|||
Do not write the pins since they aren't obstructions.
|
||||
"""
|
||||
if type(layer)==str:
|
||||
layer_num = techlayer[layer]
|
||||
lpp = techlayer[layer]
|
||||
else:
|
||||
layer_num = layer
|
||||
lpp = layer
|
||||
|
||||
blockages = []
|
||||
for i in self.objs:
|
||||
blockages += i.get_blockages(layer_num)
|
||||
blockages += i.get_blockages(lpp)
|
||||
for i in self.insts:
|
||||
blockages += i.get_blockages(layer_num)
|
||||
blockages += i.get_blockages(lpp)
|
||||
# Must add pin blockages to non-top cells
|
||||
if not top_level:
|
||||
blockages += self.get_pin_blockages(layer_num)
|
||||
blockages += self.get_pin_blockages(lpp)
|
||||
return blockages
|
||||
|
||||
def get_pin_blockages(self, layer_num):
|
||||
def get_pin_blockages(self, lpp):
|
||||
""" Return the pin shapes as blockages for non-top-level blocks. """
|
||||
# FIXME: We don't have a body contact in ptx, so just ignore it for now
|
||||
import copy
|
||||
|
|
@ -565,33 +565,57 @@ class layout():
|
|||
for pin_name in pin_names:
|
||||
pin_list = self.get_pins(pin_name)
|
||||
for pin in pin_list:
|
||||
if pin.layer_num==layer_num:
|
||||
if pin.same_lpp(pin.lpp, lpp):
|
||||
blockages += [pin.rect]
|
||||
|
||||
return blockages
|
||||
|
||||
def create_horizontal_pin_bus(self, layer, pitch, offset, names, length):
|
||||
""" Create a horizontal bus of pins. """
|
||||
return self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=True)
|
||||
return self.create_bus(layer,
|
||||
pitch,
|
||||
offset,
|
||||
names,
|
||||
length,
|
||||
vertical=False,
|
||||
make_pins=True)
|
||||
|
||||
def create_vertical_pin_bus(self, layer, pitch, offset, names, length):
|
||||
""" Create a horizontal bus of pins. """
|
||||
return self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=True)
|
||||
return self.create_bus(layer,
|
||||
pitch,
|
||||
offset,
|
||||
names,
|
||||
length,
|
||||
vertical=True,
|
||||
make_pins=True)
|
||||
|
||||
def create_vertical_bus(self, layer, pitch, offset, names, length):
|
||||
""" Create a horizontal bus. """
|
||||
return self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=False)
|
||||
return self.create_bus(layer,
|
||||
pitch,
|
||||
offset,
|
||||
names,
|
||||
length,
|
||||
vertical=True,
|
||||
make_pins=False)
|
||||
|
||||
def create_horizontal_bus(self, layer, pitch, offset, names, length):
|
||||
""" Create a horizontal bus. """
|
||||
return self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=False)
|
||||
return self.create_bus(layer,
|
||||
pitch,
|
||||
offset,
|
||||
names,
|
||||
length,
|
||||
vertical=False,
|
||||
make_pins=False)
|
||||
|
||||
|
||||
def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins):
|
||||
"""
|
||||
"""
|
||||
Create a horizontal or vertical bus. It can be either just rectangles, or actual
|
||||
layout pins. It returns an map of line center line positions indexed by name.
|
||||
The other coordinate is a 0 since the bus provides a range.
|
||||
The other coordinate is a 0 since the bus provides a range.
|
||||
TODO: combine with channel router.
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -11,42 +11,55 @@ from vector import vector
|
|||
from tech import layer
|
||||
import math
|
||||
|
||||
|
||||
class pin_layout:
|
||||
"""
|
||||
A class to represent a rectangular design pin. It is limited to a
|
||||
single shape.
|
||||
"""
|
||||
|
||||
def __init__(self, name, rect, layer_name_num):
|
||||
def __init__(self, name, rect, layer_name_pp):
|
||||
self.name = name
|
||||
# repack the rect as a vector, just in case
|
||||
if type(rect[0])==vector:
|
||||
if type(rect[0]) == vector:
|
||||
self.rect = rect
|
||||
else:
|
||||
self.rect = [vector(rect[0]),vector(rect[1])]
|
||||
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.")
|
||||
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)]
|
||||
# if it's a string, use the name
|
||||
if type(layer_name_pp) == str:
|
||||
self.layer = layer_name_pp
|
||||
# else it is required to be a lpp
|
||||
else:
|
||||
self.layer=layer_name_num
|
||||
self.layer_num = layer[self.layer]
|
||||
for (layer_name, lpp) in layer.items():
|
||||
if self.same_lpp(layer_name_pp, lpp):
|
||||
self.layer = layer_name
|
||||
break
|
||||
else:
|
||||
debug.error("Couldn't find layer {}".format(layer_name_pp), -1)
|
||||
|
||||
self.lpp = layer[self.layer]
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "({} layer={} ll={} ur={})".format(self.name,self.layer,self.rect[0],self.rect[1])
|
||||
return "({} layer={} ll={} ur={})".format(self.name,
|
||||
self.layer,
|
||||
self.rect[0],
|
||||
self.rect[1])
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
override repr function output (don't include
|
||||
"""
|
||||
override repr function output (don't include
|
||||
name since pin shapes could have same shape but diff name e.g. blockage vs A)
|
||||
"""
|
||||
return "(layer={} ll={} ur={})".format(self.layer,self.rect[0],self.rect[1])
|
||||
return "(layer={} ll={} ur={})".format(self.layer,
|
||||
self.rect[0],
|
||||
self.rect[1])
|
||||
|
||||
def __hash__(self):
|
||||
""" Implement the hash function for sets etc. """
|
||||
|
|
@ -65,15 +78,15 @@ class pin_layout:
|
|||
def __eq__(self, other):
|
||||
""" Check if these are the same pins for duplicate checks """
|
||||
if isinstance(other, self.__class__):
|
||||
return (self.layer==other.layer and self.rect == other.rect)
|
||||
return (self.lpp == other.lpp and self.rect == other.rect)
|
||||
else:
|
||||
return False
|
||||
return False
|
||||
|
||||
def bbox(self, pin_list):
|
||||
"""
|
||||
Given a list of layout pins, create a bounding box layout.
|
||||
"""
|
||||
(ll, ur) = self.rect
|
||||
(ll, ur) = self.rect
|
||||
min_x = ll.x
|
||||
max_x = ur.x
|
||||
min_y = ll.y
|
||||
|
|
@ -85,17 +98,17 @@ class pin_layout:
|
|||
min_y = min(min_y, pin.ll().y)
|
||||
max_y = max(max_y, pin.ur().y)
|
||||
|
||||
self.rect = [vector(min_x,min_y),vector(max_x,max_y)]
|
||||
self.rect = [vector(min_x, min_y), vector(max_x, max_y)]
|
||||
|
||||
def inflate(self, spacing=None):
|
||||
"""
|
||||
Inflate the rectangle by the spacing (or other rule)
|
||||
and return the new rectangle.
|
||||
"""
|
||||
Inflate the rectangle by the spacing (or other rule)
|
||||
and return the new rectangle.
|
||||
"""
|
||||
if not spacing:
|
||||
spacing = 0.5*drc("{0}_to_{0}".format(self.layer))
|
||||
|
||||
(ll,ur) = self.rect
|
||||
(ll, ur) = self.rect
|
||||
spacing = vector(spacing, spacing)
|
||||
newll = ll - spacing
|
||||
newur = ur + spacing
|
||||
|
|
@ -104,20 +117,20 @@ class pin_layout:
|
|||
|
||||
def intersection(self, other):
|
||||
""" Check if a shape overlaps with a rectangle """
|
||||
(ll,ur) = self.rect
|
||||
(oll,our) = other.rect
|
||||
(ll, ur) = self.rect
|
||||
(oll, our) = other.rect
|
||||
|
||||
min_x = max(ll.x, oll.x)
|
||||
max_x = min(ll.x, oll.x)
|
||||
min_y = max(ll.y, oll.y)
|
||||
max_y = min(ll.y, oll.y)
|
||||
|
||||
return [vector(min_x,min_y),vector(max_x,max_y)]
|
||||
return [vector(min_x, min_y), vector(max_x, max_y)]
|
||||
|
||||
def xoverlaps(self, other):
|
||||
""" Check if shape has x overlap """
|
||||
(ll,ur) = self.rect
|
||||
(oll,our) = other.rect
|
||||
(ll, ur) = self.rect
|
||||
(oll, our) = other.rect
|
||||
x_overlaps = False
|
||||
# check if self is within other x range
|
||||
if (ll.x >= oll.x and ll.x <= our.x) or (ur.x >= oll.x and ur.x <= our.x):
|
||||
|
|
@ -130,8 +143,8 @@ class pin_layout:
|
|||
|
||||
def yoverlaps(self, other):
|
||||
""" Check if shape has x overlap """
|
||||
(ll,ur) = self.rect
|
||||
(oll,our) = other.rect
|
||||
(ll, ur) = self.rect
|
||||
(oll, our) = other.rect
|
||||
y_overlaps = False
|
||||
|
||||
# check if self is within other y range
|
||||
|
|
@ -145,15 +158,15 @@ class pin_layout:
|
|||
|
||||
def xcontains(self, other):
|
||||
""" Check if shape contains the x overlap """
|
||||
(ll,ur) = self.rect
|
||||
(oll,our) = other.rect
|
||||
(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
|
||||
(ll, ur) = self.rect
|
||||
(oll, our) = other.rect
|
||||
|
||||
return (oll.y >= ll.y and our.y <= ur.y)
|
||||
|
||||
|
|
@ -164,7 +177,7 @@ class pin_layout:
|
|||
return True
|
||||
|
||||
# Can only overlap on the same layer
|
||||
if self.layer != other.layer:
|
||||
if not self.same_lpp(self.lpp, other.lpp):
|
||||
return False
|
||||
|
||||
if not self.xcontains(other):
|
||||
|
|
@ -182,11 +195,10 @@ class pin_layout:
|
|||
return True
|
||||
return False
|
||||
|
||||
|
||||
def overlaps(self, other):
|
||||
""" Check if a shape overlaps with a rectangle """
|
||||
# Can only overlap on the same layer
|
||||
if self.layer != other.layer:
|
||||
if not self.same_lpp(self.lpp, other.lpp):
|
||||
return False
|
||||
|
||||
x_overlaps = self.xoverlaps(other)
|
||||
|
|
@ -208,40 +220,45 @@ class pin_layout:
|
|||
|
||||
def normalize(self):
|
||||
""" Re-find the LL and UR points after a transform """
|
||||
(first,second)=self.rect
|
||||
ll = vector(min(first[0],second[0]),min(first[1],second[1]))
|
||||
ur = vector(max(first[0],second[0]),max(first[1],second[1]))
|
||||
self.rect=[ll,ur]
|
||||
(first, second) = self.rect
|
||||
ll = vector(min(first[0], second[0]), min(first[1], second[1]))
|
||||
ur = vector(max(first[0], second[0]), max(first[1], second[1]))
|
||||
self.rect=[ll, ur]
|
||||
|
||||
def transform(self,offset,mirror,rotate):
|
||||
""" Transform with offset, mirror and rotation to get the absolute pin location.
|
||||
We must then re-find the ll and ur. The master is the cell instance. """
|
||||
(ll,ur) = self.rect
|
||||
if mirror=="MX":
|
||||
ll=ll.scale(1,-1)
|
||||
ur=ur.scale(1,-1)
|
||||
elif mirror=="MY":
|
||||
ll=ll.scale(-1,1)
|
||||
ur=ur.scale(-1,1)
|
||||
elif mirror=="XY":
|
||||
ll=ll.scale(-1,-1)
|
||||
ur=ur.scale(-1,-1)
|
||||
def transform(self, offset, mirror, rotate):
|
||||
"""
|
||||
Transform with offset, mirror and rotation
|
||||
to get the absolute pin location.
|
||||
We must then re-find the ll and ur.
|
||||
The master is the cell instance.
|
||||
"""
|
||||
(ll, ur) = self.rect
|
||||
if mirror == "MX":
|
||||
ll = ll.scale(1, -1)
|
||||
ur = ur.scale(1, -1)
|
||||
elif mirror == "MY":
|
||||
ll = ll.scale(-1, 1)
|
||||
ur = ur.scale(-1, 1)
|
||||
elif mirror == "XY":
|
||||
ll = ll.scale(-1, -1)
|
||||
ur = ur.scale(-1, -1)
|
||||
|
||||
if rotate==90:
|
||||
ll=ll.rotate_scale(-1,1)
|
||||
ur=ur.rotate_scale(-1,1)
|
||||
elif rotate==180:
|
||||
ll=ll.scale(-1,-1)
|
||||
ur=ur.scale(-1,-1)
|
||||
elif rotate==270:
|
||||
ll=ll.rotate_scale(1,-1)
|
||||
ur=ur.rotate_scale(1,-1)
|
||||
if rotate == 90:
|
||||
ll = ll.rotate_scale(-1, 1)
|
||||
ur = ur.rotate_scale(-1, 1)
|
||||
elif rotate == 180:
|
||||
ll = ll.scale(-1, -1)
|
||||
ur = ur.scale(-1, -1)
|
||||
elif rotate == 270:
|
||||
ll = ll.rotate_scale(1, -1)
|
||||
ur = ur.rotate_scale(1, -1)
|
||||
|
||||
self.rect=[offset+ll,offset+ur]
|
||||
self.rect = [offset + ll, offset + ur]
|
||||
self.normalize()
|
||||
|
||||
def center(self):
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),0.5*(self.rect[0].y+self.rect[1].y))
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),
|
||||
0.5*(self.rect[0].y+self.rect[1].y))
|
||||
|
||||
def cx(self):
|
||||
""" Center x """
|
||||
|
|
@ -258,17 +275,17 @@ class pin_layout:
|
|||
|
||||
def ul(self):
|
||||
""" Upper left point """
|
||||
return vector(self.rect[0].x,self.rect[1].y)
|
||||
return vector(self.rect[0].x, self.rect[1].y)
|
||||
|
||||
def lr(self):
|
||||
""" Lower right point """
|
||||
return vector(self.rect[1].x,self.rect[0].y)
|
||||
return vector(self.rect[1].x, self.rect[0].y)
|
||||
|
||||
def ur(self):
|
||||
""" Upper right point """
|
||||
return self.rect[1]
|
||||
|
||||
# The possible y edge values
|
||||
# The possible y edge values
|
||||
def uy(self):
|
||||
""" Upper y value """
|
||||
return self.rect[1].y
|
||||
|
|
@ -287,67 +304,71 @@ class pin_layout:
|
|||
""" Right x value """
|
||||
return self.rect[1].x
|
||||
|
||||
|
||||
# The edge centers
|
||||
def rc(self):
|
||||
""" Right center point """
|
||||
return vector(self.rect[1].x,0.5*(self.rect[0].y+self.rect[1].y))
|
||||
return vector(self.rect[1].x,
|
||||
0.5*(self.rect[0].y+self.rect[1].y))
|
||||
|
||||
def lc(self):
|
||||
""" Left center point """
|
||||
return vector(self.rect[0].x,0.5*(self.rect[0].y+self.rect[1].y))
|
||||
return vector(self.rect[0].x,
|
||||
0.5*(self.rect[0].y+self.rect[1].y))
|
||||
|
||||
def uc(self):
|
||||
""" Upper center point """
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),self.rect[1].y)
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),
|
||||
self.rect[1].y)
|
||||
|
||||
def bc(self):
|
||||
""" Bottom center point """
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),self.rect[0].y)
|
||||
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),
|
||||
self.rect[0].y)
|
||||
|
||||
def gds_write_file(self, newLayout):
|
||||
"""Writes the pin shape and label to GDS"""
|
||||
debug.info(4, "writing pin (" + str(self.layer) + "):"
|
||||
+ str(self.width()) + "x" + str(self.height()) + " @ " + str(self.ll()))
|
||||
newLayout.addBox(layerNumber=layer[self.layer],
|
||||
purposeNumber=0,
|
||||
debug.info(4, "writing pin (" + str(self.layer) + "):"
|
||||
+ str(self.width()) + "x"
|
||||
+ str(self.height()) + " @ " + str(self.ll()))
|
||||
(layer_num, purpose) = layer[self.layer]
|
||||
newLayout.addBox(layerNumber=layer_num,
|
||||
purposeNumber=purpose,
|
||||
offsetInMicrons=self.ll(),
|
||||
width=self.width(),
|
||||
height=self.height(),
|
||||
center=False)
|
||||
# Add the tet in the middle of the pin.
|
||||
# This fixes some pin label offsetting when GDS gets imported into Magic.
|
||||
# This fixes some pin label offsetting when GDS gets
|
||||
# imported into Magic.
|
||||
newLayout.addText(text=self.name,
|
||||
layerNumber=layer[self.layer],
|
||||
purposeNumber=0,
|
||||
layerNumber=layer_num,
|
||||
purposeNumber=purpose,
|
||||
offsetInMicrons=self.center(),
|
||||
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
|
||||
(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))
|
||||
# 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)
|
||||
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]
|
||||
if dx >= 0 and dy >= 0:
|
||||
return [dx, dy]
|
||||
else:
|
||||
return [0,0]
|
||||
return [0, 0]
|
||||
|
||||
def distance(self, other):
|
||||
"""
|
||||
"""
|
||||
Calculate the distance to another pin layout.
|
||||
"""
|
||||
(r1_ll,r1_ur) = self.rect
|
||||
(r2_ll,r2_ur) = other.rect
|
||||
(r1_ll, r1_ur) = self.rect
|
||||
(r2_ll, r2_ur) = other.rect
|
||||
|
||||
def dist(x1, y1, x2, y2):
|
||||
return math.sqrt((x2-x1)**2 + (y2-y1)**2)
|
||||
|
|
@ -368,7 +389,7 @@ class pin_layout:
|
|||
elif left:
|
||||
return r1_ll.x - r2_ur.x
|
||||
elif right:
|
||||
return r2_ll.x - r1.ur.x
|
||||
return r2_ll.x - r1_ur.x
|
||||
elif bottom:
|
||||
return r1_ll.y - r2_ur.y
|
||||
elif top:
|
||||
|
|
@ -377,9 +398,8 @@ class pin_layout:
|
|||
# rectangles intersect
|
||||
return 0
|
||||
|
||||
|
||||
def overlap_length(self, other):
|
||||
"""
|
||||
"""
|
||||
Calculate the intersection segment and determine its length
|
||||
"""
|
||||
|
||||
|
|
@ -391,21 +411,21 @@ class pin_layout:
|
|||
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))
|
||||
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
|
||||
"""
|
||||
Calculate the intersection segment of two rectangles
|
||||
(if any)
|
||||
"""
|
||||
(r1_ll,r1_ur) = self.rect
|
||||
(r2_ll,r2_ur) = other.rect
|
||||
(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)
|
||||
|
|
@ -414,6 +434,7 @@ class pin_layout:
|
|||
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)
|
||||
|
|
@ -423,14 +444,14 @@ class pin_layout:
|
|||
# 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])
|
||||
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])
|
||||
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
|
||||
|
|
@ -452,18 +473,18 @@ class pin_layout:
|
|||
q.x >= min(p.x, r.x) and \
|
||||
q.y <= max(p.y, r.y) and \
|
||||
q.y >= min(p.y, r.y):
|
||||
return True
|
||||
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
|
||||
(a, b) = s1
|
||||
(c, d) = s2
|
||||
# Line AB represented as a1x + b1y = c1
|
||||
a1 = b.y - a.y
|
||||
b1 = a.x - b.x
|
||||
|
|
@ -476,12 +497,22 @@ class pin_layout:
|
|||
|
||||
determinant = a1*b2 - a2*b1
|
||||
|
||||
if determinant!=0:
|
||||
if determinant != 0:
|
||||
x = (b2*c1 - b1*c2)/determinant
|
||||
y = (a1*c2 - a2*c1)/determinant
|
||||
|
||||
r = vector(x,y).snap_to_grid()
|
||||
r = vector(x, y).snap_to_grid()
|
||||
if self.on_segment(a, r, b) and self.on_segment(c, r, d):
|
||||
return r
|
||||
|
||||
return None
|
||||
|
||||
def same_lpp(self, lpp1, lpp2):
|
||||
"""
|
||||
Check if the layers and purposes are the same.
|
||||
Ignore if purpose is a None.
|
||||
"""
|
||||
if lpp1[1] == None or lpp2[1] == None:
|
||||
return lpp1[0] == lpp2[0]
|
||||
|
||||
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import os
|
||||
import gdsMill
|
||||
import tech
|
||||
import math
|
||||
|
|
@ -16,6 +15,7 @@ from pin_layout import pin_layout
|
|||
|
||||
OPTS = globals.OPTS
|
||||
|
||||
|
||||
def ceil(decimal):
|
||||
"""
|
||||
Performs a ceiling function on the decimal place specified by the DRC grid.
|
||||
|
|
@ -23,29 +23,35 @@ def ceil(decimal):
|
|||
grid = tech.drc["grid"]
|
||||
return math.ceil(decimal * 1 / grid) / (1 / grid)
|
||||
|
||||
|
||||
def round_to_grid(number):
|
||||
"""
|
||||
Rounds an arbitrary number to the grid.
|
||||
"""
|
||||
grid = tech.drc["grid"]
|
||||
grid = tech.drc["grid"]
|
||||
# this gets the nearest integer value
|
||||
number_grid = int(round(round((number / grid), 2), 0))
|
||||
number_off = number_grid * grid
|
||||
return number_off
|
||||
|
||||
|
||||
def snap_to_grid(offset):
|
||||
"""
|
||||
Changes the coodrinate to match the grid settings
|
||||
"""
|
||||
return [round_to_grid(offset[0]),round_to_grid(offset[1])]
|
||||
return [round_to_grid(offset[0]),
|
||||
round_to_grid(offset[1])]
|
||||
|
||||
|
||||
def pin_center(boundary):
|
||||
"""
|
||||
This returns the center of a pin shape in the vlsiLayout border format.
|
||||
"""
|
||||
return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])]
|
||||
return [0.5 * (boundary[0] + boundary[2]),
|
||||
0.5 * (boundary[1] + boundary[3])]
|
||||
|
||||
def auto_measure_libcell(pin_list, name, units, layer):
|
||||
|
||||
def auto_measure_libcell(pin_list, name, units, lpp):
|
||||
"""
|
||||
Open a GDS file and find the pins in pin_list as text on a given layer.
|
||||
Return these as a set of properties including the cell width/height too.
|
||||
|
|
@ -56,43 +62,42 @@ def auto_measure_libcell(pin_list, name, units, layer):
|
|||
reader.loadFromFile(cell_gds)
|
||||
|
||||
cell = {}
|
||||
measure_result = cell_vlsi.getLayoutBorder(layer)
|
||||
if measure_result == None:
|
||||
measure_result = cell_vlsi.getLayoutBorder(lpp[0])
|
||||
if measure_result:
|
||||
measure_result = cell_vlsi.measureSize(name)
|
||||
[cell["width"], cell["height"]] = measure_result
|
||||
|
||||
for pin in pin_list:
|
||||
(name,layer,boundary)=cell_vlsi.getPinShapeByLabel(str(pin))
|
||||
(name, lpp, boundary) = cell_vlsi.getPinShapeByLabel(str(pin))
|
||||
cell[str(pin)] = pin_center(boundary)
|
||||
return cell
|
||||
|
||||
|
||||
|
||||
def get_gds_size(name, gds_filename, units, layer):
|
||||
def get_gds_size(name, gds_filename, units, lpp):
|
||||
"""
|
||||
Open a GDS file and return the size from either the
|
||||
bounding box or a border layer.
|
||||
"""
|
||||
debug.info(4,"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)
|
||||
|
||||
cell = {}
|
||||
measure_result = cell_vlsi.getLayoutBorder(layer)
|
||||
if measure_result == None:
|
||||
debug.info(2,"Layout border failed. Trying to measure size for {}".format(name))
|
||||
measure_result = cell_vlsi.getLayoutBorder(lpp)
|
||||
if not measure_result:
|
||||
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
|
||||
measure_result = cell_vlsi.measureSize(name)
|
||||
# returns width,height
|
||||
return measure_result
|
||||
|
||||
def get_libcell_size(name, units, layer):
|
||||
|
||||
def get_libcell_size(name, units, lpp):
|
||||
"""
|
||||
Open a GDS file and return the library cell size from either the
|
||||
bounding box or a border layer.
|
||||
"""
|
||||
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
|
||||
return(get_gds_size(name, cell_gds, units, layer))
|
||||
return(get_gds_size(name, cell_gds, units, lpp))
|
||||
|
||||
|
||||
def get_gds_pins(pin_names, name, gds_filename, units):
|
||||
|
|
@ -106,15 +111,18 @@ def get_gds_pins(pin_names, name, gds_filename, units):
|
|||
|
||||
cell = {}
|
||||
for pin_name in pin_names:
|
||||
cell[str(pin_name)]=[]
|
||||
pin_list=cell_vlsi.getPinShape(str(pin_name))
|
||||
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_name)].append(pin_layout(pin_name, rect, layer))
|
||||
(lpp, 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_name)].append(pin_layout(pin_name, rect, lpp))
|
||||
return cell
|
||||
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ def check(check, str):
|
|||
log("ERROR: file {0}: line {1}: {2}\n".format(
|
||||
os.path.basename(filename), line_number, str))
|
||||
|
||||
if globals.OPTS.debug_level > 0:
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
assert 0
|
||||
|
||||
|
||||
|
|
@ -37,6 +40,9 @@ def error(str, return_value=0):
|
|||
log("ERROR: file {0}: line {1}: {2}\n".format(
|
||||
os.path.basename(filename), line_number, str))
|
||||
|
||||
if globals.OPTS.debug_level > 0:
|
||||
import pdb
|
||||
pdb.set_trace()
|
||||
assert return_value == 0
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -686,10 +686,6 @@ class Gds2reader:
|
|||
if idBits==('\x07','\x00'): break; #we've reached the end of the structure
|
||||
elif(idBits==('\x06','\x06')):
|
||||
structName = self.stripNonASCII(record[2::]) #(record[2:1] + record[1::]).rstrip()
|
||||
# print(''.[x for x in structName if ord(x) < 128])
|
||||
# stripped = (c for c in structName if 0 < ord(c) < 127)
|
||||
# structName = "".join(stripped)
|
||||
# print(self.stripNonASCII(structName)) ##FIXME: trimming by Tom g. ##could be an issue here with string trimming!
|
||||
thisStructure.name = structName
|
||||
if(findStructName==thisStructure.name):
|
||||
wantedStruct=1
|
||||
|
|
@ -767,10 +763,6 @@ class Gds2reader:
|
|||
if idBits==('\x07','\x00'): break; #we've reached the end of the structure
|
||||
elif(idBits==('\x06','\x06')):
|
||||
structName = self.stripNonASCII(record[2::]) #(record[2:1] + record[1::]).rstrip()
|
||||
# print(''.[x for x in structName if ord(x) < 128])
|
||||
# stripped = (c for c in structName if 0 < ord(c) < 127)
|
||||
# structName = "".join(stripped)
|
||||
# print(self.stripNonASCIIx(structName)) ##FIXME: trimming by Tom g. ##could be an issue here with string trimming!
|
||||
thisStructure.name = structName
|
||||
if(self.debugToTerminal==1):
|
||||
print("\tStructure Name: "+structName)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ class VlsiLayout:
|
|||
self.layerNumbersInUse = []
|
||||
self.debug = False
|
||||
if name:
|
||||
self.rootStructureName=name
|
||||
#take the root structure and copy it to a new structure with the new name
|
||||
self.rootStructureName=self.padText(name)
|
||||
#create the ROOT structure
|
||||
self.structures[self.rootStructureName] = GdsStructure()
|
||||
self.structures[self.rootStructureName].name = name
|
||||
|
|
@ -82,13 +83,9 @@ class VlsiLayout:
|
|||
return coordinatesRotate
|
||||
|
||||
def rename(self,newName):
|
||||
#make sure the newName is a multiple of 2 characters
|
||||
if(len(newName)%2 == 1):
|
||||
#pad with a zero
|
||||
newName = newName + '\x00'
|
||||
#take the root structure and copy it to a new structure with the new name
|
||||
self.structures[newName] = self.structures[self.rootStructureName]
|
||||
self.structures[newName].name = newName
|
||||
self.structures[newName].name = self.padText(newName)
|
||||
#and delete the old root
|
||||
del self.structures[self.rootStructureName]
|
||||
self.rootStructureName = newName
|
||||
|
|
@ -159,13 +156,14 @@ class VlsiLayout:
|
|||
debug.check(len(structureNames)==1,"Multiple possible root structures in the layout: {}".format(str(structureNames)))
|
||||
self.rootStructureName = structureNames[0]
|
||||
|
||||
|
||||
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None,
|
||||
transformPath = [], rotateAngle = 0, transFlags = [0,0,0], coordinates = (0,0)):
|
||||
#since this is a recursive function, must deal with the default
|
||||
#parameters explicitly
|
||||
if startingStructureName == None:
|
||||
startingStructureName = self.rootStructureName
|
||||
|
||||
|
||||
#set up the rotation matrix
|
||||
if(rotateAngle == None or rotateAngle == ""):
|
||||
angle = 0
|
||||
|
|
@ -193,17 +191,21 @@ class VlsiLayout:
|
|||
delegateFunction(startingStructureName, transformPath)
|
||||
#starting with a particular structure, we will recursively traverse the tree
|
||||
#********might have to set the recursion level deeper for big layouts!
|
||||
if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others?
|
||||
#if so, go through each and call this function again
|
||||
#if not, return back to the caller (caller can be this function)
|
||||
for sref in self.structures[startingStructureName].srefs:
|
||||
#here, we are going to modify the sref coordinates based on the parent objects rotation
|
||||
self.traverseTheHierarchy(startingStructureName = sref.sName,
|
||||
delegateFunction = delegateFunction,
|
||||
transformPath = transformPath,
|
||||
rotateAngle = sref.rotateAngle,
|
||||
transFlags = sref.transFlags,
|
||||
coordinates = sref.coordinates)
|
||||
try:
|
||||
if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others?
|
||||
#if so, go through each and call this function again
|
||||
#if not, return back to the caller (caller can be this function)
|
||||
for sref in self.structures[startingStructureName].srefs:
|
||||
#here, we are going to modify the sref coordinates based on the parent objects rotation
|
||||
self.traverseTheHierarchy(startingStructureName = sref.sName,
|
||||
delegateFunction = delegateFunction,
|
||||
transformPath = transformPath,
|
||||
rotateAngle = sref.rotateAngle,
|
||||
transFlags = sref.transFlags,
|
||||
coordinates = sref.coordinates)
|
||||
except KeyError:
|
||||
debug.error("Could not find structure {} in GDS file.".format(startingStructureName),-1)
|
||||
|
||||
#MUST HANDLE AREFs HERE AS WELL
|
||||
#when we return, drop the last transform from the transformPath
|
||||
del transformPath[-1]
|
||||
|
|
@ -211,11 +213,11 @@ class VlsiLayout:
|
|||
|
||||
def initialize(self):
|
||||
self.deduceHierarchy()
|
||||
#self.traverseTheHierarchy()
|
||||
# self.traverseTheHierarchy()
|
||||
self.populateCoordinateMap()
|
||||
|
||||
for layerNumber in self.layerNumbersInUse:
|
||||
self.processLabelPins(layerNumber)
|
||||
self.processLabelPins((layerNumber, None))
|
||||
|
||||
|
||||
def populateCoordinateMap(self):
|
||||
|
|
@ -243,21 +245,24 @@ class VlsiLayout:
|
|||
self.xyTree.append((startingStructureName,origin,uVector,vVector))
|
||||
self.traverseTheHierarchy(delegateFunction = addToXyTree)
|
||||
|
||||
def microns(self,userUnits):
|
||||
def microns(self, userUnits):
|
||||
"""Utility function to convert user units to microns"""
|
||||
userUnit = self.units[1]/self.units[0]
|
||||
userUnitsPerMicron = userUnit / (userunit)
|
||||
userUnitsPerMicron = userUnit / userunit
|
||||
layoutUnitsPerMicron = userUnitsPerMicron / self.units[0]
|
||||
return userUnits / layoutUnitsPerMicron
|
||||
|
||||
def userUnits(self,microns):
|
||||
def userUnits(self, microns):
|
||||
"""Utility function to convert microns to user units"""
|
||||
userUnit = self.units[1]/self.units[0]
|
||||
#userUnitsPerMicron = userUnit / 1e-6
|
||||
# userUnitsPerMicron = userUnit / 1e-6
|
||||
userUnitsPerMicron = userUnit / (userUnit)
|
||||
layoutUnitsPerMicron = userUnitsPerMicron / self.units[0]
|
||||
#print("userUnit:",userUnit,"userUnitsPerMicron",userUnitsPerMicron,"layoutUnitsPerMicron",layoutUnitsPerMicron,[microns,microns*layoutUnitsPerMicron])
|
||||
return round(microns*layoutUnitsPerMicron,0)
|
||||
# print("userUnit:",userUnit,
|
||||
# "userUnitsPerMicron",userUnitsPerMicron,
|
||||
# "layoutUnitsPerMicron",layoutUnitsPerMicron,
|
||||
# [microns,microns*layoutUnitsPerMicron])
|
||||
return round(microns*layoutUnitsPerMicron, 0)
|
||||
|
||||
def changeRoot(self,newRoot, create=False):
|
||||
"""
|
||||
|
|
@ -384,7 +389,7 @@ class VlsiLayout:
|
|||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].boundaries.append(boundaryToAdd)
|
||||
|
||||
def addPath(self, layerNumber=0, purposeNumber = None, coordinates=[(0,0)], width=1.0):
|
||||
def addPath(self, layerNumber=0, purposeNumber=None, coordinates=[(0,0)], width=1.0):
|
||||
"""
|
||||
Method to add a path to a layout
|
||||
"""
|
||||
|
|
@ -396,24 +401,22 @@ class VlsiLayout:
|
|||
cY = self.userUnits(coordinate[1])
|
||||
layoutUnitCoordinates.append((cX,cY))
|
||||
pathToAdd = GdsPath()
|
||||
pathToAdd.drawingLayer=layerNumber
|
||||
pathToAdd.drawingLayer = layerNumber
|
||||
pathToAdd.purposeLayer = purposeNumber
|
||||
pathToAdd.pathWidth=widthInLayoutUnits
|
||||
pathToAdd.coordinates=layoutUnitCoordinates
|
||||
pathToAdd.pathWidth = widthInLayoutUnits
|
||||
pathToAdd.coordinates = layoutUnitCoordinates
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].paths.append(pathToAdd)
|
||||
|
||||
def addText(self, text, layerNumber=0, purposeNumber = None, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
|
||||
def addText(self, text, layerNumber=0, purposeNumber=None, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
|
||||
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
|
||||
textToAdd = GdsText()
|
||||
textToAdd.drawingLayer = layerNumber
|
||||
textToAdd.purposeLayer = purposeNumber
|
||||
textToAdd.dataType = 0
|
||||
textToAdd.coordinates = [offsetInLayoutUnits]
|
||||
textToAdd.transFlags = [0,0,0]
|
||||
if(len(text)%2 == 1):
|
||||
text = text + '\x00'
|
||||
textToAdd.textString = text
|
||||
textToAdd.transFlags = [0,0,0]
|
||||
textToAdd.textString = self.padText(text)
|
||||
#textToAdd.transFlags[1] = 1
|
||||
textToAdd.magFactor = magnification
|
||||
if rotate:
|
||||
|
|
@ -421,7 +424,13 @@ class VlsiLayout:
|
|||
textToAdd.rotateAngle = rotate
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].texts.append(textToAdd)
|
||||
|
||||
|
||||
def padText(self, text):
|
||||
if(len(text)%2 == 1):
|
||||
return text + '\x00'
|
||||
else:
|
||||
return text
|
||||
|
||||
def isBounded(self,testPoint,startPoint,endPoint):
|
||||
#these arguments are touples of (x,y) coordinates
|
||||
if testPoint == None:
|
||||
|
|
@ -587,41 +596,50 @@ class VlsiLayout:
|
|||
passFailIndex += 1
|
||||
print("Done\n\n")
|
||||
|
||||
def getLayoutBorder(self,borderlayer):
|
||||
cellSizeMicron=None
|
||||
def getLayoutBorder(self, lpp):
|
||||
cellSizeMicron = None
|
||||
for boundary in self.structures[self.rootStructureName].boundaries:
|
||||
if boundary.drawingLayer==borderlayer:
|
||||
if sameLPP((boundary.drawingLayer, boundary.purposeLayer),
|
||||
lpp):
|
||||
if self.debug:
|
||||
debug.info(1,"Find border "+str(boundary.coordinates))
|
||||
left_bottom=boundary.coordinates[0]
|
||||
right_top=boundary.coordinates[2]
|
||||
cellSize=[right_top[0]-left_bottom[0],right_top[1]-left_bottom[1]]
|
||||
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
|
||||
if not(cellSizeMicron):
|
||||
print("Error: "+str(self.rootStructureName)+".cell_size information not found yet")
|
||||
debug.info(1, "Find border "+str(boundary.coordinates))
|
||||
left_bottom = boundary.coordinates[0]
|
||||
right_top = boundary.coordinates[2]
|
||||
cellSize = [right_top[0]-left_bottom[0],
|
||||
right_top[1]-left_bottom[1]]
|
||||
cellSizeMicron = [cellSize[0]*self.units[0],
|
||||
cellSize[1]*self.units[0]]
|
||||
debug.check(cellSizeMicron,
|
||||
"Error: "+str(self.rootStructureName)+".cell_size information not found yet")
|
||||
|
||||
return cellSizeMicron
|
||||
|
||||
def measureSize(self,startStructure):
|
||||
self.rootStructureName=startStructure
|
||||
def measureSize(self, startStructure):
|
||||
self.rootStructureName = self.padText(startStructure)
|
||||
self.populateCoordinateMap()
|
||||
cellBoundary = [None, None, None, None]
|
||||
for TreeUnit in self.xyTree:
|
||||
cellBoundary=self.measureSizeInStructure(TreeUnit,cellBoundary)
|
||||
cellSize=[cellBoundary[2]-cellBoundary[0],cellBoundary[3]-cellBoundary[1]]
|
||||
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
|
||||
cellBoundary = self.measureSizeInStructure(TreeUnit, cellBoundary)
|
||||
cellSize = [cellBoundary[2]-cellBoundary[0],
|
||||
cellBoundary[3]-cellBoundary[1]]
|
||||
cellSizeMicron = [cellSize[0]*self.units[0],
|
||||
cellSize[1]*self.units[0]]
|
||||
return cellSizeMicron
|
||||
|
||||
def measureBoundary(self,startStructure):
|
||||
self.rootStructureName=startStructure
|
||||
def measureBoundary(self, startStructure):
|
||||
self.rootStructureName = self.padText(startStructure)
|
||||
self.populateCoordinateMap()
|
||||
cellBoundary = [None, None, None, None]
|
||||
for TreeUnit in self.xyTree:
|
||||
cellBoundary=self.measureSizeInStructure(TreeUnit,cellBoundary)
|
||||
return [[self.units[0]*cellBoundary[0],self.units[0]*cellBoundary[1]],
|
||||
[self.units[0]*cellBoundary[2],self.units[0]*cellBoundary[3]]]
|
||||
cellBoundary = self.measureSizeInStructure(TreeUnit, cellBoundary)
|
||||
return [[self.units[0]*cellBoundary[0],
|
||||
self.units[0]*cellBoundary[1]],
|
||||
[self.units[0]*cellBoundary[2],
|
||||
self.units[0]*cellBoundary[3]]]
|
||||
|
||||
def measureSizeInStructure(self,structure,cellBoundary):
|
||||
(structureName,structureOrigin,structureuVector,structurevVector)=structure
|
||||
def measureSizeInStructure(self, structure, cellBoundary):
|
||||
(structureName, structureOrigin,
|
||||
structureuVector, structurevVector) = structure
|
||||
for boundary in self.structures[str(structureName)].boundaries:
|
||||
left_bottom=boundary.coordinates[0]
|
||||
right_top=boundary.coordinates[2]
|
||||
|
|
@ -648,14 +666,14 @@ class VlsiLayout:
|
|||
cellBoundary[3]=right_top_Y
|
||||
return cellBoundary
|
||||
|
||||
|
||||
def getTexts(self, layer):
|
||||
def getTexts(self, lpp):
|
||||
"""
|
||||
Get all of the labels on a given layer only at the root level.
|
||||
"""
|
||||
text_list = []
|
||||
for Text in self.structures[self.rootStructureName].texts:
|
||||
if Text.drawingLayer == layer:
|
||||
if sameLPP((Text.drawingLayer, Text.purposeLayer),
|
||||
lpp):
|
||||
text_list.append(Text)
|
||||
return text_list
|
||||
|
||||
|
|
@ -671,9 +689,9 @@ class VlsiLayout:
|
|||
max_pin = None
|
||||
max_area = 0
|
||||
for pin in pin_list:
|
||||
(layer,boundary) = pin
|
||||
(layer, boundary) = pin
|
||||
new_area = boundaryArea(boundary)
|
||||
if max_pin == None or new_area>max_area:
|
||||
if not max_pin or new_area > max_area:
|
||||
max_pin = pin
|
||||
max_area = new_area
|
||||
max_pins.append(max_pin)
|
||||
|
|
@ -690,32 +708,33 @@ class VlsiLayout:
|
|||
pin_map = self.pins[pin_name]
|
||||
for pin_list in pin_map:
|
||||
for pin in pin_list:
|
||||
(pin_layer, boundary) = pin
|
||||
(pin_layer, boundary) = pin
|
||||
shape_list.append(pin)
|
||||
|
||||
return shape_list
|
||||
|
||||
|
||||
def processLabelPins(self, layer):
|
||||
def processLabelPins(self, lpp):
|
||||
"""
|
||||
Find all text labels and create a map to a list of shapes that
|
||||
they enclose on the given layer.
|
||||
"""
|
||||
# Get the labels on a layer in the root level
|
||||
labels = self.getTexts(layer)
|
||||
labels = self.getTexts(lpp)
|
||||
|
||||
# Get all of the shapes on the layer at all levels
|
||||
# and transform them to the current level
|
||||
shapes = self.getAllShapes(layer)
|
||||
shapes = self.getAllShapes(lpp)
|
||||
|
||||
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))
|
||||
if self.labelInRectangle(user_coordinate, boundary):
|
||||
pin_shapes.append((lpp, boundary))
|
||||
|
||||
label_text = label.textString
|
||||
|
||||
# Remove the padding if it exists
|
||||
if label_text[-1] == "\x00":
|
||||
label_text = label_text[0:-1]
|
||||
|
|
@ -725,81 +744,97 @@ class VlsiLayout:
|
|||
except KeyError:
|
||||
self.pins[label_text] = []
|
||||
self.pins[label_text].append(pin_shapes)
|
||||
|
||||
|
||||
def getBlockages(self,layer):
|
||||
def getBlockages(self, lpp):
|
||||
"""
|
||||
Return all blockages on a given layer in [coordinate 1, coordinate 2,...] format and
|
||||
Return all blockages on a given layer in
|
||||
[coordinate 1, coordinate 2,...] format and
|
||||
user units.
|
||||
"""
|
||||
blockages = []
|
||||
|
||||
shapes = self.getAllShapes(layer)
|
||||
shapes = self.getAllShapes(lpp)
|
||||
for boundary in shapes:
|
||||
vectors = []
|
||||
for i in range(0,len(boundary),2):
|
||||
vectors.append(vector(boundary[i],boundary[i+1]))
|
||||
for i in range(0, len(boundary), 2):
|
||||
vectors.append(vector(boundary[i], boundary[i+1]))
|
||||
blockages.append(vectors)
|
||||
return blockages
|
||||
|
||||
return blockages
|
||||
|
||||
def getAllShapes(self,layer):
|
||||
def getAllShapes(self, lpp):
|
||||
"""
|
||||
Return all shapes on a given layer in [llx, lly, urx, ury] format and user units for rectangles
|
||||
and [coordinate 1, coordinate 2,...] format and user units for polygons.
|
||||
Return all shapes on a given layer in [llx, lly, urx, ury]
|
||||
format and user units for rectangles
|
||||
and [coordinate 1, coordinate 2,...] format and user
|
||||
units for polygons.
|
||||
"""
|
||||
boundaries = set()
|
||||
for TreeUnit in self.xyTree:
|
||||
#print(TreeUnit[0])
|
||||
boundaries.update(self.getShapesInStructure(layer,TreeUnit))
|
||||
# print(TreeUnit[0])
|
||||
boundaries.update(self.getShapesInStructure(lpp, TreeUnit))
|
||||
|
||||
# Convert to user units
|
||||
user_boundaries = []
|
||||
for boundary in boundaries:
|
||||
boundaries_list = []
|
||||
for i in range(0,len(boundary)):
|
||||
for i in range(0, len(boundary)):
|
||||
boundaries_list.append(boundary[i]*self.units[0])
|
||||
user_boundaries.append(boundaries_list)
|
||||
return user_boundaries
|
||||
|
||||
|
||||
def getShapesInStructure(self,layer,structure):
|
||||
"""
|
||||
Go through all the shapes in a structure and return the list of shapes in
|
||||
the form [llx, lly, urx, ury] for rectangles and [coordinate 1, coordinate 2,...] for polygons.
|
||||
def getShapesInStructure(self, lpp, structure):
|
||||
"""
|
||||
(structureName,structureOrigin,structureuVector,structurevVector)=structure
|
||||
#print(structureName,"u",structureuVector.transpose(),"v",structurevVector.transpose(),"o",structureOrigin.transpose())
|
||||
Go through all the shapes in a structure and
|
||||
return the list of shapes in
|
||||
the form [llx, lly, urx, ury] for rectangles
|
||||
and [coordinate 1, coordinate 2,...] for polygons.
|
||||
"""
|
||||
(structureName, structureOrigin,
|
||||
structureuVector, structurevVector) = structure
|
||||
# print(structureName,
|
||||
# "u", structureuVector.transpose(),
|
||||
# "v",structurevVector.transpose(),
|
||||
# "o",structureOrigin.transpose())
|
||||
boundaries = []
|
||||
for boundary in self.structures[str(structureName)].boundaries:
|
||||
if layer==boundary.drawingLayer:
|
||||
if len(boundary.coordinates)!=5:
|
||||
if sameLPP((boundary.drawingLayer, boundary.purposeLayer),
|
||||
lpp):
|
||||
if len(boundary.coordinates) != 5:
|
||||
# if shape is a polygon (used in DFF)
|
||||
boundaryPolygon = []
|
||||
# Polygon is a list of coordinates going ccw
|
||||
for coord in range(0,len(boundary.coordinates)):
|
||||
for coord in range(0, len(boundary.coordinates)):
|
||||
boundaryPolygon.append(boundary.coordinates[coord][0])
|
||||
boundaryPolygon.append(boundary.coordinates[coord][1])
|
||||
# perform the rotation
|
||||
boundaryPolygon=self.transformPolygon(boundaryPolygon,structureuVector,structurevVector)
|
||||
# add the offset
|
||||
boundaryPolygon = self.transformPolygon(boundaryPolygon,
|
||||
structureuVector,
|
||||
structurevVector)
|
||||
# add the offset
|
||||
polygon = []
|
||||
for i in range(0,len(boundaryPolygon),2):
|
||||
polygon.append(boundaryPolygon[i]+structureOrigin[0].item())
|
||||
polygon.append(boundaryPolygon[i+1]+structureOrigin[1].item())
|
||||
for i in range(0, len(boundaryPolygon), 2):
|
||||
polygon.append(boundaryPolygon[i] + structureOrigin[0].item())
|
||||
polygon.append(boundaryPolygon[i+1] + structureOrigin[1].item())
|
||||
# make it a tuple
|
||||
polygon = tuple(polygon)
|
||||
boundaries.append(polygon)
|
||||
else:
|
||||
# else shape is a rectangle
|
||||
left_bottom=boundary.coordinates[0]
|
||||
right_top=boundary.coordinates[2]
|
||||
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 = [left_bottom[0], left_bottom[1],
|
||||
right_top[0], right_top[1]]
|
||||
# perform the rotation
|
||||
boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector)
|
||||
boundaryRect = self.transformRectangle(boundaryRect,
|
||||
structureuVector,
|
||||
structurevVector)
|
||||
# add the offset and make it a tuple
|
||||
boundaryRect=(boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(),
|
||||
boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item())
|
||||
boundaryRect = (boundaryRect[0]+structureOrigin[0].item(),
|
||||
boundaryRect[1]+structureOrigin[1].item(),
|
||||
boundaryRect[2]+structureOrigin[0].item(),
|
||||
boundaryRect[3]+structureOrigin[1].item())
|
||||
boundaries.append(boundaryRect)
|
||||
return boundaries
|
||||
|
||||
|
|
@ -861,6 +896,17 @@ class VlsiLayout:
|
|||
else:
|
||||
return False
|
||||
|
||||
|
||||
def sameLPP(lpp1, lpp2):
|
||||
"""
|
||||
Check if the layers and purposes are the same.
|
||||
Ignore if purpose is a None.
|
||||
"""
|
||||
if lpp1[1] == None or lpp2[1] == None:
|
||||
return lpp1[0] == lpp2[0]
|
||||
|
||||
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]
|
||||
|
||||
|
||||
def boundaryArea(A):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
"""
|
||||
This is called globals.py, but it actually parses all the arguments and performs
|
||||
the global OpenRAM setup as well.
|
||||
This is called globals.py, but it actually parses all the arguments
|
||||
and performs the global OpenRAM setup as well.
|
||||
"""
|
||||
import os
|
||||
import debug
|
||||
|
|
@ -19,12 +19,13 @@ import re
|
|||
import copy
|
||||
import importlib
|
||||
|
||||
VERSION = "1.1.0"
|
||||
VERSION = "1.1.3"
|
||||
NAME = "OpenRAM v{}".format(VERSION)
|
||||
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
||||
|
||||
OPTS = options.options()
|
||||
CHECKPOINT_OPTS=None
|
||||
CHECKPOINT_OPTS = None
|
||||
|
||||
|
||||
def parse_args():
|
||||
""" Parse the optional arguments for OpenRAM """
|
||||
|
|
@ -32,27 +33,55 @@ def parse_args():
|
|||
global OPTS
|
||||
|
||||
option_list = {
|
||||
optparse.make_option("-b", "--backannotated", action="store_true", dest="use_pex",
|
||||
optparse.make_option("-b",
|
||||
"--backannotated",
|
||||
action="store_true",
|
||||
dest="use_pex",
|
||||
help="Back annotate simulation"),
|
||||
optparse.make_option("-o", "--output", dest="output_name",
|
||||
help="Base output file name(s) prefix", metavar="FILE"),
|
||||
optparse.make_option("-p", "--outpath", dest="output_path",
|
||||
optparse.make_option("-o",
|
||||
"--output",
|
||||
dest="output_name",
|
||||
help="Base output file name(s) prefix",
|
||||
metavar="FILE"),
|
||||
optparse.make_option("-p", "--outpath",
|
||||
dest="output_path",
|
||||
help="Output file(s) location"),
|
||||
optparse.make_option("-i", "--inlinecheck", action="store_true",
|
||||
help="Enable inline LVS/DRC checks", dest="inline_lvsdrc"),
|
||||
optparse.make_option("-n", "--nocheck", action="store_false",
|
||||
help="Disable all LVS/DRC checks", dest="check_lvsdrc"),
|
||||
optparse.make_option("-v", "--verbose", action="count", dest="debug_level",
|
||||
optparse.make_option("-i",
|
||||
"--inlinecheck",
|
||||
action="store_true",
|
||||
help="Enable inline LVS/DRC checks",
|
||||
dest="inline_lvsdrc"),
|
||||
optparse.make_option("-n", "--nocheck",
|
||||
action="store_false",
|
||||
help="Disable all LVS/DRC checks",
|
||||
dest="check_lvsdrc"),
|
||||
optparse.make_option("-v",
|
||||
"--verbose",
|
||||
action="count",
|
||||
dest="debug_level",
|
||||
help="Increase the verbosity level"),
|
||||
optparse.make_option("-t", "--tech", dest="tech_name",
|
||||
optparse.make_option("-t",
|
||||
"--tech",
|
||||
dest="tech_name",
|
||||
help="Technology name"),
|
||||
optparse.make_option("-s", "--spice", dest="spice_name",
|
||||
optparse.make_option("-s",
|
||||
"--spice",
|
||||
dest="spice_name",
|
||||
help="Spice simulator executable name"),
|
||||
optparse.make_option("-r", "--remove_netlist_trimming", action="store_false", dest="trim_netlist",
|
||||
optparse.make_option("-r",
|
||||
"--remove_netlist_trimming",
|
||||
action="store_false",
|
||||
dest="trim_netlist",
|
||||
help="Disable removal of noncritical memory cells during characterization"),
|
||||
optparse.make_option("-c", "--characterize", action="store_false", dest="analytical_delay",
|
||||
optparse.make_option("-c",
|
||||
"--characterize",
|
||||
action="store_false",
|
||||
dest="analytical_delay",
|
||||
help="Perform characterization to calculate delays (default is analytical models)"),
|
||||
optparse.make_option("-d", "--dontpurge", action="store_false", dest="purge_temp",
|
||||
optparse.make_option("-d",
|
||||
"--dontpurge",
|
||||
action="store_false",
|
||||
dest="purge_temp",
|
||||
help="Don't purge the contents of the temp directory after a successful run")
|
||||
# -h --help is implicit.
|
||||
}
|
||||
|
|
@ -73,6 +102,7 @@ def parse_args():
|
|||
|
||||
return (options, args)
|
||||
|
||||
|
||||
def print_banner():
|
||||
""" Conditionally print the banner to stdout """
|
||||
global OPTS
|
||||
|
|
@ -117,13 +147,13 @@ def check_versions():
|
|||
except:
|
||||
OPTS.coverage = 0
|
||||
|
||||
|
||||
def init_openram(config_file, is_unit_test=True):
|
||||
"""Initialize the technology, paths, simulators, etc."""
|
||||
""" Initialize the technology, paths, simulators, etc. """
|
||||
|
||||
|
||||
check_versions()
|
||||
|
||||
debug.info(1,"Initializing OpenRAM...")
|
||||
debug.info(1, "Initializing OpenRAM...")
|
||||
|
||||
setup_paths()
|
||||
|
||||
|
|
@ -146,10 +176,11 @@ def init_openram(config_file, is_unit_test=True):
|
|||
# This is a hack. If we are running a unit test and have checkpointed
|
||||
# the options, load them rather than reading the config file.
|
||||
# This way, the configuration is reloaded at the start of every unit test.
|
||||
# If a unit test fails, we don't have to worry about restoring the old config values
|
||||
# If a unit test fails,
|
||||
# we don't have to worry about restoring the old config values
|
||||
# that may have been tested.
|
||||
if is_unit_test and CHECKPOINT_OPTS:
|
||||
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
|
||||
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
|
||||
return
|
||||
|
||||
# Import these to find the executables for checkpointing
|
||||
|
|
@ -159,7 +190,8 @@ def init_openram(config_file, is_unit_test=True):
|
|||
# after each unit test
|
||||
if not CHECKPOINT_OPTS:
|
||||
CHECKPOINT_OPTS = copy.copy(OPTS)
|
||||
|
||||
|
||||
|
||||
def setup_bitcell():
|
||||
"""
|
||||
Determine the correct custom or parameterized bitcell for the design.
|
||||
|
|
@ -169,30 +201,29 @@ def setup_bitcell():
|
|||
# If we have non-1rw ports,
|
||||
# and the user didn't over-ride the bitcell manually,
|
||||
# figure out the right bitcell to use
|
||||
if (OPTS.bitcell=="bitcell"):
|
||||
if (OPTS.bitcell == "bitcell"):
|
||||
|
||||
if (OPTS.num_rw_ports==1 and OPTS.num_w_ports==0 and OPTS.num_r_ports==0):
|
||||
if (OPTS.num_rw_ports == 1 and OPTS.num_w_ports == 0 and OPTS.num_r_ports == 0):
|
||||
OPTS.bitcell = "bitcell"
|
||||
OPTS.replica_bitcell = "replica_bitcell"
|
||||
OPTS.dummy_bitcell = "dummy_bitcell"
|
||||
else:
|
||||
ports = ""
|
||||
if OPTS.num_rw_ports>0:
|
||||
if OPTS.num_rw_ports > 0:
|
||||
ports += "{}rw_".format(OPTS.num_rw_ports)
|
||||
if OPTS.num_w_ports>0:
|
||||
if OPTS.num_w_ports > 0:
|
||||
ports += "{}w_".format(OPTS.num_w_ports)
|
||||
if OPTS.num_r_ports>0:
|
||||
if OPTS.num_r_ports > 0:
|
||||
ports += "{}r".format(OPTS.num_r_ports)
|
||||
|
||||
OPTS.bitcell = "bitcell_"+ports
|
||||
OPTS.replica_bitcell = "replica_bitcell_"+ports
|
||||
OPTS.dummy_bitcell = "dummy_bitcell_"+ports
|
||||
else:
|
||||
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
|
||||
OPTS.replica_bitcell = "dummy_" + OPTS.bitcell
|
||||
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
|
||||
OPTS.replica_bitcell = "dummy_" + OPTS.bitcell
|
||||
|
||||
# See if bitcell exists
|
||||
from importlib import find_loader
|
||||
try:
|
||||
__import__(OPTS.bitcell)
|
||||
__import__(OPTS.replica_bitcell)
|
||||
|
|
@ -206,7 +237,7 @@ def setup_bitcell():
|
|||
OPTS.replica_bitcell = "dummy_pbitcell"
|
||||
if not OPTS.is_unit_test:
|
||||
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
|
||||
debug.info(1,"Using bitcell: {}".format(OPTS.bitcell))
|
||||
debug.info(1, "Using bitcell: {}".format(OPTS.bitcell))
|
||||
|
||||
|
||||
def get_tool(tool_type, preferences, default_name=None):
|
||||
|
|
@ -215,63 +246,73 @@ def get_tool(tool_type, preferences, default_name=None):
|
|||
one selected and its full path. If default is specified,
|
||||
find that one only and error otherwise.
|
||||
"""
|
||||
debug.info(2,"Finding {} tool...".format(tool_type))
|
||||
debug.info(2, "Finding {} tool...".format(tool_type))
|
||||
|
||||
if default_name:
|
||||
exe_name=find_exe(default_name)
|
||||
exe_name = find_exe(default_name)
|
||||
if exe_name == None:
|
||||
debug.error("{0} not found. Cannot find {1} tool.".format(default_name,tool_type),2)
|
||||
debug.error("{0} not found. Cannot find {1} tool.".format(default_name,
|
||||
tool_type),
|
||||
2)
|
||||
else:
|
||||
debug.info(1, "Using {0}: {1}".format(tool_type,exe_name))
|
||||
return(default_name,exe_name)
|
||||
debug.info(1, "Using {0}: {1}".format(tool_type, exe_name))
|
||||
return(default_name, exe_name)
|
||||
else:
|
||||
for name in preferences:
|
||||
exe_name = find_exe(name)
|
||||
if exe_name != None:
|
||||
debug.info(1, "Using {0}: {1}".format(tool_type,exe_name))
|
||||
return(name,exe_name)
|
||||
debug.info(1, "Using {0}: {1}".format(tool_type, exe_name))
|
||||
return(name, exe_name)
|
||||
else:
|
||||
debug.info(1, "Could not find {0}, trying next {1} tool.".format(name,tool_type))
|
||||
debug.info(1,
|
||||
"Could not find {0}, trying next {1} tool.".format(name,
|
||||
tool_type))
|
||||
else:
|
||||
return(None,"")
|
||||
return(None, "")
|
||||
|
||||
|
||||
def read_config(config_file, is_unit_test=True):
|
||||
"""
|
||||
"""
|
||||
Read the configuration file that defines a few parameters. The
|
||||
config file is just a Python file that defines some config
|
||||
options. This will only actually get read the first time. Subsequent
|
||||
reads will just restore the previous copy (ask mrg)
|
||||
"""
|
||||
global OPTS
|
||||
|
||||
# Create a full path relative to current dir unless it is already an abs path
|
||||
|
||||
# it is already not an abs path, make it one
|
||||
if not os.path.isabs(config_file):
|
||||
config_file = os.getcwd() + "/" + config_file
|
||||
|
||||
# Make it a python file if the base name was only given
|
||||
config_file = re.sub(r'\.py$', "", config_file)
|
||||
|
||||
|
||||
# Expand the user if it is used
|
||||
config_file = os.path.expanduser(config_file)
|
||||
OPTS.config_file = config_file
|
||||
# Add the path to the system path so we can import things in the other directory
|
||||
|
||||
OPTS.config_file = config_file + ".py"
|
||||
# Add the path to the system path
|
||||
# so we can import things in the other directory
|
||||
dir_name = os.path.dirname(config_file)
|
||||
file_name = os.path.basename(config_file)
|
||||
module_name = os.path.basename(config_file)
|
||||
|
||||
# Prepend the path to avoid if we are using the example config
|
||||
sys.path.insert(0,dir_name)
|
||||
sys.path.insert(0, dir_name)
|
||||
# Import the configuration file of which modules to use
|
||||
debug.info(1, "Configuration file is " + config_file + ".py")
|
||||
try:
|
||||
config = importlib.import_module(file_name)
|
||||
config = importlib.import_module(module_name)
|
||||
except:
|
||||
debug.error("Unable to read configuration file: {0}".format(config_file),2)
|
||||
|
||||
for k,v in config.__dict__.items():
|
||||
for k, v in config.__dict__.items():
|
||||
# The command line will over-ride the config file
|
||||
# except in the case of the tech name! This is because the tech name
|
||||
# is sometimes used to specify the config file itself (e.g. unit tests)
|
||||
# Note that if we re-read a config file, nothing will get read again!
|
||||
if not k in OPTS.__dict__ or k=="tech_name":
|
||||
OPTS.__dict__[k]=v
|
||||
if k not in OPTS.__dict__ or k == "tech_name":
|
||||
OPTS.__dict__[k] = v
|
||||
|
||||
# Massage the output path to be an absolute one
|
||||
if not OPTS.output_path.endswith('/'):
|
||||
|
|
@ -281,20 +322,20 @@ def read_config(config_file, is_unit_test=True):
|
|||
debug.info(1, "Output saved in " + OPTS.output_path)
|
||||
|
||||
# Remember if we are running unit tests to reduce output
|
||||
OPTS.is_unit_test=is_unit_test
|
||||
OPTS.is_unit_test = is_unit_test
|
||||
|
||||
# If we are only generating a netlist, we can't do DRC/LVS
|
||||
if OPTS.netlist_only:
|
||||
OPTS.check_lvsdrc=False
|
||||
OPTS.check_lvsdrc = False
|
||||
|
||||
# If config didn't set output name, make a reasonable default.
|
||||
if (OPTS.output_name == ""):
|
||||
ports = ""
|
||||
if OPTS.num_rw_ports>0:
|
||||
if OPTS.num_rw_ports > 0:
|
||||
ports += "{}rw_".format(OPTS.num_rw_ports)
|
||||
if OPTS.num_w_ports>0:
|
||||
if OPTS.num_w_ports > 0:
|
||||
ports += "{}w_".format(OPTS.num_w_ports)
|
||||
if OPTS.num_r_ports>0:
|
||||
if OPTS.num_r_ports > 0:
|
||||
ports += "{}r_".format(OPTS.num_r_ports)
|
||||
OPTS.output_name = "sram_{0}b_{1}_{2}{3}".format(OPTS.word_size,
|
||||
OPTS.num_words,
|
||||
|
|
@ -302,8 +343,6 @@ def read_config(config_file, is_unit_test=True):
|
|||
OPTS.tech_name)
|
||||
|
||||
|
||||
|
||||
|
||||
def end_openram():
|
||||
""" Clean up openram for a proper exit """
|
||||
cleanup_paths()
|
||||
|
|
@ -312,9 +351,7 @@ def end_openram():
|
|||
import verify
|
||||
verify.print_drc_stats()
|
||||
verify.print_lvs_stats()
|
||||
verify.print_pex_stats()
|
||||
|
||||
|
||||
verify.print_pex_stats()
|
||||
|
||||
|
||||
def cleanup_paths():
|
||||
|
|
@ -323,40 +360,44 @@ def cleanup_paths():
|
|||
"""
|
||||
global OPTS
|
||||
if not OPTS.purge_temp:
|
||||
debug.info(0,"Preserving temp directory: {}".format(OPTS.openram_temp))
|
||||
debug.info(0,
|
||||
"Preserving temp directory: {}".format(OPTS.openram_temp))
|
||||
return
|
||||
elif os.path.exists(OPTS.openram_temp):
|
||||
debug.info(1,"Purging temp directory: {}".format(OPTS.openram_temp))
|
||||
# This annoyingly means you have to re-cd into the directory each debug iteration
|
||||
#shutil.rmtree(OPTS.openram_temp, ignore_errors=True)
|
||||
debug.info(1,
|
||||
"Purging temp directory: {}".format(OPTS.openram_temp))
|
||||
# This annoyingly means you have to re-cd into
|
||||
# the directory each debug iteration
|
||||
# shutil.rmtree(OPTS.openram_temp, ignore_errors=True)
|
||||
contents = [os.path.join(OPTS.openram_temp, i) for i in os.listdir(OPTS.openram_temp)]
|
||||
for i in contents:
|
||||
if os.path.isfile(i) or os.path.islink(i):
|
||||
os.remove(i)
|
||||
else:
|
||||
shutil.rmtree(i)
|
||||
|
||||
|
||||
|
||||
def setup_paths():
|
||||
""" Set up the non-tech related paths. """
|
||||
debug.info(2,"Setting up paths...")
|
||||
debug.info(2, "Setting up paths...")
|
||||
|
||||
global OPTS
|
||||
|
||||
try:
|
||||
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
|
||||
except:
|
||||
debug.error("$OPENRAM_HOME is not properly defined.",1)
|
||||
debug.check(os.path.isdir(OPENRAM_HOME),"$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME))
|
||||
debug.error("$OPENRAM_HOME is not properly defined.", 1)
|
||||
debug.check(os.path.isdir(OPENRAM_HOME),
|
||||
"$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME))
|
||||
|
||||
# Add all of the subdirs to the python path
|
||||
# These subdirs are modules and don't need to be added: characterizer, verify
|
||||
# These subdirs are modules and don't need
|
||||
# to be added: characterizer, verify
|
||||
subdirlist = [ item for item in os.listdir(OPENRAM_HOME) if os.path.isdir(os.path.join(OPENRAM_HOME, item)) ]
|
||||
for subdir in subdirlist:
|
||||
full_path = "{0}/{1}".format(OPENRAM_HOME,subdir)
|
||||
full_path = "{0}/{1}".format(OPENRAM_HOME, subdir)
|
||||
debug.check(os.path.isdir(full_path),
|
||||
"$OPENRAM_HOME/{0} does not exist: {1}".format(subdir,full_path))
|
||||
"$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path))
|
||||
sys.path.append("{0}".format(full_path))
|
||||
|
||||
if not OPTS.openram_temp.endswith('/'):
|
||||
|
|
@ -364,13 +405,16 @@ def setup_paths():
|
|||
debug.info(1, "Temporary files saved in " + OPTS.openram_temp)
|
||||
|
||||
|
||||
|
||||
def is_exe(fpath):
|
||||
""" Return true if the given is an executable file that exists. """
|
||||
return os.path.exists(fpath) and os.access(fpath, os.X_OK)
|
||||
|
||||
|
||||
def find_exe(check_exe):
|
||||
""" Check if the binary exists in any path dir and return the full path. """
|
||||
"""
|
||||
Check if the binary exists in any path dir
|
||||
and return the full path.
|
||||
"""
|
||||
# Check if the preferred spice option exists in the path
|
||||
for path in os.environ["PATH"].split(os.pathsep):
|
||||
exe = os.path.join(path, check_exe)
|
||||
|
|
@ -379,12 +423,14 @@ def find_exe(check_exe):
|
|||
return exe
|
||||
return None
|
||||
|
||||
|
||||
def init_paths():
|
||||
""" Create the temp and output directory if it doesn't exist """
|
||||
|
||||
# make the directory if it doesn't exist
|
||||
try:
|
||||
debug.info(1,"Creating temp directory: {}".format(OPTS.openram_temp))
|
||||
debug.info(1,
|
||||
"Creating temp directory: {}".format(OPTS.openram_temp))
|
||||
os.makedirs(OPTS.openram_temp, 0o750)
|
||||
except OSError as e:
|
||||
if e.errno == 17: # errno.EEXIST
|
||||
|
|
@ -398,8 +444,9 @@ def init_paths():
|
|||
if e.errno == 17: # errno.EEXIST
|
||||
os.chmod(OPTS.output_path, 0o750)
|
||||
except:
|
||||
debug.error("Unable to make output directory.",-1)
|
||||
debug.error("Unable to make output directory.", -1)
|
||||
|
||||
|
||||
def set_default_corner():
|
||||
""" Set the default corner. """
|
||||
|
||||
|
|
@ -416,17 +463,19 @@ def import_tech():
|
|||
""" Dynamically adds the tech directory to the path and imports it. """
|
||||
global OPTS
|
||||
|
||||
debug.info(2,"Importing technology: " + OPTS.tech_name)
|
||||
debug.info(2,
|
||||
"Importing technology: " + OPTS.tech_name)
|
||||
|
||||
# environment variable should point to the technology dir
|
||||
try:
|
||||
OPENRAM_TECH = os.path.abspath(os.environ.get("OPENRAM_TECH"))
|
||||
except:
|
||||
debug.error("$OPENRAM_TECH environment variable is not defined.",1)
|
||||
debug.error("$OPENRAM_TECH environment variable is not defined.", 1)
|
||||
|
||||
# Add all of the paths
|
||||
for tech_path in OPENRAM_TECH.split(":"):
|
||||
debug.check(os.path.isdir(tech_path),"$OPENRAM_TECH does not exist: {0}".format(tech_path))
|
||||
debug.check(os.path.isdir(tech_path),
|
||||
"$OPENRAM_TECH does not exist: {0}".format(tech_path))
|
||||
sys.path.append(tech_path)
|
||||
debug.info(1, "Adding technology path: {}".format(tech_path))
|
||||
|
||||
|
|
@ -438,7 +487,6 @@ def import_tech():
|
|||
|
||||
OPTS.openram_tech = os.path.dirname(tech_mod.__file__) + "/"
|
||||
|
||||
|
||||
# Add the tech directory
|
||||
tech_path = OPTS.openram_tech
|
||||
sys.path.append(tech_path)
|
||||
|
|
@ -453,22 +501,25 @@ def print_time(name, now_time, last_time=None, indentation=2):
|
|||
global OPTS
|
||||
|
||||
# Don't print during testing
|
||||
if not OPTS.is_unit_test or OPTS.debug_level>0:
|
||||
if not OPTS.is_unit_test or OPTS.debug_level > 0:
|
||||
if last_time:
|
||||
time = str(round((now_time-last_time).total_seconds(),1)) + " seconds"
|
||||
else:
|
||||
time = now_time.strftime('%m/%d/%Y %H:%M:%S')
|
||||
debug.print_raw("{0} {1}: {2}".format("*"*indentation,name,time))
|
||||
debug.print_raw("{0} {1}: {2}".format("*"*indentation, name, time))
|
||||
|
||||
|
||||
def report_status():
|
||||
""" Check for valid arguments and report the info about the SRAM being generated """
|
||||
"""
|
||||
Check for valid arguments and report the
|
||||
info about the SRAM being generated
|
||||
"""
|
||||
global OPTS
|
||||
|
||||
# Check if all arguments are integers for bits, size, banks
|
||||
if type(OPTS.word_size)!=int:
|
||||
if type(OPTS.word_size) != int:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.word_size))
|
||||
if type(OPTS.num_words)!=int:
|
||||
if type(OPTS.num_words) != int:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.sram_size))
|
||||
if type(OPTS.write_size) is not int and OPTS.write_size is not None:
|
||||
debug.error("{0} is not an integer in config file.".format(OPTS.write_size))
|
||||
|
|
@ -478,30 +529,29 @@ def report_status():
|
|||
if OPTS.write_size is not None:
|
||||
if (OPTS.word_size % OPTS.write_size != 0):
|
||||
debug.error("Write size needs to be an integer multiple of word size.")
|
||||
# If write size is more than half of the word size, then it doesn't need a write mask. It would be writing
|
||||
# If write size is more than half of the word size,
|
||||
# then it doesn't need a write mask. It would be writing
|
||||
# the whole word.
|
||||
if (OPTS.write_size < 1 or OPTS.write_size > OPTS.word_size/2):
|
||||
debug.error("Write size needs to be between 1 bit and {0} bits/2.".format(OPTS.word_size))
|
||||
|
||||
|
||||
|
||||
if not OPTS.tech_name:
|
||||
debug.error("Tech name must be specified in config file.")
|
||||
|
||||
debug.print_raw("Technology: {0}".format(OPTS.tech_name))
|
||||
total_size = OPTS.word_size*OPTS.num_words*OPTS.num_banks
|
||||
debug.print_raw("Total size: {} bits".format(total_size))
|
||||
if total_size>=2**14:
|
||||
if total_size >= 2**14:
|
||||
debug.warning("Requesting such a large memory size ({0}) will have a large run-time. ".format(total_size) +
|
||||
"Consider using multiple smaller banks.")
|
||||
debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
|
||||
OPTS.num_words,
|
||||
OPTS.num_banks))
|
||||
OPTS.num_words,
|
||||
OPTS.num_banks))
|
||||
if (OPTS.write_size != OPTS.word_size):
|
||||
debug.print_raw("Write size: {}".format(OPTS.write_size))
|
||||
debug.print_raw("RW ports: {0}\nR-only ports: {1}\nW-only ports: {2}".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports))
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports))
|
||||
|
||||
if OPTS.netlist_only:
|
||||
debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).")
|
||||
|
|
@ -518,7 +568,7 @@ def report_status():
|
|||
if OPTS.analytical_delay:
|
||||
debug.print_raw("Characterization is disabled (using analytical delay models) (analytical_delay=False to simulate).")
|
||||
else:
|
||||
if OPTS.spice_name!="":
|
||||
if OPTS.spice_name != "":
|
||||
debug.print_raw("Performing simulation-based characterization with {}".format(OPTS.spice_name))
|
||||
if OPTS.trim_netlist:
|
||||
debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).")
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import globals
|
||||
import design
|
||||
from math import log
|
||||
import design
|
||||
from tech import GDS,layer,spice,parameter
|
||||
from tech import GDS, layer, spice, parameter
|
||||
import utils
|
||||
|
||||
|
||||
class dff(design.design):
|
||||
"""
|
||||
Memory address flip-flop
|
||||
|
|
@ -19,7 +17,9 @@ class dff(design.design):
|
|||
|
||||
pin_names = ["D", "Q", "clk", "vdd", "gnd"]
|
||||
type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||
(width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"])
|
||||
(width, height) = utils.get_libcell_size("dff",
|
||||
GDS["unit"],
|
||||
layer["boundary"])
|
||||
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
|
||||
|
||||
def __init__(self, name="dff"):
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import debug
|
|||
import design
|
||||
from math import log
|
||||
from math import sqrt
|
||||
from math import ceil
|
||||
import math
|
||||
import contact
|
||||
from sram_factory import factory
|
||||
|
|
@ -31,7 +32,7 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
self.cell_height = height
|
||||
self.rows = rows
|
||||
self.num_inputs = int(math.log(self.rows, 2))
|
||||
self.num_inputs = math.ceil(math.log(self.rows, 2))
|
||||
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||
|
||||
self.create_netlist()
|
||||
|
|
@ -338,14 +339,15 @@ class hierarchical_decoder(design.design):
|
|||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
row = len(self.predec_groups[0])*j + i
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand2))
|
||||
pins =["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"Z_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(pins)
|
||||
if (row < self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand2))
|
||||
pins =["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"Z_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs >5.
|
||||
|
|
@ -356,16 +358,17 @@ class hierarchical_decoder(design.design):
|
|||
row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \
|
||||
+ len(self.predec_groups[0])*j + i
|
||||
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand3))
|
||||
if (row < self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand3))
|
||||
|
||||
pins = ["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||
"Z_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(pins)
|
||||
pins = ["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||
"Z_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def create_decoder_inv_array(self):
|
||||
|
|
@ -527,10 +530,11 @@ class hierarchical_decoder(design.design):
|
|||
for index_B in self.predec_groups[1]:
|
||||
for index_A in self.predec_groups[0]:
|
||||
# FIXME: convert to connect_bus?
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
if (row_index < self.rows):
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
row_index = row_index + 1
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
|
|
@ -538,12 +542,13 @@ class hierarchical_decoder(design.design):
|
|||
for index_B in self.predec_groups[1]:
|
||||
for index_A in self.predec_groups[0]:
|
||||
# FIXME: convert to connect_bus?
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
|
||||
if (row_index < self.rows):
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
|
||||
row_index = row_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
|
|
|
|||
|
|
@ -5,17 +5,14 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from tech import drc, parameter
|
||||
import debug
|
||||
import design
|
||||
import contact
|
||||
from math import log
|
||||
from math import sqrt
|
||||
import math
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class wordline_driver(design.design):
|
||||
"""
|
||||
Creates a Wordline Driver
|
||||
|
|
@ -58,26 +55,20 @@ class wordline_driver(design.design):
|
|||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
# This is just used for measurements,
|
||||
# so don't add the module
|
||||
|
||||
self.inv = factory.create(module_type="pdriver",
|
||||
fanout=self.cols,
|
||||
neg_polarity=True)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.inv_no_output = factory.create(module_type="pinv",
|
||||
route_output=False)
|
||||
self.add_mod(self.inv_no_output)
|
||||
|
||||
self.nand2 = factory.create(module_type="pnand2")
|
||||
self.add_mod(self.nand2)
|
||||
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
"""
|
||||
Add a pin for each row of vdd/gnd which
|
||||
are must-connects next level up.
|
||||
"""
|
||||
|
||||
# Find the x offsets for where the vias/pins should be placed
|
||||
a_xoffset = self.nand_inst[0].rx()
|
||||
|
|
@ -86,7 +77,9 @@ class wordline_driver(design.design):
|
|||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# use the inverter offset even though it will be the nand's too
|
||||
(gate_offset, y_dir) = self.get_gate_offset(0, self.inv.height, num)
|
||||
(gate_offset, y_dir) = self.get_gate_offset(0,
|
||||
self.inv.height,
|
||||
num)
|
||||
|
||||
# Route both supplies
|
||||
for name in ["vdd", "gnd"]:
|
||||
|
|
@ -97,11 +90,9 @@ class wordline_driver(design.design):
|
|||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name, pin_pos)
|
||||
|
||||
|
||||
|
||||
def create_drivers(self):
|
||||
self.nand_inst = []
|
||||
self.inv2_inst = []
|
||||
self.nand_inst = []
|
||||
self.inv2_inst = []
|
||||
for row in range(self.rows):
|
||||
name_nand = "wl_driver_nand{}".format(row)
|
||||
name_inv2 = "wl_driver_inv{}".format(row)
|
||||
|
|
@ -119,8 +110,7 @@ class wordline_driver(design.design):
|
|||
self.connect_inst(["wl_bar_{0}".format(row),
|
||||
"wl_{0}".format(row),
|
||||
"vdd", "gnd"])
|
||||
|
||||
|
||||
|
||||
def place_drivers(self):
|
||||
nand2_xoffset = 2*self.m1_width + 5*self.m1_space
|
||||
inv2_xoffset = nand2_xoffset + self.nand2.width
|
||||
|
|
@ -136,8 +126,8 @@ class wordline_driver(design.design):
|
|||
y_offset = self.inv.height*row
|
||||
inst_mirror = "R0"
|
||||
|
||||
nand2_offset=[nand2_xoffset, y_offset]
|
||||
inv2_offset=[inv2_xoffset, y_offset]
|
||||
nand2_offset = [nand2_xoffset, y_offset]
|
||||
inv2_offset = [inv2_xoffset, y_offset]
|
||||
|
||||
# add nand 2
|
||||
self.nand_inst[row].place(offset=nand2_offset,
|
||||
|
|
@ -146,17 +136,16 @@ class wordline_driver(design.design):
|
|||
self.inv2_inst[row].place(offset=inv2_offset,
|
||||
mirror=inst_mirror)
|
||||
|
||||
|
||||
def route_layout(self):
|
||||
""" Route all of the signals """
|
||||
|
||||
# Wordline enable connection
|
||||
en_pin=self.add_layout_pin(text="en",
|
||||
layer="metal2",
|
||||
offset=[self.m1_width + 2*self.m1_space,0],
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
|
||||
en_offset = [self.m1_width + 2 * self.m1_space, 0]
|
||||
en_pin = self.add_layout_pin(text="en",
|
||||
layer="metal2",
|
||||
offset=en_offset,
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
|
||||
for row in range(self.rows):
|
||||
nand_inst = self.nand_inst[row]
|
||||
|
|
@ -165,7 +154,7 @@ class wordline_driver(design.design):
|
|||
# en connection
|
||||
a_pin = nand_inst.get_pin("A")
|
||||
a_pos = a_pin.lc()
|
||||
clk_offset = vector(en_pin.bc().x,a_pos.y)
|
||||
clk_offset = vector(en_pin.bc().x, a_pos.y)
|
||||
self.add_segment_center(layer="metal1",
|
||||
start=clk_offset,
|
||||
end=a_pos)
|
||||
|
|
@ -175,7 +164,7 @@ class wordline_driver(design.design):
|
|||
# Nand2 out to 2nd inv
|
||||
zr_pos = nand_inst.get_pin("Z").rc()
|
||||
al_pos = inv2_inst.get_pin("A").lc()
|
||||
# ensure the bend is in the middle
|
||||
# ensure the bend is in the middle
|
||||
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
||||
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
|
@ -183,10 +172,14 @@ class wordline_driver(design.design):
|
|||
# connect the decoder input pin to nand2 B
|
||||
b_pin = nand_inst.get_pin("B")
|
||||
b_pos = b_pin.lc()
|
||||
# needs to move down since B nand input is nearly aligned with A inv input
|
||||
up_or_down = self.m2_space if row%2 else -self.m2_space
|
||||
input_offset = vector(0,b_pos.y + up_or_down)
|
||||
mid_via_offset = vector(clk_offset.x,input_offset.y) + vector(0.5*self.m2_width+self.m2_space+0.5*contact.m1m2.width,0)
|
||||
# needs to move down since B nand input is
|
||||
# nearly aligned with A inv input
|
||||
up_or_down = self.m2_space if row % 2 else -self.m2_space
|
||||
input_offset = vector(0, b_pos.y + up_or_down)
|
||||
base_offset = vector(clk_offset.x, input_offset.y)
|
||||
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1m2.width, 0)
|
||||
mid_via_offset = base_offset + contact_offset
|
||||
|
||||
# must under the clk line in M1
|
||||
self.add_layout_pin_segment_center(text="in_{0}".format(row),
|
||||
layer="metal1",
|
||||
|
|
@ -194,24 +187,27 @@ class wordline_driver(design.design):
|
|||
end=mid_via_offset)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=mid_via_offset,
|
||||
directions=("V","V"))
|
||||
directions=("V", "V"))
|
||||
|
||||
# now connect to the nand2 B
|
||||
self.add_path("metal2", [mid_via_offset, b_pos])
|
||||
contact_offset = b_pos - vector(0.5 * contact.m1m2.height, 0)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=b_pos - vector(0.5*contact.m1m2.height,0),
|
||||
directions=("H","H"))
|
||||
|
||||
offset=contact_offset,
|
||||
directions=("H", "H"))
|
||||
|
||||
# output each WL on the right
|
||||
wl_offset = inv2_inst.get_pin("Z").rc()
|
||||
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
|
||||
layer="metal1",
|
||||
start=wl_offset,
|
||||
end=wl_offset-vector(self.m1_width,0))
|
||||
end=wl_offset - vector(self.m1_width, 0))
|
||||
|
||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||
"""Follows the clk_buf to a wordline signal adding each stages stage effort to a list"""
|
||||
"""
|
||||
Follows the clk_buf to a wordline signal adding
|
||||
each stages stage effort to a list.
|
||||
"""
|
||||
stage_effort_list = []
|
||||
|
||||
stage1_cout = self.inv.get_cin()
|
||||
|
|
@ -225,7 +221,10 @@ class wordline_driver(design.design):
|
|||
return stage_effort_list
|
||||
|
||||
def get_wl_en_cin(self):
|
||||
"""Get the relative capacitance of all the enable connections in the bank"""
|
||||
#The enable is connected to a nand2 for every row.
|
||||
"""
|
||||
Get the relative capacitance of all
|
||||
the enable connections in the bank
|
||||
"""
|
||||
# The enable is connected to a nand2 for every row.
|
||||
total_cin = self.nand2.get_cin() * self.rows
|
||||
return total_cin
|
||||
|
|
|
|||
|
|
@ -0,0 +1,304 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
#Copyright (c) 2019 Regents of the University of California and The Board
|
||||
#of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
#(acting for and on behalf of Oklahoma State University)
|
||||
#All rights reserved.
|
||||
#
|
||||
import design
|
||||
from tech import drc, parameter, spice
|
||||
import debug
|
||||
import math
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
class pwrite_driver(design.design):
|
||||
"""
|
||||
The pwrite_driver is two tristate inverters that drive the bitlines.
|
||||
The data input is first inverted before one tristate.
|
||||
The inverted enable is also generated to control one tristate.
|
||||
"""
|
||||
def __init__(self, name, size=0):
|
||||
debug.error("pwrite_driver not implemented yet.", -1)
|
||||
debug.info(1, "creating pwrite_driver {}".format(name))
|
||||
design.design.__init__(self, name)
|
||||
self.size = size
|
||||
self.beta = parameter["beta"]
|
||||
self.pmos_width = self.beta*self.size*parameter["min_tx_size"]
|
||||
self.nmos_width = self.size*parameter["min_tx_size"]
|
||||
|
||||
# The tech M2 pitch is based on old via orientations
|
||||
self.m2_pitch = self.m2_space + self.m2_width
|
||||
|
||||
# Width is matched to the bitcell,
|
||||
# Height will be variable
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.width = self.bitcell.width
|
||||
|
||||
# Creates the netlist and layout
|
||||
# Since it has variable height, it is not a pgate.
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_insts()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_modules()
|
||||
self.route_wires()
|
||||
self.route_supplies()
|
||||
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("din", "INPUT")
|
||||
self.add_pin("bl", "OUTPUT")
|
||||
self.add_pin("br", "OUTPUT")
|
||||
self.add_pin("en", "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
|
||||
# Tristate inverter
|
||||
self.tri = factory.create(module_type="ptristate_inv", height="min")
|
||||
self.add_mod(self.tri)
|
||||
debug.check(self.tri.width<self.width,"Could not create tristate inverter to match bitcell width")
|
||||
|
||||
#self.tbuf = factory.create(module_type="ptristate_buf", height="min")
|
||||
#self.add_mod(self.tbuf)
|
||||
#debug.check(self.tbuf.width<self.width,"Could not create tristate buffer to match bitcell width")
|
||||
|
||||
# Inverter for din and en
|
||||
self.inv = factory.create(module_type="pinv", under_rail_vias=True)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_insts(self):
|
||||
# Enable inverter
|
||||
self.en_inst = self.add_inst(name="en_inv", mod=self.inv)
|
||||
self.connect_inst(["en", "en_bar", "vdd", "gnd"])
|
||||
|
||||
# Din inverter
|
||||
self.din_inst = self.add_inst(name="din_inv", mod=self.inv)
|
||||
self.connect_inst(["din", "din_bar", "vdd", "gnd"])
|
||||
|
||||
# Bitline tristate
|
||||
self.bl_inst = self.add_inst(name="bl_tri", mod=self.tri)
|
||||
self.connect_inst(["din_bar", "bl", "en", "en_bar", "vdd", "gnd"])
|
||||
|
||||
# Bitline bar tristate
|
||||
self.br_inst = self.add_inst(name="br_tri", mod=self.tri)
|
||||
self.connect_inst(["din", "br", "en", "en_bar", "vdd", "gnd"])
|
||||
|
||||
|
||||
def place_modules(self):
|
||||
|
||||
# Add enable to the right
|
||||
self.din_inst.place(vector(0, 0))
|
||||
|
||||
# Add BR tristate above
|
||||
self.br_inst.place(vector(0, self.en_inst.uy()+self.br_inst.height), mirror="MX")
|
||||
|
||||
# Add BL tristate buffer
|
||||
#print(self.bl_inst.width,self.width)
|
||||
self.bl_inst.place(vector(self.width,self.br_inst.uy()), mirror="MY")
|
||||
|
||||
# Add din to the left
|
||||
self.en_inst.place(vector(self.width, self.bl_inst.uy()+self.en_inst.height), rotate=180)
|
||||
|
||||
self.height = self.en_inst.uy()
|
||||
|
||||
|
||||
def route_bitlines(self):
|
||||
"""
|
||||
Route the bitlines to the spacing of the bitcell
|
||||
( even though there may be a column mux )
|
||||
"""
|
||||
|
||||
# Second from left track and second from right track
|
||||
right_x = self.width + self.m2_pitch
|
||||
left_x = -self.m2_pitch
|
||||
|
||||
bl_xoffset = left_x
|
||||
bl_out=vector(bl_xoffset, self.height)
|
||||
bl_in=self.bl_inst.get_pin("out").center()
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=bl_in)
|
||||
|
||||
bl_mid = vector(bl_out.x,bl_in.y)
|
||||
self.add_path("metal2", [bl_in, bl_mid, bl_out])
|
||||
|
||||
self.add_layout_pin_rect_center(text="bl",
|
||||
layer="metal2",
|
||||
offset=bl_out)
|
||||
|
||||
br_xoffset = right_x
|
||||
br_out=vector(br_xoffset, self.height)
|
||||
br_in=self.br_inst.get_pin("out").center()
|
||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||
offset=br_in)
|
||||
|
||||
br_mid = vector(br_out.x,br_in.y)
|
||||
self.add_path("metal2", [br_in, br_mid, br_out])
|
||||
self.add_layout_pin_rect_center(text="br",
|
||||
layer="metal2",
|
||||
offset=br_out)
|
||||
|
||||
#br_xoffset = b.get_pin("br".cx()
|
||||
#self.br_inst.get_pin("br")
|
||||
|
||||
def route_din(self):
|
||||
|
||||
# Left
|
||||
track_xoff = self.get_m2_track(1)
|
||||
|
||||
din_loc = self.din_inst.get_pin("A").center()
|
||||
self.add_via_stack("metal1", "metal2", din_loc)
|
||||
din_track = vector(track_xoff,din_loc.y)
|
||||
|
||||
br_in = self.br_inst.get_pin("in").center()
|
||||
self.add_via_stack("metal1", "metal2", br_in)
|
||||
br_track = vector(track_xoff,br_in.y)
|
||||
|
||||
din_in = vector(track_xoff,0)
|
||||
|
||||
self.add_path("metal2", [din_in, din_track, din_loc, din_track, br_track, br_in])
|
||||
|
||||
self.add_layout_pin_rect_center(text="din",
|
||||
layer="metal2",
|
||||
offset=din_in)
|
||||
|
||||
def route_din_bar(self):
|
||||
|
||||
# Left
|
||||
track_xoff = self.get_m4_track(self.din_bar_track)
|
||||
|
||||
din_bar_in = self.din_inst.get_pin("Z").center()
|
||||
self.add_via_stack("metal1", "metal3", din_bar_in)
|
||||
din_bar_track = vector(track_xoff,din_bar_in.y)
|
||||
|
||||
bl_in = self.bl_inst.get_pin("in").center()
|
||||
self.add_via_stack("metal1", "metal3", bl_in)
|
||||
bl_track = vector(track_xoff,bl_in.y)
|
||||
|
||||
din_in = vector(track_xoff,0)
|
||||
|
||||
self.add_wire(("metal3","via3","metal4"), [din_bar_in, din_bar_track, bl_track, bl_in])
|
||||
|
||||
self.add_layout_pin_rect_center(text="din",
|
||||
layer="metal4",
|
||||
offset=din_in)
|
||||
|
||||
|
||||
def route_en_bar(self):
|
||||
# Enable in track
|
||||
track_xoff = self.get_m4_track(self.en_bar_track)
|
||||
|
||||
# This M2 pitch is a hack since the A and Z pins align horizontally
|
||||
en_bar_loc = self.en_inst.get_pin("Z").uc()
|
||||
en_bar_track = vector(track_xoff, en_bar_loc.y)
|
||||
self.add_via_stack("metal1", "metal3", en_bar_loc)
|
||||
|
||||
# This is a U route to the right down then left
|
||||
bl_en_loc = self.bl_inst.get_pin("en_bar").center()
|
||||
bl_en_track = vector(track_xoff, bl_en_loc.y)
|
||||
self.add_via_stack("metal1", "metal3", bl_en_loc)
|
||||
br_en_loc = self.br_inst.get_pin("en_bar").center()
|
||||
br_en_track = vector(track_xoff, bl_en_loc.y)
|
||||
self.add_via_stack("metal1", "metal3", br_en_loc)
|
||||
|
||||
|
||||
# L shape
|
||||
self.add_wire(("metal3","via3","metal4"),
|
||||
[en_bar_loc, en_bar_track, bl_en_track])
|
||||
# U shape
|
||||
self.add_wire(("metal3","via3","metal4"),
|
||||
[bl_en_loc, bl_en_track, br_en_track, br_en_loc])
|
||||
|
||||
|
||||
def route_en(self):
|
||||
|
||||
# Enable in track
|
||||
track_xoff = self.get_m4_track(self.en_track)
|
||||
|
||||
# The en pin will be over the vdd rail
|
||||
vdd_yloc = self.en_inst.get_pin("vdd").cy()
|
||||
self.add_layout_pin_segment_center(text="en",
|
||||
layer="metal3",
|
||||
start=vector(0,vdd_yloc),
|
||||
end=vector(self.width,vdd_yloc))
|
||||
|
||||
en_loc = self.en_inst.get_pin("A").center()
|
||||
en_rail = vector(en_loc.x, vdd_yloc)
|
||||
self.add_via_stack("metal1", "metal2", en_loc)
|
||||
self.add_path("metal2", [en_loc, en_rail])
|
||||
self.add_via_stack("metal2", "metal3", en_rail)
|
||||
|
||||
# Start point in the track on the pin rail
|
||||
en_track = vector(track_xoff, vdd_yloc)
|
||||
self.add_via_stack("metal3", "metal4", en_track)
|
||||
|
||||
# This is a U route to the right down then left
|
||||
bl_en_loc = self.bl_inst.get_pin("en").center()
|
||||
bl_en_track = vector(track_xoff, bl_en_loc.y)
|
||||
self.add_via_stack("metal1", "metal3", bl_en_loc)
|
||||
br_en_loc = self.br_inst.get_pin("en").center()
|
||||
br_en_track = vector(track_xoff, bl_en_loc.y)
|
||||
self.add_via_stack("metal1", "metal3", br_en_loc)
|
||||
|
||||
# U shape
|
||||
self.add_wire(("metal3","via3","metal4"),
|
||||
[en_track, bl_en_track, bl_en_loc, bl_en_track, br_en_track, br_en_loc])
|
||||
|
||||
|
||||
def get_m4_track(self,i):
|
||||
return 0.5*self.m4_space + i*(self.m4_width+self.m4_space)
|
||||
def get_m3_track(self,i):
|
||||
return 0.5*self.m3_space + i*(self.m3_width+self.m3_space)
|
||||
def get_m2_track(self,i):
|
||||
return 0.5*self.m2_space + i*(self.m2_width+self.m2_space)
|
||||
|
||||
def route_wires(self):
|
||||
# M4 tracks
|
||||
self.din_bar_track = 2
|
||||
self.en_track = 0
|
||||
self.en_bar_track = 1
|
||||
|
||||
self.route_bitlines()
|
||||
self.route_din()
|
||||
self.route_din_bar()
|
||||
self.route_en()
|
||||
self.route_en_bar()
|
||||
|
||||
def route_supplies(self):
|
||||
for inst in [self.en_inst, self.din_inst, self.bl_inst, self.br_inst]:
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=inst.get_pin("vdd")
|
||||
self.add_layout_pin(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_pin.ll().scale(0,1),
|
||||
width=self.width,
|
||||
height=vdd_pin.height())
|
||||
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=inst.get_pin("gnd")
|
||||
self.add_layout_pin(text="gnd",
|
||||
layer="metal1",
|
||||
offset=gnd_pin.ll().scale(0,1),
|
||||
width=self.width,
|
||||
height=vdd_pin.height())
|
||||
|
||||
|
||||
def get_w_en_cin(self):
|
||||
"""Get the relative capacitance of a single input"""
|
||||
# This is approximated from SCMOS. It has roughly 5 3x transistor gates.
|
||||
return 5*3
|
||||
|
|
@ -7,42 +7,44 @@
|
|||
#
|
||||
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.
|
||||
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
|
||||
|
||||
# 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
|
||||
# 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.
|
||||
# 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
|
||||
|
|
@ -51,16 +53,16 @@ class pin_group:
|
|||
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:
|
||||
enclosure_string = "\n enclose={}".format(self.enclosures)
|
||||
total_string += enclosure_string
|
||||
|
|
@ -71,7 +73,7 @@ class pin_group:
|
|||
def __repr__(self):
|
||||
""" override repr function output """
|
||||
return str(self)
|
||||
|
||||
|
||||
def size(self):
|
||||
return len(self.grids)
|
||||
|
||||
|
|
@ -80,7 +82,7 @@ class pin_group:
|
|||
|
||||
def is_routed(self):
|
||||
return self.routed
|
||||
|
||||
|
||||
def remove_redundant_shapes(self, pin_list):
|
||||
"""
|
||||
Remove any pin layout that is contained within another.
|
||||
|
|
@ -88,39 +90,40 @@ class pin_group:
|
|||
"""
|
||||
local_debug = False
|
||||
if local_debug:
|
||||
debug.info(0,"INITIAL: {}".format(pin_list))
|
||||
|
||||
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):
|
||||
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
|
||||
|
||||
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:
|
||||
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))
|
||||
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))
|
||||
|
||||
debug.info(0, "FINAL : {}".format(new_pin_list))
|
||||
|
||||
return new_pin_list
|
||||
|
||||
def compute_enclosures(self):
|
||||
|
|
@ -130,23 +133,32 @@ class pin_group:
|
|||
# Enumerate every possible enclosure
|
||||
pin_list = []
|
||||
for seed in self.grids:
|
||||
(ll, ur) = self.enclose_pin_grids(seed, direction.NORTH, direction.EAST)
|
||||
(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)
|
||||
(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)
|
||||
|
||||
debug.check(len(pin_list) > 0,
|
||||
"Did not find any enclosures.")
|
||||
|
||||
# Now simplify the enclosure list
|
||||
# Now simplify the enclosure list
|
||||
new_pin_list = self.remove_redundant_shapes(pin_list)
|
||||
|
||||
return new_pin_list
|
||||
|
||||
debug.check(len(new_pin_list) > 0,
|
||||
"Did not find any enclosures.")
|
||||
|
||||
return new_pin_list
|
||||
|
||||
def compute_connector(self, pin, enclosure):
|
||||
"""
|
||||
Compute a shape to connect the pin to the enclosure shape.
|
||||
"""
|
||||
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):
|
||||
|
|
@ -154,9 +166,9 @@ class pin_group:
|
|||
plc = pin.lc()
|
||||
prc = pin.rc()
|
||||
elc = enclosure.lc()
|
||||
erc = enclosure.rc()
|
||||
ymin = min(plc.y,elc.y)
|
||||
ymax = max(plc.y,elc.y)
|
||||
# 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)
|
||||
elif pin.yoverlaps(enclosure):
|
||||
|
|
@ -164,9 +176,9 @@ class pin_group:
|
|||
pbc = pin.bc()
|
||||
puc = pin.uc()
|
||||
ebc = enclosure.bc()
|
||||
euc = enclosure.uc()
|
||||
xmin = min(pbc.x,ebc.x)
|
||||
xmax = max(pbc.x,ebc.x)
|
||||
# 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)
|
||||
else:
|
||||
|
|
@ -180,7 +192,7 @@ class pin_group:
|
|||
ll = vector(xmin, ymin)
|
||||
ur = vector(xmax, ymax)
|
||||
|
||||
if ll.x==ur.x or ll.y==ur.y:
|
||||
if ll.x == ur.x or ll.y == ur.y:
|
||||
return None
|
||||
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
||||
return p
|
||||
|
|
@ -195,20 +207,20 @@ class pin_group:
|
|||
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():
|
||||
if item.by() >= pin.uy():
|
||||
above_item = item
|
||||
else:
|
||||
break
|
||||
|
||||
# There was nothing
|
||||
if above_item==None:
|
||||
# There was nothing
|
||||
if not above_item:
|
||||
return None
|
||||
# If it already overlaps, no connector needed
|
||||
if above_item.overlaps(pin):
|
||||
|
|
@ -219,7 +231,7 @@ class pin_group:
|
|||
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.
|
||||
"""
|
||||
|
|
@ -228,31 +240,31 @@ class pin_group:
|
|||
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():
|
||||
if item.uy() <= pin.by():
|
||||
bottom_item = item
|
||||
else:
|
||||
break
|
||||
|
||||
# There was nothing to the left
|
||||
if bottom_item==None:
|
||||
if not bottom_item:
|
||||
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.
|
||||
"""
|
||||
|
|
@ -261,31 +273,31 @@ class pin_group:
|
|||
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():
|
||||
if item.rx() <= pin.lx():
|
||||
left_item = item
|
||||
else:
|
||||
break
|
||||
|
||||
# There was nothing to the left
|
||||
if left_item==None:
|
||||
if not left_item:
|
||||
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.
|
||||
"""
|
||||
|
|
@ -294,79 +306,79 @@ class pin_group:
|
|||
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():
|
||||
if item.lx() >= pin.rx():
|
||||
right_item = item
|
||||
else:
|
||||
break
|
||||
|
||||
# There was nothing to the right
|
||||
if right_item==None:
|
||||
if not right_item:
|
||||
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..
|
||||
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():
|
||||
if not smallest 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
|
||||
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)
|
||||
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():
|
||||
# overlap_length = pin.overlap_length(overlap_shape)
|
||||
if not smallest_shape or overlap_shape.area() < smallest_shape.area():
|
||||
smallest_shape = overlap_shape
|
||||
|
||||
return smallest_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
|
||||
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)
|
||||
zindex = self.router.get_zindex(pin.lpp[0])
|
||||
(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():
|
||||
if not smallest_shape 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.
|
||||
|
|
@ -378,16 +390,15 @@ class pin_group:
|
|||
|
||||
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):
|
||||
|
|
@ -398,19 +409,18 @@ class pin_group:
|
|||
dir1 and dir2 should be two orthogonal directions.
|
||||
"""
|
||||
|
||||
offset1= direction.get_offset(dir1)
|
||||
offset2= direction.get_offset(dir2)
|
||||
offset1 = direction.get_offset(dir1)
|
||||
offset2 = direction.get_offset(dir2)
|
||||
|
||||
# We may have started with an empty set
|
||||
if not self.grids:
|
||||
return None
|
||||
debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.")
|
||||
|
||||
# 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
|
||||
# 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:
|
||||
|
|
@ -419,7 +429,7 @@ class pin_group:
|
|||
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
|
||||
# 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:
|
||||
|
|
@ -431,74 +441,87 @@ class pin_group:
|
|||
|
||||
# Add a shape from ll to ur
|
||||
ur = row[-1]
|
||||
return (ll,ur)
|
||||
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 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()
|
||||
|
||||
# Find a connector to every pin and add it to the enclosures
|
||||
for pin in self.pins:
|
||||
|
||||
|
||||
# If it is contained, it won't need a connector
|
||||
if pin.contained_by_any(self.enclosures):
|
||||
continue
|
||||
|
||||
# Find a connector in the cardinal directions
|
||||
# If there is overlap, but it isn't contained, these could all be None
|
||||
# These could also be none if the pin is diagonal from the enclosure
|
||||
# If there is overlap, but it isn't contained,
|
||||
# these could all be None
|
||||
# These could also be none if the pin is
|
||||
# diagonal from the enclosure
|
||||
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)
|
||||
connector_list = [left_connector, right_connector, above_connector, below_connector]
|
||||
filtered_list = list(filter(lambda x: x!=None, connector_list))
|
||||
if (len(filtered_list)>0):
|
||||
connector_list = [left_connector,
|
||||
right_connector,
|
||||
above_connector,
|
||||
below_connector]
|
||||
filtered_list = list(filter(lambda x: x != None, connector_list))
|
||||
if (len(filtered_list) > 0):
|
||||
import copy
|
||||
bbox_connector = copy.copy(pin)
|
||||
bbox_connector = copy.copy(pin)
|
||||
bbox_connector.bbox(filtered_list)
|
||||
self.enclosures.append(bbox_connector)
|
||||
|
||||
# Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector.
|
||||
# This could only happen when there was no enclosure in any cardinal direction from a pin
|
||||
# Now, make sure each pin touches an enclosure.
|
||||
# If not, add another (diagonal) connector.
|
||||
# This could only happen when there was no enclosure
|
||||
# in any cardinal direction from a pin
|
||||
if not self.overlap_any_shape(self.pins, self.enclosures):
|
||||
connector = self.find_smallest_connector(self.pins, self.enclosures)
|
||||
if connector==None:
|
||||
debug.error("Could not find a connector for {} with {}".format(self.pins, self.enclosures))
|
||||
connector = self.find_smallest_connector(self.pins,
|
||||
self.enclosures)
|
||||
if not connector:
|
||||
debug.error("Could not find a connector for {} with {}".format(self.pins,
|
||||
self.enclosures))
|
||||
self.router.write_debug_gds("no_connector.gds")
|
||||
import pdb; pdb.set_trace()
|
||||
self.enclosures.append(connector)
|
||||
|
||||
# At this point, the pins are overlapping, but there might be more than one!
|
||||
|
||||
# At this point, the pins are overlapping,
|
||||
# but there might be more than one!
|
||||
overlap_set = set()
|
||||
for pin in self.pins:
|
||||
overlap_set.update(self.transitive_overlap(pin, self.enclosures))
|
||||
# Use the new enclosures and recompute the grids that correspond to them
|
||||
if len(overlap_set)<len(self.enclosures):
|
||||
# Use the new enclosures and recompute the grids
|
||||
# that correspond to them
|
||||
if len(overlap_set) < len(self.enclosures):
|
||||
self.enclosures = overlap_set
|
||||
self.grids=set()
|
||||
# Also update the grid locations with the new (possibly pruned) enclosures
|
||||
self.grids = set()
|
||||
# Also update the grid locations with the new
|
||||
# (possibly pruned) enclosures
|
||||
for enclosure in self.enclosures:
|
||||
(sufficient,insufficient) = self.router.convert_pin_to_tracks(self.name,enclosure)
|
||||
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
|
||||
enclosure)
|
||||
self.grids.update(sufficient)
|
||||
|
||||
|
||||
debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
|
||||
self.pins,
|
||||
self.grids,
|
||||
self.enclosures))
|
||||
|
||||
debug.info(3, "Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
|
||||
self.pins,
|
||||
self.grids,
|
||||
self.enclosures))
|
||||
|
||||
def transitive_overlap(self, shape, shape_list):
|
||||
"""
|
||||
Given shape, find the elements in shape_list that overlap transitively.
|
||||
I.e. if shape overlaps A and A overlaps B, return both A and B.
|
||||
I.e. if shape overlaps A and A overlaps B, return both A and B.
|
||||
"""
|
||||
|
||||
augmented_shape_list = set(shape_list)
|
||||
|
|
@ -512,31 +535,29 @@ class pin_group:
|
|||
for cur_shape in augmented_shape_list:
|
||||
if old_shape.overlaps(cur_shape):
|
||||
connected_set.add(cur_shape)
|
||||
|
||||
|
||||
# Remove the original shape
|
||||
connected_set.remove(shape)
|
||||
|
||||
|
||||
# if len(connected_set)<len(shape_list):
|
||||
# import pprint
|
||||
# print("S: ",shape)
|
||||
# pprint.pprint(shape_list)
|
||||
# pprint.pprint(connected_set)
|
||||
|
||||
|
||||
return connected_set
|
||||
|
||||
|
||||
|
||||
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))
|
||||
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):
|
||||
"""
|
||||
|
|
@ -551,11 +572,11 @@ class pin_group:
|
|||
# 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
|
||||
|
|
@ -566,9 +587,8 @@ class pin_group:
|
|||
|
||||
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.
|
||||
"""
|
||||
|
|
@ -580,11 +600,11 @@ class pin_group:
|
|||
adj_grids.add(g1)
|
||||
|
||||
return adj_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
|
||||
The secondary set of grids are "optional" pin shapes that
|
||||
should be either blocked or part of the pin.
|
||||
"""
|
||||
pin_set = set()
|
||||
|
|
@ -592,53 +612,59 @@ class pin_group:
|
|||
blockage_set = set()
|
||||
|
||||
for pin in self.pins:
|
||||
debug.info(2," Converting {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
|
||||
debug.info(2, " Converting {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
|
||||
pin)
|
||||
pin_set.update(sufficient)
|
||||
partial_set.update(insufficient)
|
||||
|
||||
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
||||
blockage_in_tracks = self.router.convert_blockage(pin)
|
||||
|
||||
# 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))
|
||||
if len(shared_set) > 0:
|
||||
debug.info(2, "Removing pins {}".format(shared_set))
|
||||
pin_set.difference_update(shared_set)
|
||||
shared_set = partial_set & self.router.blocked_grids
|
||||
if len(shared_set)>0:
|
||||
debug.info(2,"Removing pins {}".format(shared_set))
|
||||
if len(shared_set) > 0:
|
||||
debug.info(2, "Removing pins {}".format(shared_set))
|
||||
partial_set.difference_update(shared_set)
|
||||
shared_set = blockage_set & self.router.blocked_grids
|
||||
if len(shared_set)>0:
|
||||
debug.info(2,"Removing blocks {}".format(shared_set))
|
||||
if len(shared_set) > 0:
|
||||
debug.info(2, "Removing blocks {}".format(shared_set))
|
||||
blockage_set.difference_update(shared_set)
|
||||
|
||||
|
||||
# At least one of the groups must have some valid tracks
|
||||
if (len(pin_set)==0 and len(partial_set)==0 and len(blockage_set)==0):
|
||||
#debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
|
||||
|
||||
if (len(pin_set) == 0 and len(partial_set) == 0 and len(blockage_set) == 0):
|
||||
# debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
|
||||
|
||||
for pin in self.pins:
|
||||
debug.warning(" Expanding conversion {0}".format(pin))
|
||||
# Determine which tracks the pin overlaps
|
||||
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
|
||||
# Determine which tracks the pin overlaps
|
||||
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
|
||||
pin,
|
||||
expansion=1)
|
||||
pin_set.update(sufficient)
|
||||
partial_set.update(insufficient)
|
||||
|
||||
if len(pin_set)==0 and len(partial_set)==0:
|
||||
debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins))
|
||||
|
||||
if len(pin_set) == 0 and len(partial_set) == 0:
|
||||
debug.error("Unable to find unblocked pin {} {}".format(self.name,
|
||||
self.pins))
|
||||
self.router.write_debug_gds("blocked_pin.gds")
|
||||
|
||||
# Consider all the grids that would be blocked
|
||||
self.grids = pin_set | partial_set
|
||||
# Remember the secondary grids for removing adjacent pins
|
||||
self.secondary_grids = partial_set
|
||||
self.grids = pin_set | partial_set
|
||||
if len(self.grids) < 0:
|
||||
debug.error("Did not find any unblocked grids: {}".format(str(self.pins)))
|
||||
self.router.write_debug_gds("blocked_pin.gds")
|
||||
|
||||
# Remember the secondary grids for removing adjacent pins
|
||||
self.secondary_grids = partial_set
|
||||
|
||||
debug.info(2," pins {}".format(self.grids))
|
||||
debug.info(2," secondary {}".format(self.secondary_grids))
|
||||
|
||||
|
||||
|
||||
debug.info(2, " pins {}".format(self.grids))
|
||||
debug.info(2, " secondary {}".format(self.secondary_grids))
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,13 +5,13 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from tech import drc,layer
|
||||
from tech import drc, layer
|
||||
from contact import contact
|
||||
from pin_group import pin_group
|
||||
from vector import vector
|
||||
import debug
|
||||
import math
|
||||
|
||||
|
||||
class router_tech:
|
||||
"""
|
||||
This is a class to hold the router tech constants.
|
||||
|
|
@ -25,9 +25,9 @@ class router_tech:
|
|||
self.layers = layers
|
||||
self.rail_track_width = rail_track_width
|
||||
|
||||
if len(self.layers)==1:
|
||||
if len(self.layers) == 1:
|
||||
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
|
||||
self.horiz_layer_number = self.vert_layer_number = layer[self.layers[0]]
|
||||
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
|
||||
|
||||
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
|
||||
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
|
||||
|
|
@ -40,8 +40,8 @@ class router_tech:
|
|||
via_connect = contact(self.layers, (1, 1))
|
||||
max_via_size = max(via_connect.width,via_connect.height)
|
||||
|
||||
self.horiz_layer_number = layer[self.horiz_layer_name]
|
||||
self.vert_layer_number = layer[self.vert_layer_name]
|
||||
self.horiz_lpp = layer[self.horiz_layer_name]
|
||||
self.vert_lpp = layer[self.vert_layer_name]
|
||||
|
||||
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
|
||||
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
|
||||
|
|
@ -68,8 +68,18 @@ class router_tech:
|
|||
# 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_wire, 1, self.track_wire]
|
||||
|
||||
def get_zindex(self,layer_num):
|
||||
if layer_num==self.horiz_layer_number:
|
||||
def same_lpp(self, lpp1, lpp2):
|
||||
"""
|
||||
Check if the layers and purposes are the same.
|
||||
Ignore if purpose is a None.
|
||||
"""
|
||||
if lpp1[1] == None or lpp2[1] == None:
|
||||
return lpp1[0] == lpp2[0]
|
||||
|
||||
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]
|
||||
|
||||
def get_zindex(self, lpp):
|
||||
if self.same_lpp(lpp, self.horiz_lpp):
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class supply_grid_router(router):
|
|||
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
|
||||
|
||||
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
||||
"""
|
||||
"""
|
||||
Add power supply rails and connect all pins to these rails.
|
||||
"""
|
||||
debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
|
||||
|
|
@ -75,7 +75,6 @@ class supply_grid_router(router):
|
|||
start_time = datetime.now()
|
||||
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
|
||||
print_time("Finding pins and blockages",datetime.now(), start_time, 3)
|
||||
|
||||
# Add the supply rails in a mesh network and connect H/V with vias
|
||||
start_time = datetime.now()
|
||||
# Block everything
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class sram():
|
|||
# Write the config file
|
||||
start_time = datetime.datetime.now()
|
||||
from shutil import copyfile
|
||||
copyfile(OPTS.config_file + '.py', OPTS.output_path + OPTS.output_name + '.py')
|
||||
copyfile(OPTS.config_file, OPTS.output_path + OPTS.output_name + '.py')
|
||||
debug.print_raw("Config: Writing to {0}".format(OPTS.output_path + OPTS.output_name + '.py'))
|
||||
print_time("Config", datetime.datetime.now(), start_time)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import debug
|
|||
class library_drc_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import verify
|
||||
|
||||
(gds_dir, gds_files) = setup_files()
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import debug
|
|||
class library_lvs_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import verify
|
||||
|
||||
(gds_dir, sp_dir, allnames) = setup_files()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class contact_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
for layer_stack in [("metal1", "via1", "metal2"), ("poly", "contact", "metal1")]:
|
||||
stack_name = ":".join(map(str, layer_stack))
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import debug
|
|||
class path_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import wire_path
|
||||
import tech
|
||||
import design
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class ptx_1finger_nmos_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking min size NMOS with 1 finger")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class ptx_1finger_pmos_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking min size PMOS with 1 finger")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class ptx_3finger_nmos_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking three fingers NMOS")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class ptx_3finger_pmos_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking three fingers PMOS")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class ptx_4finger_nmos_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking three fingers NMOS")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class ptx_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import tech
|
||||
|
||||
debug.info(2, "Checking three fingers PMOS")
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import debug
|
|||
class wire_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import wire
|
||||
import tech
|
||||
import design
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class replica_pbitcell_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import dummy_pbitcell
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pand2_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
global verify
|
||||
import verify
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pand3_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
global verify
|
||||
import verify
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ from sram_factory import factory
|
|||
class pbitcell_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.num_rw_ports=1
|
||||
OPTS.num_w_ports=1
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pbuf_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing inverter/buffer 4x 8x")
|
||||
a = factory.create(module_type="pbuf", size=8)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pdriver_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing inverter/buffer 4x 8x")
|
||||
# a tests the error message for specifying conflicting conditions
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pinv_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Checking 8x inverter")
|
||||
tx = factory.create(module_type="pinv", size=8)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pinv_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Checking 1x beta=3 size inverter")
|
||||
tx = factory.create(module_type="pinv", size=1, beta=3)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pinv_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Checking 1x size inverter")
|
||||
tx = factory.create(module_type="pinv", size=1)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pinv_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Checking 2x size inverter")
|
||||
tx = factory.create(module_type="pinv", size=2)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pinvbuf_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing inverter/buffer 4x 8x")
|
||||
a = factory.create(module_type="pinvbuf", size=8)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pnand2_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Checking 2-input nand gate")
|
||||
tx = factory.create(module_type="pnand2", size=1)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pnand3_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Checking 3-input nand gate")
|
||||
tx = factory.create(module_type="pnand3", size=1)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class pnor2_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Checking 2-input nor gate")
|
||||
tx = factory.create(module_type="pnor2", size=1)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class precharge_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check precharge in single port
|
||||
debug.info(2, "Checking precharge for handmade bitcell")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/env python3
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2019 Regents of the University of California and The Board
|
||||
# of Regents for the Oklahoma Agricultural and Mechanical College
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import unittest
|
||||
from testutils import header, openram_test
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
@unittest.skip("SKIPPING 04_pwrite_driver_test")
|
||||
class pwrite_driver_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
|
||||
debug.info(2, "Checking 1x pwrite_driver")
|
||||
tx = factory.create(module_type="pwrite_driver", size=1)
|
||||
self.local_check(tx)
|
||||
|
||||
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()
|
||||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class replica_pbitcell_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import replica_pbitcell
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import debug
|
|||
class single_level_column_mux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check single level column mux in single port
|
||||
debug.info(2, "Checking column mux")
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import debug
|
|||
class bitcell_1rw_1r_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import debug
|
|||
class array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing 4x4 array for 6t_cell")
|
||||
a = factory.create(module_type="bitcell_array", cols=4, rows=4)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import debug
|
|||
class dummy_row_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing dummy row for 6t_cell")
|
||||
a = factory.create(module_type="dummy_array", rows=1, cols=4)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class pbitcell_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell")
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import debug
|
|||
class replica_bitcell_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
OPTS.replica_bitcell = "replica_pbitcell"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class hierarchical_decoder_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
# Doesn't require hierarchical decoder
|
||||
# debug.info(1, "Testing 4 row sample for hierarchical_decoder")
|
||||
# a = hierarchical_decoder.hierarchical_decoder(name="hd1, rows=4)
|
||||
|
|
@ -34,14 +35,30 @@ class hierarchical_decoder_test(openram_test):
|
|||
a = factory.create(module_type="hierarchical_decoder", rows=16)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=17)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 23 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=23)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=32)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 65 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=65)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 128 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=128)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 341 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=341)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=512)
|
||||
self.local_check(a)
|
||||
|
|
@ -57,14 +74,34 @@ class hierarchical_decoder_test(openram_test):
|
|||
a = factory.create(module_type="hierarchical_decoder", rows=16)
|
||||
self.local_check(a)
|
||||
|
||||
factory.reset()
|
||||
debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=17)
|
||||
self.local_check(a)
|
||||
|
||||
factory.reset()
|
||||
debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=23)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=32)
|
||||
self.local_check(a)
|
||||
|
||||
factory.reset()
|
||||
debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=65)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=128)
|
||||
self.local_check(a)
|
||||
|
||||
factory.reset()
|
||||
debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=341)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)")
|
||||
a = factory.create(module_type="hierarchical_decoder", rows=512)
|
||||
self.local_check(a)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class hierarchical_predecode2x4_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# checking hierarchical precode 2x4 for single port
|
||||
debug.info(1, "Testing sample for hierarchy_predecode2x4")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class hierarchical_predecode3x8_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# checking hierarchical precode 3x8 for single port
|
||||
debug.info(1, "Testing sample for hierarchy_predecode3x8")
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ import debug
|
|||
class single_level_column_mux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import single_level_column_mux_array
|
||||
|
||||
# check single level column mux array in single port
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class precharge_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check precharge array in single port
|
||||
debug.info(2, "Checking 3 column precharge")
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import debug
|
|||
class wordline_driver_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check wordline driver for single port
|
||||
debug.info(2, "Checking driver")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class sense_amp_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check sense amp array for single port
|
||||
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class write_driver_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check write driver array for single port
|
||||
debug.info(2, "Testing write_driver_array for columns=8, word_size=8")
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import debug
|
|||
class write_driver_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check write driver array for single port
|
||||
debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4")
|
||||
|
|
@ -58,4 +59,4 @@ if __name__ == "__main__":
|
|||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import debug
|
|||
class write_mask_and_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
# check write driver array for single port
|
||||
debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4")
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class dff_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing dff_array for 3x3")
|
||||
a = factory.create(module_type="dff_array", rows=3, columns=3)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class dff_buf_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing dff_buf_array for 3x3")
|
||||
a = factory.create(module_type="dff_buf_array", rows=3, columns=3)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class dff_buf_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing dff_buf 4x 8x")
|
||||
a = factory.create(module_type="dff_buf", inv1_size=4, inv2_size=8)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class tri_gate_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(1, "Testing tri_gate_array for columns=8, word_size=8")
|
||||
a = factory.create(module_type="tri_gate_array", columns=8, word_size=8)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class delay_chain_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing delay_chain")
|
||||
a = factory.create(module_type="delay_chain", fanout_list=[4, 4, 4, 4])
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import debug
|
|||
class replica_bitcell_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import debug
|
|||
class replica_bitcell_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing 4x4 array for 6t_cell")
|
||||
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0])
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import debug
|
|||
class replica_column_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(2, "Testing replica column for 6t_cell")
|
||||
a = factory.create(module_type="replica_column", rows=4, left_rbl=1, right_rbl=0, replica_bit=1)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ import debug
|
|||
class control_logic_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import control_logic
|
||||
import tech
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class control_logic_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
import control_logic
|
||||
import tech
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import debug
|
|||
class port_address_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(1, "Port address 16 rows")
|
||||
a = factory.create("port_address", cols=16, rows=16)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ import debug
|
|||
class port_data_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
c = sram_config(word_size=4,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class port_data_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
c = sram_config(word_size=16,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class bank_select_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
|
||||
debug.info(1, "No column mux, rw control logic")
|
||||
a = factory.create(module_type="bank_select", port="rw")
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class multi_bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
c = sram_config(word_size=4,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class multi_bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class psingle_bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class single_bank_1rw_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class single_bank_1w_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "bitcell_1w_1r"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class single_bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
c = sram_config(word_size=4,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class single_bank_wmask_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ import debug
|
|||
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
|
||||
class psram_1bank_2mux_1rw_1w_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import debug
|
|||
class psram_1bank_2mux_1rw_1w_wmask_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
|
@ -58,4 +59,4 @@ if __name__ == "__main__":
|
|||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ import debug
|
|||
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error")
|
||||
class psram_1bank_2mux_1w_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ import debug
|
|||
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error")
|
||||
class psram_1bank_2mux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
OPTS.bitcell = "pbitcell"
|
||||
OPTS.replica_bitcell="replica_pbitcell"
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@ import debug
|
|||
|
||||
class psram_1bank_4mux_1rw_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "pbitcell"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class sram_1bank_2mux_1rw_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ import debug
|
|||
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error")
|
||||
class psram_1bank_2mux_1w_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
def runTest(self):
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "bitcell_1w_1r"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class sram_1bank_2mux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=4,
|
||||
num_words=32,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import debug
|
|||
class sram_1bank_2mux_wmask_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=8,
|
||||
write_size=4,
|
||||
|
|
@ -51,4 +52,4 @@ if __name__ == "__main__":
|
|||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import debug
|
|||
class sram_1bank_32b_1024_wmask_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=32,
|
||||
write_size=8,
|
||||
|
|
@ -50,4 +51,4 @@ if __name__ == "__main__":
|
|||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class sram_1bank_4mux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=4,
|
||||
num_words=64,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class sram_1bank_8mux_1rw_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class sram_1bank_8mux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=2,
|
||||
num_words=128,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class sram_1bank_nomux_1rw_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
|
||||
OPTS.bitcell = "bitcell_1rw_1r"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class sram_1bank_nomux_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=4,
|
||||
num_words=16,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import debug
|
|||
class sram_1bank_nomux_wmask_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=8,
|
||||
write_size=4,
|
||||
|
|
@ -51,4 +52,4 @@ if __name__ == "__main__":
|
|||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ import debug
|
|||
class sram_2bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=16,
|
||||
num_words=32,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ import debug
|
|||
class timing_sram_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||
globals.init_openram(config_file)
|
||||
OPTS.spice_name="hspice"
|
||||
OPTS.analytical_delay = False
|
||||
OPTS.netlist_only = True
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue