Merge remote-tracking branch 'origin/dev' into tech_migration

This commit is contained in:
mrg 2020-02-25 00:36:43 +00:00
commit e80677caf7
16 changed files with 427 additions and 319 deletions

4
.gitignore vendored
View File

@ -1,8 +1,10 @@
.DS_Store
*~
*.orig
*.rej
*.pyc
*.aux
*.out
*.toc
*.synctex.gz
**/model_data
**/model_data

View File

@ -116,6 +116,15 @@ class cell_properties():
self._dff_buff_array = _dff_buff_array(use_custom_ports = False,
add_body_contacts = False)
self._write_driver = _cell({'din': 'din',
'bl' : 'bl',
'br' : 'br',
'en' : 'en'})
self._sense_amp = _cell({'bl' : 'bl',
'br' : 'br',
'dout' : 'dout',
'en' : 'en'})
@property
def bitcell(self):
return self._bitcell
@ -132,3 +141,10 @@ class cell_properties():
def dff_buff_array(self):
return self._dff_buff_array
@property
def write_driver(self):
return self._write_driver
@property
def sense_amp(self):
return self._sense_amp

View File

@ -51,7 +51,7 @@ class geometry:
y = item[0] * math.sin(angle) + item[1] * mirr * math.cos(angle) + offset[1]
coordinate += [[x, y]]
return coordinate
def normalize(self):
""" Re-find the LL and UR points after a transform """
(first, second) = self.boundary
@ -64,14 +64,14 @@ class geometry:
def update_boundary(self):
""" Update the boundary with a new placement. """
self.compute_boundary(self.offset, self.mirror, self.rotate)
def compute_boundary(self, offset=vector(0, 0), mirror="", rotate=0):
""" 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. """
if OPTS.netlist_only:
self.boundary = [vector(0,0), vector(0,0)]
return
(ll, ur) = [vector(0, 0), vector(self.width, self.height)]
if mirror == "MX":
@ -83,7 +83,7 @@ class geometry:
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)
@ -96,19 +96,19 @@ class geometry:
self.boundary = [offset + ll, offset + ur]
self.normalize()
def ll(self):
""" Return the lower left corner """
return self.boundary[0]
def ur(self):
""" Return the upper right corner """
return self.boundary[1]
def lr(self):
""" Return the lower right corner """
return vector(self.boundary[1].x, self.boundary[0].y)
def ul(self):
""" Return the upper left corner """
return vector(self.boundary[0].x, self.boundary[1].y)
@ -132,12 +132,12 @@ class geometry:
def cx(self):
""" Return the center x """
return 0.5 * (self.boundary[0].x + self.boundary[1].x)
def cy(self):
""" Return the center y """
return 0.5 * (self.boundary[0].y + self.boundary[1].y)
class instance(geometry):
"""
An instance of an instance/module with a specified location and
@ -148,7 +148,7 @@ class instance(geometry):
geometry.__init__(self)
debug.check(mirror not in ["R90", "R180", "R270"],
"Please use rotation and not mirroring during instantiation.")
self.name = name
self.mod = mod
self.gds = mod.gds
@ -166,7 +166,7 @@ class instance(geometry):
self.width = round_to_grid(mod.width)
self.height = round_to_grid(mod.height)
self.compute_boundary(offset, mirror, rotate)
debug.info(4, "creating instance: " + self.name)
def get_blockages(self, lpp, top=False):
@ -202,11 +202,11 @@ class instance(geometry):
new_blockages.append(self.transform_coords(b,self.offset, mirr, angle))
return new_blockages
def gds_write_file(self, new_layout):
"""Recursively writes all the sub-modules in this instance"""
debug.info(4, "writing instance: " + self.name)
# make sure to write out my module/structure
# make sure to write out my module/structure
# (it will only be written the first time though)
self.mod.gds_write_file(self.gds)
# now write an instance of my module/structure
@ -215,7 +215,7 @@ class instance(geometry):
offsetInMicrons=self.offset,
mirror=self.mirror,
rotate=self.rotate)
def place(self, offset, mirror="R0", rotate=0):
""" This updates the placement of an instance. """
# Update the placement of an already added instance
@ -224,8 +224,8 @@ class instance(geometry):
self.rotate = rotate
self.update_boundary()
debug.info(3, "placing instance {}".format(self))
def get_pin(self,name,index=-1):
""" Return an absolute pin that is offset and transformed based on
this instance location. Index will return one of several pins."""
@ -243,20 +243,20 @@ class instance(geometry):
def get_num_pins(self, name):
""" Return the number of pins of a given name """
return len(self.mod.get_pins(name))
def get_pins(self,name):
""" Return an absolute pin that is offset and transformed based on
this instance location. """
import copy
pin = copy.deepcopy(self.mod.get_pins(name))
new_pins = []
for p in pin:
p.transform(self.offset,self.mirror,self.rotate)
p.transform(self.offset,self.mirror,self.rotate)
new_pins.append(p)
return new_pins
def __str__(self):
""" override print function output """
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
@ -293,7 +293,7 @@ class path(geometry):
def get_blockages(self, layer):
""" Fail since we don't support paths yet. """
assert(0)
def __str__(self):
""" override print function output """
return "path: layer=" + self.layerNumber + " w=" + self.width
@ -329,6 +329,7 @@ class label(geometry):
debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text)
new_layout.addText(text=self.text,
layerNumber=self.layerNumber,
purposeNumber=self.layerPurpose,
offsetInMicrons=self.offset,
magnification=self.zoom,
rotate=None)
@ -336,7 +337,7 @@ class label(geometry):
def get_blockages(self, layer):
""" Returns an empty list since text cannot be blockages. """
return []
def __str__(self):
""" override print function output """
return "label: " + self.text + " layer=" + str(self.layerNumber)
@ -345,7 +346,7 @@ class label(geometry):
""" override print function output """
return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )"
class rectangle(geometry):
"""Represents a rectangular shape"""
@ -363,7 +364,7 @@ class rectangle(geometry):
debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): "
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
def get_blockages(self, layer):
""" Returns a list of one rectangle if it is on this layer"""
if self.layerNumber == layer:

View File

@ -30,7 +30,7 @@ class pin_layout:
debug.check(self.width() > 0, "Zero width pin.")
debug.check(self.height() > 0, "Zero height pin.")
# if it's a string, use the name
if type(layer_name_pp) == str:
self._layer = layer_name_pp
@ -93,17 +93,17 @@ class pin_layout:
is a major speedup, if pin_layout is used as a key for dicts.
"""
return self._hash
def __lt__(self, other):
""" Provide a function for ordering items by the ll point """
(ll, ur) = self.rect
(oll, our) = other.rect
if ll.x < oll.x and ll.y < oll.y:
return True
return False
def __eq__(self, other):
""" Check if these are the same pins for duplicate checks """
if isinstance(other, self.__class__):
@ -128,14 +128,14 @@ class pin_layout:
max_y = max(max_y, pin.ur().y)
self.rect = [vector(min_x, min_y), vector(max_x, max_y)]
def fix_minarea(self):
"""
Try to fix minimum area rule.
"""
min_area = drc("{}_minarea".format(self.layer))
pass
def inflate(self, spacing=None):
"""
Inflate the rectangle by the spacing (or other rule)
@ -143,12 +143,12 @@ class pin_layout:
"""
if not spacing:
spacing = 0.5*drc("{0}_to_{0}".format(self.layer))
(ll, ur) = self.rect
spacing = vector(spacing, spacing)
newll = ll - spacing
newur = ur + spacing
return (newll, newur)
def intersection(self, other):
@ -191,7 +191,7 @@ class pin_layout:
y_overlaps = True
return y_overlaps
def xcontains(self, other):
""" Check if shape contains the x overlap """
(ll, ur) = self.rect
@ -205,13 +205,13 @@ class pin_layout:
(oll, our) = other.rect
return (oll.y >= ll.y and our.y <= ur.y)
def contains(self, other):
""" Check if a shape contains another rectangle """
# If it is the same shape entirely, it is contained!
if self == other:
return True
# Can only overlap on the same layer
if not self.same_lpp(self.lpp, other.lpp):
return False
@ -230,13 +230,13 @@ class pin_layout:
if shape.contains(self):
return True
return False
def overlaps(self, other):
""" Check if a shape overlaps with a rectangle """
# Can only overlap on the same layer
if not self.same_lpp(self.lpp, other.lpp):
return False
x_overlaps = self.xoverlaps(other)
y_overlaps = self.yoverlaps(other)
@ -245,11 +245,11 @@ class pin_layout:
def area(self):
""" Return the area. """
return self.height()*self.width()
def height(self):
""" Return height. Abs is for pre-normalized value."""
return abs(self.rect[1].y-self.rect[0].y)
def width(self):
""" Return width. Abs is for pre-normalized value."""
return abs(self.rect[1].x-self.rect[0].x)
@ -260,7 +260,7 @@ class pin_layout:
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
@ -278,7 +278,7 @@ class pin_layout:
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)
@ -303,7 +303,7 @@ class pin_layout:
def cy(self):
""" Center y """
return 0.5*(self.rect[0].y+self.rect[1].y)
# The four possible corners
def ll(self):
""" Lower left point """
@ -320,7 +320,7 @@ class pin_layout:
def ur(self):
""" Upper right point """
return self.rect[1]
# The possible y edge values
def uy(self):
""" Upper y value """
@ -331,15 +331,15 @@ class pin_layout:
return self.rect[0].y
# The possible x edge values
def lx(self):
""" Left x value """
return self.rect[0].x
def rx(self):
""" Right x value """
return self.rect[1].x
# The edge centers
def rc(self):
""" Right center point """
@ -350,7 +350,7 @@ class pin_layout:
""" Left center point """
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),
@ -378,6 +378,7 @@ class pin_layout:
# imported into Magic.
newLayout.addText(text=self.name,
layerNumber=layer_num,
purposeNumber=purpose,
offsetInMicrons=self.center(),
magnification=GDS["zoom"],
rotate=None)
@ -392,7 +393,7 @@ class pin_layout:
dy = min(r1_ur.y, r2_ur.y) - max(r1_ll.y, r2_ll.y)
dx = min(r1_ur.x, r2_ur.x) - max(r1_ll.x, r2_ll.x)
if dx >= 0 and dy >= 0:
return [dx, dy]
else:
@ -407,7 +408,7 @@ class pin_layout:
def dist(x1, y1, x2, y2):
return math.sqrt((x2-x1)**2 + (y2-y1)**2)
left = r2_ur.x < r1_ll.x
right = r1_ur.x < r2_ll.x
bottom = r2_ur.y < r1_ll.y
@ -432,7 +433,7 @@ class pin_layout:
else:
# rectangles intersect
return 0
def overlap_length(self, other):
"""
Calculate the intersection segment and determine its length
@ -453,7 +454,7 @@ class pin_layout:
# 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
@ -469,19 +470,19 @@ 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)
next(b, None)
return zip(a, b)
# R1 edges CW
r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll]
r1_edges = []
for (p, q) in pairwise(r1_cw_points):
r1_edges.append([p, q])
# R2 edges CW
r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll]
r2_edges = []
@ -509,9 +510,9 @@ class pin_layout:
q.y <= max(p.y, r.y) and \
q.y >= min(p.y, r.y):
return True
return False
def segment_intersection(self, s1, s2):
"""
Determine the intersection point of two segments
@ -524,22 +525,22 @@ class pin_layout:
a1 = b.y - a.y
b1 = a.x - b.x
c1 = a1*a.x + b1*a.y
# Line CD represented as a2x + b2y = c2
a2 = d.y - c.y
b2 = c.x - d.x
c2 = a2*c.x + b2*c.y
determinant = a1*b2 - a2*b1
if determinant != 0:
x = (b2*c1 - b1*c2)/determinant
y = (a1*c2 - a2*c1)/determinant
r = 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):
@ -549,5 +550,5 @@ class pin_layout:
"""
if lpp1[1] == None or lpp2[1] == None:
return lpp1[0] == lpp2[0]
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]

View File

@ -180,30 +180,30 @@ class delay(simulation):
def create_read_bit_measures(self):
""" Adds bit measurements for read0 and read1 cycles """
self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
self.read_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE)
for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items():
meas.meta_str = cycle
self.bit_meas[polarity].append(meas)
self.read_bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements
return [meas for meas_list in self.bit_meas.values() for meas in meas_list]
return [meas for meas_list in self.read_bit_meas.values() for meas in meas_list]
def create_write_bit_measures(self):
""" Adds bit measurements for write0 and write1 cycles """
self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
self.write_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE)
for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items():
meas.meta_str = cycle
self.bit_meas[polarity].append(meas)
self.write_bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements
return [meas for meas_list in self.bit_meas.values() for meas in meas_list]
return [meas for meas_list in self.write_bit_meas.values() for meas in meas_list]
def get_bit_measures(self, meas_tag, probe_address, probe_data):
"""
@ -649,8 +649,9 @@ class delay(simulation):
if (time_out <= 0):
debug.error("Timed out, could not find a feasible period.",2)
# Clear any write target ports and set read port
self.targ_write_ports = [port]
# Write ports are assumed non-critical to timing, so the first available is used
self.targ_write_ports = [self.write_ports[0]]
# Set target read port for simulation
self.targ_read_ports = [port]
debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port))
@ -733,7 +734,8 @@ class delay(simulation):
# First, check that the memory has the right values at the right times
if not self.check_bit_measures():
if not self.check_bit_measures(self.read_bit_meas) or \
not self.check_bit_measures(self.write_bit_meas):
return(False,{})
for port in self.targ_write_ports:
@ -824,13 +826,13 @@ class delay(simulation):
return dout_success
def check_bit_measures(self):
def check_bit_measures(self, bit_measures):
"""
Checks the measurements which represent the internal storage voltages
at the end of the read cycle.
"""
success = False
for polarity, meas_list in self.bit_meas.items():
for polarity, meas_list in bit_measures.items():
for meas in meas_list:
val = meas.retrieve_measure()
debug.info(2,"{}={}".format(meas.name, val))
@ -965,7 +967,8 @@ class delay(simulation):
# Binary search algorithm to find the min period (max frequency) of input port
time_out = 25
self.targ_write_ports = [port]
# Write ports are assumed non-critical to timing, so the first available is used
self.targ_write_ports = [self.write_ports[0]]
self.targ_read_ports = [port]
while True:
time_out -= 1
@ -1253,8 +1256,8 @@ class delay(simulation):
"""
# Using this requires setting at least one port to target for simulation.
if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0:
debug.error("No port selected for characterization.",1)
if len(self.targ_write_ports) == 0 or len(self.targ_read_ports) == 0:
debug.error("Write and read port must be specified for characterization.",1)
self.set_stimulus_variables()
# Get any available read/write port in case only a single write or read ports is being characterized.

View File

@ -81,17 +81,25 @@ class lib:
self.lib_files = []
# Nominal corner
self.add_corner(nom_process, nom_supply, nom_temperature)
corner_set = set()
nom_corner = (nom_process, nom_supply, nom_temperature)
corner_set.add(nom_corner)
if not OPTS.nominal_corner_only:
# Temperature corners
self.add_corner(nom_process, nom_supply, min_temperature)
self.add_corner(nom_process, nom_supply, max_temperature)
corner_set.add((nom_process, nom_supply, min_temperature))
corner_set.add((nom_process, nom_supply, max_temperature))
# Supply corners
self.add_corner(nom_process, min_supply, nom_temperature)
self.add_corner(nom_process, max_supply, nom_temperature)
corner_set.add((nom_process, min_supply, nom_temperature))
corner_set.add((nom_process, max_supply, nom_temperature))
# Process corners
self.add_corner(min_process, nom_supply, nom_temperature)
self.add_corner(max_process, nom_supply, nom_temperature)
corner_set.add((min_process, nom_supply, nom_temperature))
corner_set.add((max_process, nom_supply, nom_temperature))
# Enforce that nominal corner is the first to be characterized
self.add_corner(*nom_corner)
corner_set.remove(nom_corner)
for corner_tuple in corner_set:
self.add_corner(*corner_tuple)
def add_corner(self, proc, volt, temp):
self.corner_name = "{0}_{1}_{2}V_{3}C".format(self.sram.name,

View File

@ -1,12 +1,5 @@
import math
from globals import OPTS
# default purpose layer is used for addText() in vlsiLayout.py
if OPTS.tech_name == "s8":
purposeLayer=20
else:
purposeLayer=0
class GdsStructure:
"""Class represent a GDS Structure Object"""
def __init__(self):
@ -147,7 +140,7 @@ class GdsText:
self.elementFlags=""
self.plex=""
self.drawingLayer=""
self.purposeLayer=purposeLayer
self.purposeLayer=0
self.transFlags=[0,0,0]
self.magFactor=""
self.rotateAngle=""

View File

@ -35,7 +35,7 @@ class VlsiLayout:
modDate.hour,
modDate.minute,
modDate.second)
self.info = dict() #information gathered from the GDSII header
self.info['units']=self.units
self.info['dates']=(modDate.year,
@ -52,12 +52,12 @@ class VlsiLayout:
modDate.second)
self.info['libraryName']=libraryName
self.info['gdsVersion']=gdsVersion
self.xyTree = [] #This will contain a list of all structure names
#expanded to include srefs / arefs separately.
#each structure will have an X,Y,offset, and rotate associated
#with it. Populate via traverseTheHierarchy method.
#temp variables used in delegate functions
self.tempCoordinates=None
self.tempPassFail = True
@ -73,14 +73,14 @@ class VlsiLayout:
if(rotateAngle):
angle = math.radians(float(rotateAngle))
coordinatesRotate = [] #this will hold the rotated values
coordinatesRotate = [] #this will hold the rotated values
for coordinate in coordinatesToRotate:
# This is the CCW rotation matrix
newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle)
newY = coordinate[0]*math.sin(angle) + coordinate[1]*math.cos(angle)
coordinatesRotate.extend((newX,newY))
return coordinatesRotate
def rename(self,newName):
#take the root structure and copy it to a new structure with the new name
self.structures[newName] = self.structures[self.rootStructureName]
@ -129,8 +129,8 @@ class VlsiLayout:
modDate.hour,
modDate.minute,
modDate.second)
#repopulate the 2d map so drawing occurs correctly
self.prepareForWrite()
@ -155,15 +155,15 @@ 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,
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
#parameters explicitly
if startingStructureName == None:
startingStructureName = self.rootStructureName
startingStructureName = self.rootStructureName
#set up the rotation matrix
#set up the rotation matrix
if(rotateAngle == None or rotateAngle == ""):
angle = 0
else:
@ -193,10 +193,10 @@ class VlsiLayout:
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)
#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,
#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,
@ -204,12 +204,12 @@ class VlsiLayout:
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]
return
def initialize(self):
self.deduceHierarchy()
# self.traverseTheHierarchy()
@ -217,17 +217,17 @@ class VlsiLayout:
for layerNumber in self.layerNumbersInUse:
self.processLabelPins((layerNumber, None))
def populateCoordinateMap(self):
def addToXyTree(startingStructureName = None,transformPath = None):
uVector = np.array([[1.0],[0.0],[0.0]]) #start with normal basis vectors
vVector = np.array([[0.0],[1.0],[0.0]])
origin = np.array([[0.0],[0.0],[1.0]]) #and an origin (Z component is 1.0 to indicate position instead of vector)
#make a copy of all the transforms and reverse it
#make a copy of all the transforms and reverse it
reverseTransformPath = transformPath[:]
if len(reverseTransformPath) > 1:
reverseTransformPath.reverse()
reverseTransformPath.reverse()
#now go through each transform and apply them to our basis and origin in succession
for transform in reverseTransformPath:
origin = np.dot(transform[0], origin) #rotate
@ -237,20 +237,20 @@ class VlsiLayout:
uVector = np.dot(transform[1], uVector) #scale
vVector = np.dot(transform[1], vVector) #scale
origin = np.dot(transform[2], origin) #translate
#we don't need to do a translation on the basis vectors
#we don't need to do a translation on the basis vectors
#uVector = transform[2] * uVector #translate
#vVector = transform[2] * vVector #translate
#populate the xyTree with each structureName and coordinate space
self.xyTree.append((startingStructureName,origin,uVector,vVector))
self.traverseTheHierarchy(delegateFunction = addToXyTree)
def microns(self, userUnits):
"""Utility function to convert user units to microns"""
userUnit = self.units[1]/self.units[0]
userUnitsPerMicron = userUnit / userunit
layoutUnitsPerMicron = userUnitsPerMicron / self.units[0]
return userUnits / layoutUnitsPerMicron
def userUnits(self, microns):
"""Utility function to convert microns to user units"""
userUnit = self.units[1]/self.units[0]
@ -270,7 +270,7 @@ class VlsiLayout:
if self.debug:
debug.info(0,"DEBUG: GdsMill vlsiLayout: changeRoot: %s "%newRoot)
# Determine if newRoot exists
# layoutToAdd (default) or nameOfLayout
if (newRoot == 0 | ((newRoot not in self.structures) & ~create)):
@ -282,19 +282,19 @@ class VlsiLayout:
self.rootStructureName = newRoot
def addInstance(self,layoutToAdd,nameOfLayout=0,offsetInMicrons=(0,0),mirror=None,rotate=None):
"""
Method to insert one layout into another at a particular offset.
"""
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
if self.debug:
if self.debug:
debug.info(0,"DEBUG: GdsMill vlsiLayout: addInstance: type {0}, nameOfLayout {1}".format(type(layoutToAdd),nameOfLayout))
debug.info(0,"DEBUG: name={0} offset={1} mirror={2} rotate={3}".format(layoutToAdd.rootStructureName,offsetInMicrons, mirror, rotate))
# Determine if we are instantiating the root design of
# Determine if we are instantiating the root design of
# layoutToAdd (default) or nameOfLayout
if nameOfLayout == 0:
StructureFound = True
@ -303,7 +303,7 @@ class VlsiLayout:
StructureName = nameOfLayout #layoutToAdd
StructureFound = False
for structure in layoutToAdd.structures:
if StructureName in structure:
if StructureName in structure:
if self.debug:
debug.info(1,"DEBUG: Structure %s Found"%StructureName)
StructureFound = True
@ -311,7 +311,7 @@ class VlsiLayout:
debug.check(StructureFound,"Could not find layout to instantiate {}".format(StructureName))
# If layoutToAdd is a unique object (not this), then copy hierarchy,
# If layoutToAdd is a unique object (not this), then copy hierarchy,
# otherwise, if it is a text name of an internal structure, use it.
if layoutToAdd != self:
@ -330,7 +330,7 @@ class VlsiLayout:
layoutToAddSref.coordinates = offsetInLayoutUnits
if mirror or rotate:
layoutToAddSref.transFlags = [0,0,0]
# transFlags = (mirror around x-axis, magnification, rotation)
# If magnification or rotation is true, it is the flags are then
@ -356,7 +356,7 @@ class VlsiLayout:
#add the sref to the root structure
self.structures[self.rootStructureName].srefs.append(layoutToAddSref)
def addBox(self,layerNumber=0, purposeNumber=0, offsetInMicrons=(0,0), width=1.0, height=1.0,center=False):
"""
Method to add a box to a layout
@ -373,7 +373,7 @@ class VlsiLayout:
(offsetInLayoutUnits[0],offsetInLayoutUnits[1]+heightInLayoutUnits),
offsetInLayoutUnits]
else:
startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2.0, offsetInLayoutUnits[1]-heightInLayoutUnits/2.0)
startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2.0, offsetInLayoutUnits[1]-heightInLayoutUnits/2.0)
coordinates=[startPoint,
(startPoint[0]+widthInLayoutUnits,startPoint[1]),
(startPoint[0]+widthInLayoutUnits,startPoint[1]+heightInLayoutUnits),
@ -386,7 +386,7 @@ class VlsiLayout:
boundaryToAdd.purposeLayer = purposeNumber
#add the sref to the root structure
self.structures[self.rootStructureName].boundaries.append(boundaryToAdd)
def addPath(self, layerNumber=0, purposeNumber=0, coordinates=[(0,0)], width=1.0):
"""
Method to add a path to a layout
@ -405,11 +405,12 @@ class VlsiLayout:
pathToAdd.coordinates = layoutUnitCoordinates
#add the sref to the root structure
self.structures[self.rootStructureName].paths.append(pathToAdd)
def addText(self, text, layerNumber=0, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
def addText(self, text, layerNumber=0, purposeNumber=0, 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.coordinates = [offsetInLayoutUnits]
textToAdd.transFlags = [0,0,0]
textToAdd.textString = self.padText(text)
@ -438,7 +439,7 @@ class VlsiLayout:
return 1
else:
return 0
def intersectionPoint(self,startPoint1,endPoint1,startPoint2,endPoint2):
if((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])!=0):
pSlope = (endPoint1[1]-startPoint1[1])/(endPoint1[0]-startPoint1[0])
@ -458,7 +459,7 @@ class VlsiLayout:
newY = None
elif((endPoint1[0]-startPoint1[0])==0 and (endPoint2[0]-startPoint2[0])!=0):
qSlope = (endPoint2[1]-startPoint2[1])/(endPoint2[0]-startPoint2[0])
qIntercept = startPoint2[1]-qSlope*startPoint2[0]
qIntercept = startPoint2[1]-qSlope*startPoint2[0]
newX=endPoint1[0]
newY=qSlope*newX+qIntercept
elif((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])==0):
@ -467,14 +468,14 @@ class VlsiLayout:
newX=endPoint2[0]
newY=pSlope*newX+pIntercept
return (newX,newY)
def isCollinear(self,testPoint,point1,point2):
slope1 = (testPoint[1]-point1[1])/(testPoint[0]-point1[0])
slope2 = (point2[1]-point1[1])/(point2[0]-point1[0])
if slope1 == slope2:
return True
return False
def doShapesIntersect(self,shape1Coordinates, shape2Coordinates):
"""
Utility function to determine if 2 arbitrary shapes intersect.
@ -491,7 +492,7 @@ class VlsiLayout:
if(self.isBounded(intersect,startPoint1,endPoint1) and self.isBounded(intersect,startPoint2,endPoint2)):
return True #these shapes overlap!
return False #these shapes are ok
def isPointInsideOfBox(self,pointCoordinates,boxCoordinates):
"""
Check if a point is contained in the shape
@ -516,7 +517,7 @@ class VlsiLayout:
pointCoordinates[1]<bottomBound):
return False
return True
def isShapeInsideOfBox(self,shapeCoordinates, boxCoordinates):
"""
Go through every point in the shape to test if they are all inside the box.
@ -525,8 +526,8 @@ class VlsiLayout:
if not self.isPointInsideOfBox(point,boxCoordinates):
return False
return True
def fillAreaDensity(self, layerToFill = 0, offsetInMicrons = (0,0), coverageWidth = 100.0, coverageHeight = 100.0, minSpacing = 0.22, blockSize = 1.0):
effectiveBlock = blockSize+minSpacing
widthInBlocks = int(coverageWidth/effectiveBlock)
@ -545,7 +546,7 @@ class VlsiLayout:
shiftedBoundaryCoordinates.append((shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1]))
joint = self.doShapesIntersect(self.tempCoordinates, shiftedBoundaryCoordinates)
if joint:
self.tempPassFail = False
self.tempPassFail = False
common = self.isShapeInsideOfBox(shiftedBoundaryCoordinates,self.tempCoordinates)
if common:
self.tempPassFail = False
@ -558,11 +559,11 @@ class VlsiLayout:
shiftedBoundaryCoordinates.append((shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1]))
joint = self.doShapesIntersect(self.tempCoordinates, shiftedBoundaryCoordinates)
if joint:
self.tempPassFail = False
self.tempPassFail = False
common = self.isShapeInsideOfBox(shiftedBoundaryCoordinates,self.tempCoordinates)
if common:
self.tempPassFail = False
for yIndex in range(0,heightInBlocks):
for xIndex in range(0,widthInBlocks):
percentDone = (float((yIndex*heightInBlocks)+xIndex) / (heightInBlocks*widthInBlocks))*100
@ -581,7 +582,7 @@ class VlsiLayout:
passFailRecord.append(self.tempPassFail)
print("Percent Complete:"+str(percentDone))
passFailIndex=0
for yIndex in range(0,heightInBlocks):
for xIndex in range(0,widthInBlocks):
@ -632,7 +633,7 @@ class VlsiLayout:
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
@ -645,7 +646,7 @@ class VlsiLayout:
thisBoundary[2]+structureOrigin[0],thisBoundary[3]+structureOrigin[1]]
cellBoundary=self.updateBoundary(thisBoundary,cellBoundary)
return cellBoundary
def updateBoundary(self,thisBoundary,cellBoundary):
[left_bott_X,left_bott_Y,right_top_X,right_top_Y]=thisBoundary
# If any are None
@ -672,7 +673,7 @@ class VlsiLayout:
lpp):
text_list.append(Text)
return text_list
def getPinShape(self, pin_name):
"""
Search for a pin label and return the largest enclosing rectangle
@ -693,7 +694,7 @@ class VlsiLayout:
max_pins.append(max_pin)
return max_pins
def getAllPinShapes(self, pin_name):
"""
@ -716,7 +717,7 @@ class VlsiLayout:
"""
# Get the labels on a layer in the root level
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(lpp)
@ -730,7 +731,7 @@ class VlsiLayout:
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]
@ -740,7 +741,7 @@ class VlsiLayout:
except KeyError:
self.pins[label_text] = []
self.pins[label_text].append(pin_shapes)
def getBlockages(self, lpp):
"""
Return all blockages on a given layer in
@ -755,7 +756,7 @@ class VlsiLayout:
for i in range(0, len(boundary), 2):
vectors.append((boundary[i], boundary[i+1]))
blockages.append(vectors)
return blockages
def getAllShapes(self, lpp):
@ -870,7 +871,7 @@ class VlsiLayout:
"""
Rotate a coordinate in space.
"""
# MRG: 9/3/18 Incorrect matrix multiplication!
# MRG: 9/3/18 Incorrect matrix multiplication!
# This is fixed to be:
# |u[0] v[0]| |x| |x'|
# |u[1] v[1]|x|y|=|y'|
@ -892,7 +893,7 @@ class VlsiLayout:
else:
return False
def sameLPP(lpp1, lpp2):
"""
Check if the layers and purposes are the same.
@ -900,14 +901,13 @@ def sameLPP(lpp1, lpp2):
"""
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):
"""
Returns boundary area for sorting.
"""
area_A=(A[2]-A[0])*(A[3]-A[1])
return area_A

View File

@ -647,7 +647,7 @@ class control_logic(design.design):
if self.port_type=="rw":
input_name = "we_bar"
else:
input_name = "cs_bar"
input_name = "cs"
# GATE FOR S_EN
self.s_en_gate_inst = self.add_inst(name="buf_s_en_and",
mod=self.sen_and3)
@ -670,7 +670,7 @@ class control_logic(design.design):
if self.port_type=="rw":
input_name = "we_bar"
else:
input_name = "cs_bar"
input_name = "cs"
sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name])
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets)

View File

@ -8,6 +8,7 @@ from tech import drc, parameter
import debug
import design
from sram_factory import factory
from collections import namedtuple
from vector import vector
from globals import OPTS
@ -46,7 +47,19 @@ class port_data(design.design):
# br lines are connect from the precharger
return self.precharge.get_br_names()
def get_bl_name(self, port=0):
bl_name = "bl"
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self, port=0):
br_name = "br"
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
def create_netlist(self):
self.precompute_constants()
@ -94,8 +107,8 @@ class port_data(design.design):
self.add_pin("rbl_bl","INOUT")
self.add_pin("rbl_br","INOUT")
for bit in range(self.num_cols):
bl_name = self.precharge_array.get_bl_name(self.port)
br_name = self.precharge_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
self.add_pin("{0}_{1}".format(bl_name, bit),"INOUT")
self.add_pin("{0}_{1}".format(br_name, bit),"INOUT")
if self.port in self.read_ports:
@ -253,8 +266,8 @@ class port_data(design.design):
self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port),
mod=self.precharge_array)
bl_name = self.precharge_array.get_bl_name(self.port)
br_name = self.precharge_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
temp = []
# Use left BLs for RBL
@ -285,8 +298,8 @@ class port_data(design.design):
self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port),
mod=self.column_mux_array)
bl_name = self.column_mux_array.get_bl_name(self.port)
br_name = self.column_mux_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
temp = []
for col in range(self.num_cols):
temp.append("{0}_{1}".format(bl_name, col))
@ -315,8 +328,8 @@ class port_data(design.design):
self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port),
mod=self.sense_amp_array)
bl_name = self.sense_amp_array.get_bl_name(self.port)
br_name = self.sense_amp_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
temp = []
for bit in range(self.word_size):
temp.append("dout_{}".format(bit))
@ -341,8 +354,8 @@ class port_data(design.design):
""" Creating Write Driver """
self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port),
mod=self.write_driver_array)
bl_name = self.write_driver_array.get_bl_name(self.port)
br_name = self.write_driver_array.get_br_name(self.port)
bl_name = self.get_bl_name(self.port)
br_name = self.get_br_name(self.port)
temp = []
for bit in range(self.word_size):
@ -523,13 +536,16 @@ class port_data(design.design):
# Only do this if we have a column mux!
if self.col_addr_size==0:
return
inst1 = self.column_mux_array_inst
inst2 = self.precharge_array_inst
if self.port==0:
self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1)
else:
self.connect_bitlines(inst1, inst2, self.num_cols)
insn2_start_bit = 1 if self.port == 0 else 0
self.connect_bitlines(inst1=inst1,
inst2=inst2,
num_bits=self.num_cols,
inst2_start_bit=insn2_start_bit)
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
@ -539,46 +555,49 @@ class port_data(design.design):
if self.col_addr_size>0:
# Sense amp is connected to the col mux
inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
inst1_bls_templ = "{inst}_out_{bit}"
start_bit = 0
else:
# Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
inst1_bls_templ="{inst}_{bit}"
if self.port==0:
start_bit=1
else:
start_bit=0
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
self.channel_route_bitlines(inst1=inst1,
inst1_bls_template=inst1_bls_templ,
inst2=inst2,
num_bits=self.word_size,
inst1_start_bit=start_bit)
def route_write_driver_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """
inst2 = self.write_driver_array_inst
inst2_bl_name = inst2.mod.get_bl_name()
inst2_br_name = inst2.mod.get_br_name()
if self.col_addr_size>0:
# Write driver is connected to the col mux
inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
inst1_bls_templ = "{inst}_out_{bit}"
start_bit = 0
else:
# Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
inst1_bls_templ="{inst}_{bit}"
if self.port==0:
start_bit=1
else:
start_bit=0
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
self.channel_route_bitlines(inst1=inst1, inst2=inst2,
num_bits=self.word_size,
inst1_bls_template=inst1_bls_templ,
inst1_start_bit=start_bit)
def route_write_driver_to_sense_amp(self, port):
@ -589,7 +608,9 @@ class port_data(design.design):
# These should be pitch matched in the cell library,
# but just in case, do a channel route.
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size)
self.channel_route_bitlines(inst1=inst1,
inst2=inst2,
num_bits=self.word_size)
def route_bitline_pins(self):
@ -635,64 +656,108 @@ class port_data(design.design):
if self.write_mask_and_array_inst:
self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en")
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
def _group_bitline_instances(self, inst1, inst2, num_bits,
inst1_bls_template,
inst1_start_bit,
inst2_bls_template,
inst2_start_bit):
"""
Route the bl and br of two modules using the channel router.
Groups all the parameters into a named tuple and seperates them into
top and bottom instances.
"""
inst_group = namedtuple('InstanceGroup', ('inst', 'bls_template',
'bl_name', 'br_name', 'start_bit'))
inst1_group = inst_group(inst1, inst1_bls_template,
inst1.mod.get_bl_name(),
inst1.mod.get_br_name(),
inst1_start_bit)
inst2_group = inst_group(inst2, inst2_bls_template,
inst2.mod.get_bl_name(),
inst2.mod.get_br_name(),
inst2_start_bit)
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
bot_inst_group = inst1_group
top_inst_group = inst2_group
else:
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
bot_inst_group = inst2_group
top_inst_group = inst1_group
return (bot_inst_group, top_inst_group)
def _get_bitline_pins(self, inst_group, bit):
"""
Extracts bl/br pins from an InstanceGroup based on the bit modifier.
"""
full_bl_name = inst_group.bls_template.format(
**{'inst' : inst_group.bl_name,
'bit' : inst_group.start_bit + bit}
)
full_br_name = inst_group.bls_template.format(
**{'inst' : inst_group.br_name,
'bit' : inst_group.start_bit + bit}
)
return (inst_group.inst.get_pin(full_bl_name),
inst_group.inst.get_pin(full_br_name))
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bls_template="{inst}_{bit}",
inst1_start_bit=0,
inst2_bls_template="{inst}_{bit}",
inst2_start_bit=0):
"""
Route the bl and br of two modules using the channel router.
"""
bot_inst_group, top_inst_group = self._group_bitline_instances(
inst1, inst2, num_bits,
inst1_bls_template, inst1_start_bit,
inst2_bls_template, inst2_start_bit)
# Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
offset = bot_inst_group.inst.ul() + vector(0,self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))]
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
top_names = self._get_bitline_pins(top_inst_group, bit)
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
inst1_bls_template="{inst}_{bit}",
inst1_start_bit=0,
inst2_bls_template="{inst}_{bit}",
inst2_start_bit=0):
"""
Connect the bl and br of two modules.
This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed).
"""
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
else:
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
bot_inst_group, top_inst_group = self._group_bitline_instances(
inst1, inst2, num_bits,
inst1_bls_template, inst1_start_bit,
inst2_bls_template, inst2_start_bit)
bottom_inst = bot_inst_group.inst
top_inst = top_inst_group.inst
for col in range(num_bits):
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col+bottom_start_bit)).uc()
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col+bottom_start_bit)).uc()
top_bl = top_inst.get_pin(top_bl_name.format(col+top_start_bit)).bc()
top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc()
bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col)
top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col)
bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc()
top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc()
yoffset = 0.5*(top_bl.y+bottom_bl.y)
self.add_path("m2",[bottom_bl, vector(bottom_bl.x,yoffset),
yoffset = 0.5*(top_bl.y+bot_bl.y)
self.add_path("m2",[bot_bl, vector(bot_bl.x,yoffset),
vector(top_bl.x,yoffset), top_bl])
self.add_path("m2",[bottom_br, vector(bottom_br.x,yoffset),
self.add_path("m2",[bot_br, vector(bot_br.x,yoffset),
vector(top_br.x,yoffset), top_br])
def graph_exclude_precharge(self):

View File

@ -32,20 +32,13 @@ class precharge_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
def get_bl_name(self):
bl_name = self.pc_cell.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
return bl_name
def get_br_name(self, port=0):
def get_br_name(self):
br_name = self.pc_cell.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
return br_name
def add_pins(self):
"""Adds pins for spice file"""

View File

@ -9,6 +9,7 @@ import design
import debug
import utils
from tech import GDS,layer, parameter,drc
from tech import cell_properties as props
from globals import OPTS
import logical_effort
@ -19,8 +20,12 @@ class sense_amp(design.design):
the technology library.
Sense amplifier to read a pair of bit-lines.
"""
pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"]
pin_names = [props.sense_amp.pin.bl,
props.sense_amp.pin.br,
props.sense_amp.pin.dout,
props.sense_amp.pin.en,
props.sense_amp.pin.vdd,
props.sense_amp.pin.gnd]
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
@ -30,10 +35,18 @@ class sense_amp(design.design):
pin_map = []
def get_bl_names(self):
return "bl"
return props.sense_amp.pin.bl
def get_br_names(self):
return "br"
return props.sense_amp.pin.br
@property
def dout_name(self):
return props.sense_amp.pin.dout
@property
def en_name(self):
return props.sense_amp.pin.en
def __init__(self, name):
design.design.__init__(self, name)
@ -79,11 +92,10 @@ class sense_amp(design.design):
def get_enable_name(self):
"""Returns name used for enable net"""
#FIXME: A better programmatic solution to designate pins
enable_name = "en"
enable_name = self.en_name
debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name))
return enable_name
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)

View File

@ -33,20 +33,21 @@ class sense_amp_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self):
bl_name = "bl"
return bl_name
def get_bl_name(self, port=0):
bl_name = self.amp.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self):
br_name = "br"
return br_name
def get_br_name(self, port=0):
br_name = self.amp.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
@property
def data_name(self):
return "data"
@property
def en_name(self):
return "en"
def create_netlist(self):
self.add_modules()
@ -69,10 +70,10 @@ class sense_amp_array(design.design):
def add_pins(self):
for i in range(0,self.word_size):
self.add_pin("data_{0}".format(i), "OUTPUT")
self.add_pin("bl_{0}".format(i), "INPUT")
self.add_pin("br_{0}".format(i), "INPUT")
self.add_pin("en", "INPUT")
self.add_pin(self.data_name + "_{0}".format(i), "OUTPUT")
self.add_pin(self.get_bl_name() + "_{0}".format(i), "INPUT")
self.add_pin(self.get_br_name() + "_{0}".format(i), "INPUT")
self.add_pin(self.en_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
@ -92,10 +93,10 @@ class sense_amp_array(design.design):
name = "sa_d{0}".format(i)
self.local_insts.append(self.add_inst(name=name,
mod=self.amp))
self.connect_inst(["bl_{0}".format(i),
"br_{0}".format(i),
"data_{0}".format(i),
"en", "vdd", "gnd"])
self.connect_inst([self.get_bl_name() + "_{0}".format(i),
self.get_br_name() + "_{0}".format(i),
self.data_name + "_{0}".format(i),
self.en_name, "vdd", "gnd"])
def place_sense_amp_array(self):
from tech import cell_properties
@ -135,22 +136,22 @@ class sense_amp_array(design.design):
start_layer="m2",
vertical=True)
bl_pin = inst.get_pin("bl")
br_pin = inst.get_pin("br")
dout_pin = inst.get_pin("dout")
self.add_layout_pin(text="bl_{0}".format(i),
bl_pin = inst.get_pin(inst.mod.get_bl_names())
br_pin = inst.get_pin(inst.mod.get_br_names())
dout_pin = inst.get_pin(inst.mod.dout_name)
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
layer="m2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
self.add_layout_pin(text="br_{0}".format(i),
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
layer="m2",
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
self.add_layout_pin(text="data_{0}".format(i),
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
layer="m2",
offset=dout_pin.ll(),
width=dout_pin.width(),
@ -159,8 +160,8 @@ class sense_amp_array(design.design):
def route_rails(self):
# add sclk rail across entire array
sclk_offset = self.amp.get_pin("en").ll().scale(0,1)
self.add_layout_pin(text="en",
sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0,1)
self.add_layout_pin(text=self.en_name,
layer="m1",
offset=sclk_offset,
width=self.width,

View File

@ -37,20 +37,13 @@ class single_level_column_mux_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
def get_bl_name(self):
bl_name = self.mux.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
return bl_name
def get_br_name(self, port=0):
br_name = self.mux.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
return br_name
def create_netlist(self):
self.add_modules()

View File

@ -10,6 +10,7 @@ import design
import utils
from globals import OPTS
from tech import GDS,layer
from tech import cell_properties as props
class write_driver(design.design):
"""
@ -19,7 +20,13 @@ class write_driver(design.design):
the technology library.
"""
pin_names = ["din", "bl", "br", "en", "vdd", "gnd"]
pin_names = [props.write_driver.pin.din,
props.write_driver.pin.bl,
props.write_driver.pin.br,
props.write_driver.pin.en,
props.write_driver.pin.vdd,
props.write_driver.pin.gnd]
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
@ -38,10 +45,18 @@ class write_driver(design.design):
self.add_pin_types(self.type_list)
def get_bl_names(self):
return "bl"
return props.write_driver.pin.bl
def get_br_names(self):
return "br"
return props.write_driver.pin.br
@property
def din_name(self):
return props.write_driver.pin.din
@property
def en_name(self):
return props.write_driver.pin.en
def get_w_en_cin(self):
"""Get the relative capacitance of a single input"""

View File

@ -37,19 +37,21 @@ class write_driver_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
bl_name = self.driver.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_bl_name(self):
bl_name = "bl"
return bl_name
def get_br_name(self, port=0):
br_name = self.driver.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
def get_br_name(self):
br_name = "br"
return br_name
@property
def data_name(self):
return "data"
@property
def en_name(self):
return "en"
def create_netlist(self):
self.add_modules()
@ -71,15 +73,15 @@ class write_driver_array(design.design):
def add_pins(self):
for i in range(self.word_size):
self.add_pin("data_{0}".format(i), "INPUT")
for i in range(self.word_size):
self.add_pin("bl_{0}".format(i), "OUTPUT")
self.add_pin("br_{0}".format(i), "OUTPUT")
self.add_pin(self.data_name + "_{0}".format(i), "INPUT")
for i in range(self.word_size):
self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT")
self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT")
if self.write_size:
for i in range(self.num_wmasks):
self.add_pin("en_{0}".format(i), "INPUT")
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
else:
self.add_pin("en", "INPUT")
self.add_pin(self.en_name, "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
@ -102,20 +104,20 @@ class write_driver_array(design.design):
mod=self.driver)
if self.write_size:
self.connect_inst(["data_{0}".format(index),
"bl_{0}".format(index),
"br_{0}".format(index),
"en_{0}".format(windex), "vdd", "gnd"])
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name + "_{0}".format(windex), "vdd", "gnd"])
w+=1
# when w equals write size, the next en pin can be connected since we are now at the next wmask bit
if w == self.write_size:
w = 0
windex+=1
else:
self.connect_inst(["data_{0}".format(index),
"bl_{0}".format(index),
"br_{0}".format(index),
"en", "vdd", "gnd"])
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
self.en_name, "vdd", "gnd"])
def place_write_array(self):
@ -140,21 +142,22 @@ class write_driver_array(design.design):
def add_layout_pins(self):
for i in range(self.word_size):
din_pin = self.driver_insts[i].get_pin("din")
self.add_layout_pin(text="data_{0}".format(i),
inst = self.driver_insts[i]
din_pin = inst.get_pin(inst.mod.din_name)
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
layer="m2",
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
bl_pin = self.driver_insts[i].get_pin("bl")
self.add_layout_pin(text="bl_{0}".format(i),
bl_pin = inst.get_pin(inst.mod.get_bl_names())
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
layer="m2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
br_pin = self.driver_insts[i].get_pin("br")
self.add_layout_pin(text="br_{0}".format(i),
br_pin = inst.get_pin(inst.mod.get_br_names())
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
layer="m2",
offset=br_pin.ll(),
width=br_pin.width(),
@ -169,7 +172,8 @@ class write_driver_array(design.design):
start_layer = "m2")
if self.write_size:
for bit in range(self.num_wmasks):
en_pin = self.driver_insts[bit*self.write_size].get_pin("en")
inst = self.driver_insts[bit*self.write_size]
en_pin = inst.get_pin(inst.mod.en_name)
# Determine width of wmask modified en_pin with/without col mux
wmask_en_len = self.words_per_row*(self.write_size * self.driver_spacing)
if (self.words_per_row == 1):
@ -177,15 +181,16 @@ class write_driver_array(design.design):
else:
en_gap = self.driver_spacing
self.add_layout_pin(text="en_{0}".format(bit),
self.add_layout_pin(text=self.en_name + "_{0}".format(bit),
layer=en_pin.layer,
offset=en_pin.ll(),
width=wmask_en_len-en_gap,
height=en_pin.height())
else:
self.add_layout_pin(text="en",
inst = self.driver_insts[0]
self.add_layout_pin(text=self.en_name,
layer="m1",
offset=self.driver_insts[0].get_pin("en").ll().scale(0,1),
offset=inst.get_pin(inst.mod.en_name).ll().scale(0,1),
width=self.width)