mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into bisr
This commit is contained in:
commit
d5041afebc
|
|
@ -1,7 +1,7 @@
|
||||||
before_script:
|
before_script:
|
||||||
- . /home/gitlab-runner/setup-paths.sh
|
- . /home/gitlab-runner/setup-paths.sh
|
||||||
- export OPENRAM_HOME="`pwd`/compiler"
|
- export OPENRAM_HOME="`pwd`/compiler"
|
||||||
- export OPENRAM_TECH="`pwd`/technology"
|
- export OPENRAM_TECH="`pwd`/technology:/home/PDKs/skywater-tech"
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
|
|
@ -25,6 +25,15 @@ scn4m_subm:
|
||||||
- .coverage.*
|
- .coverage.*
|
||||||
expire_in: 1 week
|
expire_in: 1 week
|
||||||
|
|
||||||
|
# s8:
|
||||||
|
# stage: test
|
||||||
|
# script:
|
||||||
|
# - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8
|
||||||
|
# artifacts:
|
||||||
|
# paths:
|
||||||
|
# - .coverage.*
|
||||||
|
# expire_in: 1 week
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
stage: coverage
|
stage: coverage
|
||||||
script:
|
script:
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,10 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
|
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
|
||||||
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
|
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
|
||||||
self.directions = (first_dir, second_dir)
|
self.directions = (first_dir, second_dir)
|
||||||
|
# Preferred directions
|
||||||
|
elif directions == "pref":
|
||||||
|
self.directions = (tech.preferred_directions[layer_stack[0]],
|
||||||
|
tech.preferred_directions[layer_stack[2]])
|
||||||
# User directions
|
# User directions
|
||||||
elif directions:
|
elif directions:
|
||||||
self.directions = directions
|
self.directions = directions
|
||||||
|
|
@ -149,7 +153,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
self.first_layer_vertical_enclosure = max(self.first_layer_enclosure,
|
self.first_layer_vertical_enclosure = max(self.first_layer_enclosure,
|
||||||
(self.first_layer_minwidth - self.contact_array_height) / 2)
|
(self.first_layer_minwidth - self.contact_array_height) / 2)
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid first layer direction.", -1)
|
debug.error("Invalid first layer direction: ".format(self.directions[0]), -1)
|
||||||
|
|
||||||
# In some technologies, the minimum width may be larger
|
# In some technologies, the minimum width may be larger
|
||||||
# than the overlap requirement around the via, so
|
# than the overlap requirement around the via, so
|
||||||
|
|
@ -165,7 +169,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
self.second_layer_vertical_enclosure = max(self.second_layer_enclosure,
|
self.second_layer_vertical_enclosure = max(self.second_layer_enclosure,
|
||||||
(self.second_layer_minwidth - self.contact_array_width) / 2)
|
(self.second_layer_minwidth - self.contact_array_width) / 2)
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid second layer direction.", -1)
|
debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
|
||||||
|
|
||||||
def create_contact_array(self):
|
def create_contact_array(self):
|
||||||
""" Create the contact array at the origin"""
|
""" Create the contact array at the origin"""
|
||||||
|
|
|
||||||
|
|
@ -66,14 +66,17 @@ class geometry:
|
||||||
self.compute_boundary(self.offset, self.mirror, self.rotate)
|
self.compute_boundary(self.offset, self.mirror, self.rotate)
|
||||||
|
|
||||||
def compute_boundary(self, offset=vector(0, 0), mirror="", rotate=0):
|
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. """
|
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:
|
if OPTS.netlist_only:
|
||||||
self.boundary = [vector(0,0), vector(0,0)]
|
self.boundary = [vector(0, 0), vector(0, 0)]
|
||||||
return
|
return
|
||||||
|
|
||||||
(ll, ur) = [vector(0, 0), vector(self.width, self.height)]
|
(ll, ur) = [vector(0, 0), vector(self.width, self.height)]
|
||||||
|
|
||||||
|
# Mirroring is performed before rotation
|
||||||
if mirror == "MX":
|
if mirror == "MX":
|
||||||
ll = ll.scale(1, -1)
|
ll = ll.scale(1, -1)
|
||||||
ur = ur.scale(1, -1)
|
ur = ur.scale(1, -1)
|
||||||
|
|
@ -83,8 +86,14 @@ class geometry:
|
||||||
elif mirror == "XY":
|
elif mirror == "XY":
|
||||||
ll = ll.scale(-1, -1)
|
ll = ll.scale(-1, -1)
|
||||||
ur = ur.scale(-1, -1)
|
ur = ur.scale(-1, -1)
|
||||||
|
elif mirror == "" or mirror == "R0":
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
debug.error("Invalid mirroring: {}".format(mirror), -1)
|
||||||
|
|
||||||
if rotate == 90:
|
if rotate == 0:
|
||||||
|
pass
|
||||||
|
elif rotate == 90:
|
||||||
ll = ll.rotate_scale(-1, 1)
|
ll = ll.rotate_scale(-1, 1)
|
||||||
ur = ur.rotate_scale(-1, 1)
|
ur = ur.rotate_scale(-1, 1)
|
||||||
elif rotate == 180:
|
elif rotate == 180:
|
||||||
|
|
@ -93,6 +102,8 @@ class geometry:
|
||||||
elif rotate == 270:
|
elif rotate == 270:
|
||||||
ll = ll.rotate_scale(1, -1)
|
ll = ll.rotate_scale(1, -1)
|
||||||
ur = ur.rotate_scale(1, -1)
|
ur = ur.rotate_scale(1, -1)
|
||||||
|
else:
|
||||||
|
debug.error("Invalid rotation: {}".format(rotate), -1)
|
||||||
|
|
||||||
self.boundary = [offset + ll, offset + ur]
|
self.boundary = [offset + ll, offset + ur]
|
||||||
self.normalize()
|
self.normalize()
|
||||||
|
|
@ -136,6 +147,10 @@ class geometry:
|
||||||
def cy(self):
|
def cy(self):
|
||||||
""" Return the center y """
|
""" Return the center y """
|
||||||
return 0.5 * (self.boundary[0].y + self.boundary[1].y)
|
return 0.5 * (self.boundary[0].y + self.boundary[1].y)
|
||||||
|
|
||||||
|
def center(self):
|
||||||
|
""" Return the center coordinate """
|
||||||
|
return vector(self.cx(), self.cy())
|
||||||
|
|
||||||
|
|
||||||
class instance(geometry):
|
class instance(geometry):
|
||||||
|
|
@ -195,14 +210,13 @@ class instance(geometry):
|
||||||
blockages = []
|
blockages = []
|
||||||
blockages = self.mod.gds.getBlockages(lpp)
|
blockages = self.mod.gds.getBlockages(lpp)
|
||||||
for b in blockages:
|
for b in blockages:
|
||||||
new_blockages.append(self.transform_coords(b,self.offset, mirr, angle))
|
new_blockages.append(self.transform_coords(b, self.offset, mirr, angle))
|
||||||
else:
|
else:
|
||||||
blockages = self.mod.get_blockages(lpp)
|
blockages = self.mod.get_blockages(lpp)
|
||||||
for b in blockages:
|
for b in blockages:
|
||||||
new_blockages.append(self.transform_coords(b,self.offset, mirr, angle))
|
new_blockages.append(self.transform_coords(b, self.offset, mirr, angle))
|
||||||
return new_blockages
|
return new_blockages
|
||||||
|
|
||||||
|
|
||||||
def gds_write_file(self, new_layout):
|
def gds_write_file(self, new_layout):
|
||||||
"""Recursively writes all the sub-modules in this instance"""
|
"""Recursively writes all the sub-modules in this instance"""
|
||||||
debug.info(4, "writing instance: " + self.name)
|
debug.info(4, "writing instance: " + self.name)
|
||||||
|
|
@ -225,26 +239,25 @@ class instance(geometry):
|
||||||
self.update_boundary()
|
self.update_boundary()
|
||||||
debug.info(3, "placing instance {}".format(self))
|
debug.info(3, "placing instance {}".format(self))
|
||||||
|
|
||||||
|
def get_pin(self, name, index=-1):
|
||||||
def get_pin(self,name,index=-1):
|
|
||||||
""" Return an absolute pin that is offset and transformed based on
|
""" Return an absolute pin that is offset and transformed based on
|
||||||
this instance location. Index will return one of several pins."""
|
this instance location. Index will return one of several pins."""
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
if index == -1:
|
if index == -1:
|
||||||
pin = copy.deepcopy(self.mod.get_pin(name))
|
pin = copy.deepcopy(self.mod.get_pin(name))
|
||||||
pin.transform(self.offset,self.mirror,self.rotate)
|
pin.transform(self.offset, self.mirror, self.rotate)
|
||||||
return pin
|
return pin
|
||||||
else:
|
else:
|
||||||
pins = copy.deepcopy(self.mod.get_pin(name))
|
pins = copy.deepcopy(self.mod.get_pin(name))
|
||||||
pin.transform(self.offset,self.mirror,self.rotate)
|
pins.transform(self.offset, self.mirror, self.rotate)
|
||||||
return pin[index]
|
return pin[index]
|
||||||
|
|
||||||
def get_num_pins(self, name):
|
def get_num_pins(self, name):
|
||||||
""" Return the number of pins of a given name """
|
""" Return the number of pins of a given name """
|
||||||
return len(self.mod.get_pins(name))
|
return len(self.mod.get_pins(name))
|
||||||
|
|
||||||
def get_pins(self,name):
|
def get_pins(self, name):
|
||||||
""" Return an absolute pin that is offset and transformed based on
|
""" Return an absolute pin that is offset and transformed based on
|
||||||
this instance location. """
|
this instance location. """
|
||||||
|
|
||||||
|
|
@ -253,7 +266,7 @@ class instance(geometry):
|
||||||
|
|
||||||
new_pins = []
|
new_pins = []
|
||||||
for p in pin:
|
for p in pin:
|
||||||
p.transform(self.offset,self.mirror,self.rotate)
|
p.transform(self.offset, self.mirror, self.rotate)
|
||||||
new_pins.append(p)
|
new_pins.append(p)
|
||||||
return new_pins
|
return new_pins
|
||||||
|
|
||||||
|
|
@ -265,6 +278,7 @@ class instance(geometry):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
||||||
|
|
||||||
|
|
||||||
class path(geometry):
|
class path(geometry):
|
||||||
"""Represents a Path"""
|
"""Represents a Path"""
|
||||||
|
|
||||||
|
|
@ -322,7 +336,7 @@ class label(geometry):
|
||||||
|
|
||||||
self.size = 0
|
self.size = 0
|
||||||
|
|
||||||
debug.info(4,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset))
|
debug.info(4, "creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset))
|
||||||
|
|
||||||
def gds_write_file(self, new_layout):
|
def gds_write_file(self, new_layout):
|
||||||
"""Writes the text label to GDS"""
|
"""Writes the text label to GDS"""
|
||||||
|
|
@ -340,7 +354,7 @@ class label(geometry):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
return "label: " + self.text + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose)
|
return "label: " + self.text + " layer=" + str(self.layerNumber) + " purpose=" + str(self.layerPurpose)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
""" override print function output """
|
""" override print function output """
|
||||||
|
|
|
||||||
|
|
@ -42,14 +42,14 @@ class layout():
|
||||||
self.visited = [] # List of modules we have already visited
|
self.visited = [] # List of modules we have already visited
|
||||||
self.is_library_cell = False # Flag for library cells
|
self.is_library_cell = False # Flag for library cells
|
||||||
self.gds_read()
|
self.gds_read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from tech import power_grid
|
from tech import power_grid
|
||||||
self.pwr_grid_layer = power_grid[0]
|
self.pwr_grid_layer = power_grid[0]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
self.pwr_grid_layer = "m3"
|
self.pwr_grid_layer = "m3"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# GDS layout
|
# GDS layout
|
||||||
|
|
@ -244,25 +244,27 @@ class layout():
|
||||||
height))
|
height))
|
||||||
return self.objs[-1]
|
return self.objs[-1]
|
||||||
|
|
||||||
def add_segment_center(self, layer, start, end):
|
def add_segment_center(self, layer, start, end, width=None):
|
||||||
"""
|
"""
|
||||||
Add a min-width rectanglular segment using center
|
Add a min-width rectanglular segment using center
|
||||||
line on the start to end point
|
line on the start to end point
|
||||||
"""
|
"""
|
||||||
minwidth_layer = drc["minwidth_{}".format(layer)]
|
if not width:
|
||||||
|
width = drc["minwidth_{}".format(layer)]
|
||||||
|
|
||||||
if start.x != end.x and start.y != end.y:
|
if start.x != end.x and start.y != end.y:
|
||||||
debug.error("Nonrectilinear center rect!", -1)
|
debug.error("Nonrectilinear center rect!", -1)
|
||||||
elif start.x != end.x:
|
elif start.x != end.x:
|
||||||
offset = vector(0, 0.5 * minwidth_layer)
|
offset = vector(0, 0.5 * width)
|
||||||
return self.add_rect(layer,
|
return self.add_rect(layer,
|
||||||
start - offset,
|
start - offset,
|
||||||
end.x - start.x,
|
end.x - start.x,
|
||||||
minwidth_layer)
|
width)
|
||||||
else:
|
else:
|
||||||
offset = vector(0.5 * minwidth_layer, 0)
|
offset = vector(0.5 * width, 0)
|
||||||
return self.add_rect(layer,
|
return self.add_rect(layer,
|
||||||
start - offset,
|
start - offset,
|
||||||
minwidth_layer,
|
width,
|
||||||
end.y - start.y)
|
end.y - start.y)
|
||||||
|
|
||||||
def get_pin(self, text):
|
def get_pin(self, text):
|
||||||
|
|
@ -322,7 +324,7 @@ class layout():
|
||||||
for pin_name in self.pin_map.keys():
|
for pin_name in self.pin_map.keys():
|
||||||
self.copy_layout_pin(instance, pin_name, prefix + pin_name)
|
self.copy_layout_pin(instance, pin_name, prefix + pin_name)
|
||||||
|
|
||||||
def add_layout_pin_segment_center(self, text, layer, start, end):
|
def add_layout_pin_segment_center(self, text, layer, start, end, width=None):
|
||||||
"""
|
"""
|
||||||
Creates a path like pin with center-line convention
|
Creates a path like pin with center-line convention
|
||||||
"""
|
"""
|
||||||
|
|
@ -331,27 +333,27 @@ class layout():
|
||||||
self.gds_write(file_name)
|
self.gds_write(file_name)
|
||||||
debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1)
|
debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1)
|
||||||
|
|
||||||
minwidth_layer = drc["minwidth_{}".format(layer)]
|
if not width:
|
||||||
|
layer_width = drc["minwidth_{}".format(layer)]
|
||||||
|
else:
|
||||||
|
layer_width = width
|
||||||
|
|
||||||
# one of these will be zero
|
# one of these will be zero
|
||||||
width = max(start.x, end.x) - min(start.x, end.x)
|
bbox_width = max(start.x, end.x) - min(start.x, end.x)
|
||||||
height = max(start.y, end.y) - min(start.y, end.y)
|
bbox_height = max(start.y, end.y) - min(start.y, end.y)
|
||||||
ll_offset = vector(min(start.x, end.x), min(start.y, end.y))
|
ll_offset = vector(min(start.x, end.x), min(start.y, end.y))
|
||||||
|
|
||||||
# Shift it down 1/2 a width in the 0 dimension
|
# Shift it down 1/2 a width in the 0 dimension
|
||||||
if height == 0:
|
if bbox_height == 0:
|
||||||
ll_offset -= vector(0, 0.5 * minwidth_layer)
|
ll_offset -= vector(0, 0.5 * layer_width)
|
||||||
if width == 0:
|
if bbox_width == 0:
|
||||||
ll_offset -= vector(0.5 * minwidth_layer, 0)
|
ll_offset -= vector(0.5 * layer_width, 0)
|
||||||
# This makes sure it is long enough, but also it is not 0 width!
|
|
||||||
height = max(minwidth_layer, height)
|
|
||||||
width = max(minwidth_layer, width)
|
|
||||||
|
|
||||||
return self.add_layout_pin(text,
|
return self.add_layout_pin(text=text,
|
||||||
layer,
|
layer=layer,
|
||||||
ll_offset,
|
offset=ll_offset,
|
||||||
width,
|
width=bbox_width,
|
||||||
height)
|
height=bbox_height)
|
||||||
|
|
||||||
def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None):
|
def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None):
|
||||||
""" Creates a path like pin with center-line convention """
|
""" Creates a path like pin with center-line convention """
|
||||||
|
|
@ -448,24 +450,31 @@ class layout():
|
||||||
path=coordinates,
|
path=coordinates,
|
||||||
layer_widths=layer_widths)
|
layer_widths=layer_widths)
|
||||||
|
|
||||||
def add_zjog(self, layer, start, end, first_direction="H"):
|
def add_zjog(self, layer, start, end, first_direction="H", var_offset=0.5, fixed_offset=None):
|
||||||
"""
|
"""
|
||||||
Add a simple jog at the halfway point.
|
Add a simple jog at the halfway point.
|
||||||
If layer is a single value, it is a path.
|
If layer is a single value, it is a path.
|
||||||
If layer is a tuple, it is a wire with preferred directions.
|
If layer is a tuple, it is a wire with preferred directions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
neg_offset = 1.0 - var_offset
|
||||||
# vertical first
|
# vertical first
|
||||||
if first_direction == "V":
|
if first_direction == "V":
|
||||||
mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y)
|
if fixed_offset:
|
||||||
|
mid1 = vector(start.x, fixed_offset)
|
||||||
|
else:
|
||||||
|
mid1 = vector(start.x, neg_offset * start.y + var_offset * end.y)
|
||||||
mid2 = vector(end.x, mid1.y)
|
mid2 = vector(end.x, mid1.y)
|
||||||
# horizontal first
|
# horizontal first
|
||||||
elif first_direction == "H":
|
elif first_direction == "H":
|
||||||
mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y)
|
if fixed_offset:
|
||||||
|
mid1 = vector(fixed_offset, start.y)
|
||||||
|
else:
|
||||||
|
mid1 = vector(neg_offset * start.x + var_offset * end.x, start.y)
|
||||||
mid2 = vector(mid1, end.y)
|
mid2 = vector(mid1, end.y)
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid direction for jog -- must be H or V.")
|
debug.error("Invalid direction for jog -- must be H or V.")
|
||||||
|
|
||||||
if layer in layer_stacks:
|
if layer in layer_stacks:
|
||||||
self.add_wire(layer, [start, mid1, mid2, end])
|
self.add_wire(layer, [start, mid1, mid2, end])
|
||||||
elif layer in techlayer:
|
elif layer in techlayer:
|
||||||
|
|
@ -480,7 +489,7 @@ class layout():
|
||||||
mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y)
|
mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y)
|
||||||
mid2 = vector(mid1, end.y)
|
mid2 = vector(mid1, end.y)
|
||||||
self.add_path(layer, [start, mid1, mid2, end])
|
self.add_path(layer, [start, mid1, mid2, end])
|
||||||
|
|
||||||
def add_wire(self, layers, coordinates, widen_short_wires=True):
|
def add_wire(self, layers, coordinates, widen_short_wires=True):
|
||||||
"""Connects a routing path on given layer,coordinates,width.
|
"""Connects a routing path on given layer,coordinates,width.
|
||||||
The layers are the (horizontal, via, vertical). """
|
The layers are the (horizontal, via, vertical). """
|
||||||
|
|
@ -620,7 +629,7 @@ class layout():
|
||||||
last_via=via,
|
last_via=via,
|
||||||
size=size)
|
size=size)
|
||||||
return via
|
return via
|
||||||
|
|
||||||
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
|
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
|
||||||
"""Adds a ptx module to the design."""
|
"""Adds a ptx module to the design."""
|
||||||
import ptx
|
import ptx
|
||||||
|
|
@ -692,6 +701,8 @@ class layout():
|
||||||
boundary_layer = "stdc"
|
boundary_layer = "stdc"
|
||||||
boundary = [self.find_lowest_coords(),
|
boundary = [self.find_lowest_coords(),
|
||||||
self.find_highest_coords()]
|
self.find_highest_coords()]
|
||||||
|
debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.")
|
||||||
|
|
||||||
height = boundary[1][1] - boundary[0][1]
|
height = boundary[1][1] - boundary[0][1]
|
||||||
width = boundary[1][0] - boundary[0][0]
|
width = boundary[1][0] - boundary[0][0]
|
||||||
(layer_number, layer_purpose) = techlayer[boundary_layer]
|
(layer_number, layer_purpose) = techlayer[boundary_layer]
|
||||||
|
|
@ -973,7 +984,7 @@ class layout():
|
||||||
self.add_via_stack_center(from_layer=vlayer,
|
self.add_via_stack_center(from_layer=vlayer,
|
||||||
to_layer=dest_pin.layer,
|
to_layer=dest_pin.layer,
|
||||||
offset=out_pos)
|
offset=out_pos)
|
||||||
|
|
||||||
def get_layer_pitch(self, layer):
|
def get_layer_pitch(self, layer):
|
||||||
""" Return the track pitch on a given layer """
|
""" Return the track pitch on a given layer """
|
||||||
try:
|
try:
|
||||||
|
|
@ -985,7 +996,7 @@ class layout():
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
debug.error("Cannot find layer pitch.", -1)
|
debug.error("Cannot find layer pitch.", -1)
|
||||||
return (nonpref_pitch, pitch, pitch - space, space)
|
return (nonpref_pitch, pitch, pitch - space, space)
|
||||||
|
|
||||||
def add_horizontal_trunk_route(self,
|
def add_horizontal_trunk_route(self,
|
||||||
pins,
|
pins,
|
||||||
trunk_offset,
|
trunk_offset,
|
||||||
|
|
@ -1103,7 +1114,7 @@ class layout():
|
||||||
pitch = self.horizontal_nonpref_pitch
|
pitch = self.horizontal_nonpref_pitch
|
||||||
else:
|
else:
|
||||||
pitch = self.vertical_nonpref_pitch
|
pitch = self.vertical_nonpref_pitch
|
||||||
|
|
||||||
for pin1 in net1:
|
for pin1 in net1:
|
||||||
for pin2 in net2:
|
for pin2 in net2:
|
||||||
if vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
if vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
||||||
|
|
@ -1113,7 +1124,7 @@ class layout():
|
||||||
|
|
||||||
def vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
def vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
||||||
""" Check for vertical or horizontal overlap of the two pins """
|
""" Check for vertical or horizontal overlap of the two pins """
|
||||||
|
|
||||||
# FIXME: If the pins are not in a row, this may break.
|
# FIXME: If the pins are not in a row, this may break.
|
||||||
# However, a top pin shouldn't overlap another top pin,
|
# However, a top pin shouldn't overlap another top pin,
|
||||||
# for example, so the extra comparison *shouldn't* matter.
|
# for example, so the extra comparison *shouldn't* matter.
|
||||||
|
|
@ -1147,7 +1158,7 @@ class layout():
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.vertical_layer)
|
layer_stuff = self.get_layer_pitch(self.vertical_layer)
|
||||||
(self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
|
(self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
|
||||||
|
|
||||||
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
|
||||||
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
|
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
|
||||||
|
|
||||||
|
|
@ -1172,7 +1183,7 @@ class layout():
|
||||||
# print("Nets:")
|
# print("Nets:")
|
||||||
# for net_name in nets:
|
# for net_name in nets:
|
||||||
# print(net_name, [x.name for x in nets[net_name]])
|
# print(net_name, [x.name for x in nets[net_name]])
|
||||||
|
|
||||||
# Find the vertical pin conflicts
|
# Find the vertical pin conflicts
|
||||||
# FIXME: O(n^2) but who cares for now
|
# FIXME: O(n^2) but who cares for now
|
||||||
for net_name1 in nets:
|
for net_name1 in nets:
|
||||||
|
|
@ -1306,16 +1317,16 @@ class layout():
|
||||||
which vias are needed.
|
which vias are needed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
via = self.add_via_stack_center(from_layer=start_layer,
|
|
||||||
to_layer=self.pwr_grid_layer,
|
|
||||||
size=size,
|
|
||||||
offset=loc,
|
|
||||||
directions=directions)
|
|
||||||
if start_layer == self.pwr_grid_layer:
|
if start_layer == self.pwr_grid_layer:
|
||||||
self.add_layout_pin_rect_center(text=name,
|
self.add_layout_pin_rect_center(text=name,
|
||||||
layer=self.pwr_grid_layer,
|
layer=self.pwr_grid_layer,
|
||||||
offset=loc)
|
offset=loc)
|
||||||
else:
|
else:
|
||||||
|
via = self.add_via_stack_center(from_layer=start_layer,
|
||||||
|
to_layer=self.pwr_grid_layer,
|
||||||
|
size=size,
|
||||||
|
offset=loc,
|
||||||
|
directions=directions)
|
||||||
# Hack for min area
|
# Hack for min area
|
||||||
if OPTS.tech_name == "s8":
|
if OPTS.tech_name == "s8":
|
||||||
width = round_to_grid(sqrt(drc["minarea_m3"]))
|
width = round_to_grid(sqrt(drc["minarea_m3"]))
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,7 @@ class spice():
|
||||||
# parses line into ports and remove subckt
|
# parses line into ports and remove subckt
|
||||||
self.pins = subckt_line.split(" ")[2:]
|
self.pins = subckt_line.split(" ")[2:]
|
||||||
else:
|
else:
|
||||||
|
debug.info(4, "no spfile {0}".format(self.sp_file))
|
||||||
self.spice = []
|
self.spice = []
|
||||||
|
|
||||||
# We don't define self.lvs and will use self.spice if dynamically created
|
# We don't define self.lvs and will use self.spice if dynamically created
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import utils
|
import utils
|
||||||
from tech import GDS, layer, parameter
|
from tech import GDS, layer
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
import bitcell_base
|
import bitcell_base
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 debug
|
||||||
|
import utils
|
||||||
|
from tech import GDS, layer
|
||||||
|
from tech import cell_properties as props
|
||||||
|
import bitcell_base
|
||||||
|
|
||||||
|
|
||||||
|
class col_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
|
"""
|
||||||
|
todo"""
|
||||||
|
|
||||||
|
pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
|
||||||
|
props.bitcell.cell_1rw1r.pin.br0,
|
||||||
|
props.bitcell.cell_1rw1r.pin.bl1,
|
||||||
|
props.bitcell.cell_1rw1r.pin.br1,
|
||||||
|
props.bitcell.cell_1rw1r.pin.vdd]
|
||||||
|
|
||||||
|
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
|
||||||
|
"POWER", "GROUND"]
|
||||||
|
|
||||||
|
(width, height) = utils.get_libcell_size("col_cap_cell_1rw_1r",
|
||||||
|
GDS["unit"],
|
||||||
|
layer["boundary"])
|
||||||
|
pin_map = utils.get_libcell_pins(pin_names,
|
||||||
|
"col_cap_cell_1rw_1r",
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
def __init__(self, name=""):
|
||||||
|
# Ignore the name argument
|
||||||
|
bitcell_base.bitcell_base.__init__(self, "col_cap_cell_1rw_1r")
|
||||||
|
debug.info(2, "Create col_cap bitcell 1rw+1r object")
|
||||||
|
|
||||||
|
self.width = col_cap_bitcell_1rw_1r.width
|
||||||
|
self.height = col_cap_bitcell_1rw_1r.height
|
||||||
|
self.pin_map = col_cap_bitcell_1rw_1r.pin_map
|
||||||
|
self.add_pin_types(self.type_list)
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 debug
|
||||||
|
import utils
|
||||||
|
from tech import GDS, layer
|
||||||
|
from tech import cell_properties as props
|
||||||
|
import bitcell_base
|
||||||
|
|
||||||
|
|
||||||
|
class row_cap_bitcell_1rw_1r(bitcell_base.bitcell_base):
|
||||||
|
"""
|
||||||
|
A single bit cell which is forced to store a 0.
|
||||||
|
This module implements the single memory cell used in the design. It
|
||||||
|
is a hand-made cell, so the layout and netlist should be available in
|
||||||
|
the technology library. """
|
||||||
|
|
||||||
|
pin_names = [props.bitcell.cell_1rw1r.pin.wl0,
|
||||||
|
props.bitcell.cell_1rw1r.pin.wl1,
|
||||||
|
props.bitcell.cell_1rw1r.pin.gnd]
|
||||||
|
|
||||||
|
type_list = ["INPUT", "INPUT", "GROUND"]
|
||||||
|
|
||||||
|
(width, height) = utils.get_libcell_size("row_cap_cell_1rw_1r",
|
||||||
|
GDS["unit"],
|
||||||
|
layer["boundary"])
|
||||||
|
pin_map = utils.get_libcell_pins(pin_names,
|
||||||
|
"row_cap_cell_1rw_1r",
|
||||||
|
GDS["unit"])
|
||||||
|
|
||||||
|
def __init__(self, name=""):
|
||||||
|
# Ignore the name argument
|
||||||
|
bitcell_base.bitcell_base.__init__(self, "row_cap_cell_1rw_1r")
|
||||||
|
debug.info(2, "Create row_cap bitcell 1rw+1r object")
|
||||||
|
|
||||||
|
self.width = row_cap_bitcell_1rw_1r.width
|
||||||
|
self.height = row_cap_bitcell_1rw_1r.height
|
||||||
|
self.pin_map = row_cap_bitcell_1rw_1r.pin_map
|
||||||
|
self.add_pin_types(self.type_list)
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 debug
|
||||||
|
from vector import vector
|
||||||
|
import design
|
||||||
|
from sram_factory import factory
|
||||||
|
from globals import OPTS
|
||||||
|
from tech import layer
|
||||||
|
|
||||||
|
|
||||||
|
class and2_dec(design.design):
|
||||||
|
"""
|
||||||
|
This is an AND with configurable drive strength.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||||
|
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
debug.info(1, "Creating and2_dec {}".format(name))
|
||||||
|
self.add_comment("size: {}".format(size))
|
||||||
|
self.size = size
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
self.create_layout()
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
self.add_pins()
|
||||||
|
self.create_modules()
|
||||||
|
self.create_insts()
|
||||||
|
|
||||||
|
def create_modules(self):
|
||||||
|
self.nand = factory.create(module_type="nand2_dec",
|
||||||
|
height=self.height)
|
||||||
|
|
||||||
|
self.inv = factory.create(module_type="inv_dec",
|
||||||
|
height=self.height,
|
||||||
|
size=self.size)
|
||||||
|
|
||||||
|
self.add_mod(self.nand)
|
||||||
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
|
||||||
|
if "li" in layer:
|
||||||
|
self.route_layer = "li"
|
||||||
|
else:
|
||||||
|
self.route_layer = "m1"
|
||||||
|
self.width = self.nand.width + self.inv.width
|
||||||
|
self.height = self.nand.height
|
||||||
|
|
||||||
|
self.place_insts()
|
||||||
|
self.add_wires()
|
||||||
|
self.add_layout_pins()
|
||||||
|
self.route_supply_rails()
|
||||||
|
self.add_boundary()
|
||||||
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
def add_pins(self):
|
||||||
|
self.add_pin("A", "INPUT")
|
||||||
|
self.add_pin("B", "INPUT")
|
||||||
|
self.add_pin("Z", "OUTPUT")
|
||||||
|
self.add_pin("vdd", "POWER")
|
||||||
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
|
def create_insts(self):
|
||||||
|
self.nand_inst = self.add_inst(name="pand2_dec_nand",
|
||||||
|
mod=self.nand)
|
||||||
|
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
|
self.inv_inst = self.add_inst(name="pand2_dec_inv",
|
||||||
|
mod=self.inv)
|
||||||
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_insts(self):
|
||||||
|
# Add NAND to the right
|
||||||
|
self.nand_inst.place(offset=vector(0, 0))
|
||||||
|
|
||||||
|
# Add INV to the right
|
||||||
|
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||||
|
|
||||||
|
def route_supply_rails(self):
|
||||||
|
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
for name in ["vdd", "gnd"]:
|
||||||
|
for inst in [self.nand_inst, self.inv_inst]:
|
||||||
|
self.copy_layout_pin(inst, name)
|
||||||
|
else:
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, 0),
|
||||||
|
width=self.width)
|
||||||
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, self.height),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
def add_wires(self):
|
||||||
|
# nand Z to inv A
|
||||||
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
|
||||||
|
else:
|
||||||
|
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||||
|
self.add_path(self.route_layer,
|
||||||
|
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||||
|
|
||||||
|
def add_layout_pins(self):
|
||||||
|
pin = self.inv_inst.get_pin("Z")
|
||||||
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
for pin_name in ["A", "B"]:
|
||||||
|
pin = self.nand_inst.get_pin(pin_name)
|
||||||
|
self.add_layout_pin_rect_center(text=pin_name,
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
|
stage_effort_list = []
|
||||||
|
stage1_cout = self.inv.get_cin()
|
||||||
|
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||||
|
stage_effort_list.append(stage1)
|
||||||
|
last_stage_is_rise = stage1.is_rise
|
||||||
|
|
||||||
|
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||||
|
stage_effort_list.append(stage2)
|
||||||
|
|
||||||
|
return stage_effort_list
|
||||||
|
|
||||||
|
def get_cin(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
return self.nand.get_cin()
|
||||||
|
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 debug
|
||||||
|
from vector import vector
|
||||||
|
import design
|
||||||
|
from sram_factory import factory
|
||||||
|
from globals import OPTS
|
||||||
|
from tech import layer
|
||||||
|
|
||||||
|
|
||||||
|
class and3_dec(design.design):
|
||||||
|
"""
|
||||||
|
This is an AND with configurable drive strength.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
debug.info(1, "Creating and3_dec {}".format(name))
|
||||||
|
self.add_comment("size: {}".format(size))
|
||||||
|
self.size = size
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
self.create_layout()
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
self.add_pins()
|
||||||
|
self.create_modules()
|
||||||
|
self.create_insts()
|
||||||
|
|
||||||
|
def create_modules(self):
|
||||||
|
self.nand = factory.create(module_type="nand3_dec",
|
||||||
|
height=self.height)
|
||||||
|
|
||||||
|
self.inv = factory.create(module_type="inv_dec",
|
||||||
|
height=self.height,
|
||||||
|
size=self.size)
|
||||||
|
|
||||||
|
self.add_mod(self.nand)
|
||||||
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
if "li" in layer:
|
||||||
|
self.route_layer = "li"
|
||||||
|
else:
|
||||||
|
self.route_layer = "m1"
|
||||||
|
|
||||||
|
self.width = self.nand.width + self.inv.width
|
||||||
|
self.height = self.nand.height
|
||||||
|
|
||||||
|
self.place_insts()
|
||||||
|
self.add_wires()
|
||||||
|
self.add_layout_pins()
|
||||||
|
self.route_supply_rails()
|
||||||
|
self.add_boundary()
|
||||||
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
def add_pins(self):
|
||||||
|
self.add_pin("A", "INPUT")
|
||||||
|
self.add_pin("B", "INPUT")
|
||||||
|
self.add_pin("C", "INPUT")
|
||||||
|
self.add_pin("Z", "OUTPUT")
|
||||||
|
self.add_pin("vdd", "POWER")
|
||||||
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
|
def create_insts(self):
|
||||||
|
self.nand_inst = self.add_inst(name="pand3_dec_nand",
|
||||||
|
mod=self.nand)
|
||||||
|
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
|
self.inv_inst = self.add_inst(name="pand3_dec_inv",
|
||||||
|
mod=self.inv)
|
||||||
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_insts(self):
|
||||||
|
# Add NAND to the right
|
||||||
|
self.nand_inst.place(offset=vector(0, 0))
|
||||||
|
|
||||||
|
# Add INV to the right
|
||||||
|
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||||
|
|
||||||
|
def route_supply_rails(self):
|
||||||
|
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
for name in ["vdd", "gnd"]:
|
||||||
|
for inst in [self.nand_inst, self.inv_inst]:
|
||||||
|
self.copy_layout_pin(inst, name)
|
||||||
|
else:
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, 0),
|
||||||
|
width=self.width)
|
||||||
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, self.height),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
def add_wires(self):
|
||||||
|
# nand Z to inv A
|
||||||
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
|
||||||
|
else:
|
||||||
|
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||||
|
self.add_path(self.route_layer,
|
||||||
|
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||||
|
|
||||||
|
def add_layout_pins(self):
|
||||||
|
pin = self.inv_inst.get_pin("Z")
|
||||||
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
for pin_name in ["A", "B", "C"]:
|
||||||
|
pin = self.nand_inst.get_pin(pin_name)
|
||||||
|
self.add_layout_pin_rect_center(text=pin_name,
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
def analytical_delay(self, corner, slew, load=0.0):
|
||||||
|
""" Calculate the analytical delay of DFF-> INV -> INV """
|
||||||
|
nand_delay = self.nand.analytical_delay(corner,
|
||||||
|
slew=slew,
|
||||||
|
load=self.inv.input_load())
|
||||||
|
inv_delay = self.inv.analytical_delay(corner,
|
||||||
|
slew=nand_delay.slew,
|
||||||
|
load=load)
|
||||||
|
return nand_delay + inv_delay
|
||||||
|
|
||||||
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
|
stage_effort_list = []
|
||||||
|
stage1_cout = self.inv.get_cin()
|
||||||
|
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||||
|
stage_effort_list.append(stage1)
|
||||||
|
last_stage_is_rise = stage1.is_rise
|
||||||
|
|
||||||
|
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||||
|
stage_effort_list.append(stage2)
|
||||||
|
|
||||||
|
return stage_effort_list
|
||||||
|
|
||||||
|
def get_cin(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
return self.nand.get_cin()
|
||||||
|
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 debug
|
||||||
|
from vector import vector
|
||||||
|
import design
|
||||||
|
from sram_factory import factory
|
||||||
|
from globals import OPTS
|
||||||
|
from tech import layer
|
||||||
|
|
||||||
|
|
||||||
|
class and4_dec(design.design):
|
||||||
|
"""
|
||||||
|
This is an AND with configurable drive strength.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||||
|
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
debug.info(1, "Creating and4_dec {}".format(name))
|
||||||
|
self.add_comment("size: {}".format(size))
|
||||||
|
self.size = size
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
self.create_layout()
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
self.add_pins()
|
||||||
|
self.create_modules()
|
||||||
|
self.create_insts()
|
||||||
|
|
||||||
|
def create_modules(self):
|
||||||
|
self.nand = factory.create(module_type="nand4_dec",
|
||||||
|
height=self.height)
|
||||||
|
|
||||||
|
self.inv = factory.create(module_type="inv_dec",
|
||||||
|
height=self.height,
|
||||||
|
size=self.size)
|
||||||
|
|
||||||
|
self.add_mod(self.nand)
|
||||||
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
if "li" in layer:
|
||||||
|
self.route_layer = "li"
|
||||||
|
else:
|
||||||
|
self.route_layer = "m1"
|
||||||
|
|
||||||
|
self.width = self.nand.width + self.inv.width
|
||||||
|
self.height = self.nand.height
|
||||||
|
|
||||||
|
self.place_insts()
|
||||||
|
self.add_wires()
|
||||||
|
self.add_layout_pins()
|
||||||
|
self.route_supply_rails()
|
||||||
|
self.add_boundary()
|
||||||
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
def add_pins(self):
|
||||||
|
self.add_pin("A", "INPUT")
|
||||||
|
self.add_pin("B", "INPUT")
|
||||||
|
self.add_pin("C", "INPUT")
|
||||||
|
self.add_pin("D", "INPUT")
|
||||||
|
self.add_pin("Z", "OUTPUT")
|
||||||
|
self.add_pin("vdd", "POWER")
|
||||||
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
|
def create_insts(self):
|
||||||
|
self.nand_inst = self.add_inst(name="pand4_dec_nand",
|
||||||
|
mod=self.nand)
|
||||||
|
self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
|
self.inv_inst = self.add_inst(name="pand4_dec_inv",
|
||||||
|
mod=self.inv)
|
||||||
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_insts(self):
|
||||||
|
# Add NAND to the right
|
||||||
|
self.nand_inst.place(offset=vector(0, 0))
|
||||||
|
|
||||||
|
# Add INV to the right
|
||||||
|
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||||
|
|
||||||
|
def route_supply_rails(self):
|
||||||
|
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
for name in ["vdd", "gnd"]:
|
||||||
|
for inst in [self.nand_inst, self.inv_inst]:
|
||||||
|
self.copy_layout_pin(inst, name)
|
||||||
|
else:
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, 0),
|
||||||
|
width=self.width)
|
||||||
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, self.height),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
def add_wires(self):
|
||||||
|
# nand Z to inv A
|
||||||
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
|
||||||
|
else:
|
||||||
|
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||||
|
self.add_path(self.route_layer,
|
||||||
|
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||||
|
|
||||||
|
def add_layout_pins(self):
|
||||||
|
pin = self.inv_inst.get_pin("Z")
|
||||||
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
for pin_name in ["A", "B", "C"]:
|
||||||
|
pin = self.nand_inst.get_pin(pin_name)
|
||||||
|
self.add_layout_pin_rect_center(text=pin_name,
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
def analytical_delay(self, corner, slew, load=0.0):
|
||||||
|
""" Calculate the analytical delay of DFF-> INV -> INV """
|
||||||
|
nand_delay = self.nand.analytical_delay(corner,
|
||||||
|
slew=slew,
|
||||||
|
load=self.inv.input_load())
|
||||||
|
inv_delay = self.inv.analytical_delay(corner,
|
||||||
|
slew=nand_delay.slew,
|
||||||
|
load=load)
|
||||||
|
return nand_delay + inv_delay
|
||||||
|
|
||||||
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
|
stage_effort_list = []
|
||||||
|
stage1_cout = self.inv.get_cin()
|
||||||
|
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||||
|
stage_effort_list.append(stage1)
|
||||||
|
last_stage_is_rise = stage1.is_rise
|
||||||
|
|
||||||
|
stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise)
|
||||||
|
stage_effort_list.append(stage2)
|
||||||
|
|
||||||
|
return stage_effort_list
|
||||||
|
|
||||||
|
def get_cin(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
return self.nand.get_cin()
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ class dff(design.design):
|
||||||
#Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
|
#Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
|
||||||
return parameter["dff_clk_cin"]
|
return parameter["dff_clk_cin"]
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||||
self.add_graph_edges(graph, port_nets)
|
self.add_graph_edges(graph, port_nets)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 GDS, layer, spice, parameter
|
||||||
|
import logical_effort
|
||||||
|
import utils
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class inv_dec(design.design):
|
||||||
|
"""
|
||||||
|
INV for address decoders.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pin_names = ["A", "Z", "vdd", "gnd"]
|
||||||
|
type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
|
(width, height) = utils.get_libcell_size("inv_dec",
|
||||||
|
GDS["unit"],
|
||||||
|
layer["boundary"])
|
||||||
|
pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"])
|
||||||
|
|
||||||
|
def __init__(self, name="inv_dec", height=None):
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
self.width = inv_dec.width
|
||||||
|
self.height = inv_dec.height
|
||||||
|
self.pin_map = inv_dec.pin_map
|
||||||
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
|
def analytical_power(self, corner, load):
|
||||||
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
|
freq = spice["default_event_frequency"]
|
||||||
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
|
power_leak = spice["inv_leakage"]
|
||||||
|
|
||||||
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
|
return total_power
|
||||||
|
|
||||||
|
def calculate_effective_capacitance(self, load):
|
||||||
|
"""Computes effective capacitance. Results in fF"""
|
||||||
|
c_load = load
|
||||||
|
# In fF
|
||||||
|
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||||
|
|
||||||
|
return transition_prob * (c_load + c_para)
|
||||||
|
|
||||||
|
def input_load(self):
|
||||||
|
"""
|
||||||
|
Return the capacitance of the gate connection in generic capacitive
|
||||||
|
units relative to the minimum width of a transistor
|
||||||
|
"""
|
||||||
|
return self.nmos_size + self.pmos_size
|
||||||
|
|
||||||
|
def get_stage_effort(self, cout, inp_is_rise=True):
|
||||||
|
"""
|
||||||
|
Returns an object representing the parameters for delay in tau units.
|
||||||
|
Optional is_rise refers to the input direction rise/fall.
|
||||||
|
Input inverted by this stage.
|
||||||
|
"""
|
||||||
|
parasitic_delay = 1
|
||||||
|
return logical_effort.logical_effort(self.name,
|
||||||
|
self.size,
|
||||||
|
self.input_load(),
|
||||||
|
cout,
|
||||||
|
parasitic_delay,
|
||||||
|
not inp_is_rise)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 GDS, layer, spice, parameter, drc
|
||||||
|
import logical_effort
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
|
class nand2_dec(design.design):
|
||||||
|
"""
|
||||||
|
2-input NAND decoder for address decoders.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pin_names = ["A", "B", "Z", "vdd", "gnd"]
|
||||||
|
type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
|
(width, height) = utils.get_libcell_size("nand2_dec",
|
||||||
|
GDS["unit"],
|
||||||
|
layer["boundary"])
|
||||||
|
pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"])
|
||||||
|
|
||||||
|
def __init__(self, name="nand2_dec", height=None):
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
self.width = nand2_dec.width
|
||||||
|
self.height = nand2_dec.height
|
||||||
|
self.pin_map = nand2_dec.pin_map
|
||||||
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
|
# FIXME: For now...
|
||||||
|
size = 1
|
||||||
|
self.size = size
|
||||||
|
self.nmos_size = 2 * size
|
||||||
|
self.pmos_size = parameter["beta"] * size
|
||||||
|
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||||
|
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||||
|
|
||||||
|
def analytical_power(self, corner, load):
|
||||||
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
|
freq = spice["default_event_frequency"]
|
||||||
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
|
power_leak = spice["nand2_leakage"]
|
||||||
|
|
||||||
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
|
return total_power
|
||||||
|
|
||||||
|
def calculate_effective_capacitance(self, load):
|
||||||
|
"""Computes effective capacitance. Results in fF"""
|
||||||
|
c_load = load
|
||||||
|
# In fF
|
||||||
|
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||||
|
transition_prob = 0.1875
|
||||||
|
return transition_prob * (c_load + c_para)
|
||||||
|
|
||||||
|
def input_load(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
return self.nmos_size + self.pmos_size
|
||||||
|
|
||||||
|
def get_stage_effort(self, cout, inp_is_rise=True):
|
||||||
|
"""
|
||||||
|
Returns an object representing the parameters for delay in tau units.
|
||||||
|
Optional is_rise refers to the input direction rise/fall.
|
||||||
|
Input inverted by this stage.
|
||||||
|
"""
|
||||||
|
parasitic_delay = 2
|
||||||
|
return logical_effort.logical_effort(self.name,
|
||||||
|
self.size,
|
||||||
|
self.input_load(),
|
||||||
|
cout,
|
||||||
|
parasitic_delay,
|
||||||
|
not inp_is_rise)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 GDS, layer, spice, parameter, drc
|
||||||
|
import logical_effort
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
|
class nand3_dec(design.design):
|
||||||
|
"""
|
||||||
|
3-input NAND decoder for address decoders.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pin_names = ["A", "B", "C", "Z", "vdd", "gnd"]
|
||||||
|
type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
|
(width, height) = utils.get_libcell_size("nand3_dec",
|
||||||
|
GDS["unit"],
|
||||||
|
layer["boundary"])
|
||||||
|
pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"])
|
||||||
|
|
||||||
|
def __init__(self, name="nand3_dec", height=None):
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
self.width = nand3_dec.width
|
||||||
|
self.height = nand3_dec.height
|
||||||
|
self.pin_map = nand3_dec.pin_map
|
||||||
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
|
# FIXME: For now...
|
||||||
|
size = 1
|
||||||
|
self.size = size
|
||||||
|
self.nmos_size = 2 * size
|
||||||
|
self.pmos_size = parameter["beta"] * size
|
||||||
|
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||||
|
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||||
|
|
||||||
|
def analytical_power(self, corner, load):
|
||||||
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
|
freq = spice["default_event_frequency"]
|
||||||
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
|
power_leak = spice["nand3_leakage"]
|
||||||
|
|
||||||
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
|
return total_power
|
||||||
|
|
||||||
|
def calculate_effective_capacitance(self, load):
|
||||||
|
"""Computes effective capacitance. Results in fF"""
|
||||||
|
c_load = load
|
||||||
|
# In fF
|
||||||
|
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||||
|
transition_prob = 0.1875
|
||||||
|
return transition_prob * (c_load + c_para)
|
||||||
|
|
||||||
|
def input_load(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
return self.nmos_size + self.pmos_size
|
||||||
|
|
||||||
|
def get_stage_effort(self, cout, inp_is_rise=True):
|
||||||
|
"""
|
||||||
|
Returns an object representing the parameters for delay in tau units.
|
||||||
|
Optional is_rise refers to the input direction rise/fall.
|
||||||
|
Input inverted by this stage.
|
||||||
|
"""
|
||||||
|
parasitic_delay = 2
|
||||||
|
return logical_effort.logical_effort(self.name,
|
||||||
|
self.size,
|
||||||
|
self.input_load(),
|
||||||
|
cout,
|
||||||
|
parasitic_delay,
|
||||||
|
not inp_is_rise)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 GDS, layer, spice, parameter, drc
|
||||||
|
import logical_effort
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
|
class nand4_dec(design.design):
|
||||||
|
"""
|
||||||
|
2-input NAND decoder for address decoders.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"]
|
||||||
|
type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
|
||||||
|
|
||||||
|
(width, height) = utils.get_libcell_size("nand4_dec",
|
||||||
|
GDS["unit"],
|
||||||
|
layer["boundary"])
|
||||||
|
pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"])
|
||||||
|
|
||||||
|
def __init__(self, name="nand4_dec", height=None):
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
self.width = nand4_dec.width
|
||||||
|
self.height = nand4_dec.height
|
||||||
|
self.pin_map = nand4_dec.pin_map
|
||||||
|
self.add_pin_types(self.type_list)
|
||||||
|
|
||||||
|
# FIXME: For now...
|
||||||
|
size = 1
|
||||||
|
self.size = size
|
||||||
|
self.nmos_size = 2 * size
|
||||||
|
self.pmos_size = parameter["beta"] * size
|
||||||
|
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||||
|
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||||
|
|
||||||
|
def analytical_power(self, corner, load):
|
||||||
|
"""Returns dynamic and leakage power. Results in nW"""
|
||||||
|
c_eff = self.calculate_effective_capacitance(load)
|
||||||
|
freq = spice["default_event_frequency"]
|
||||||
|
power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
|
||||||
|
power_leak = spice["nand4_leakage"]
|
||||||
|
|
||||||
|
total_power = self.return_power(power_dyn, power_leak)
|
||||||
|
return total_power
|
||||||
|
|
||||||
|
def calculate_effective_capacitance(self, load):
|
||||||
|
"""Computes effective capacitance. Results in fF"""
|
||||||
|
c_load = load
|
||||||
|
# In fF
|
||||||
|
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||||
|
transition_prob = 0.1875
|
||||||
|
return transition_prob * (c_load + c_para)
|
||||||
|
|
||||||
|
def input_load(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
return self.nmos_size + self.pmos_size
|
||||||
|
|
||||||
|
def get_stage_effort(self, cout, inp_is_rise=True):
|
||||||
|
"""
|
||||||
|
Returns an object representing the parameters for delay in tau units.
|
||||||
|
Optional is_rise refers to the input direction rise/fall.
|
||||||
|
Input inverted by this stage.
|
||||||
|
"""
|
||||||
|
parasitic_delay = 2
|
||||||
|
return logical_effort.logical_effort(self.name,
|
||||||
|
self.size,
|
||||||
|
self.input_load(),
|
||||||
|
cout,
|
||||||
|
parasitic_delay,
|
||||||
|
not inp_is_rise)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
@ -214,25 +214,18 @@ def setup_bitcell():
|
||||||
if OPTS.num_r_ports > 0:
|
if OPTS.num_r_ports > 0:
|
||||||
ports += "{}r".format(OPTS.num_r_ports)
|
ports += "{}r".format(OPTS.num_r_ports)
|
||||||
|
|
||||||
OPTS.bitcell = "bitcell_"+ports
|
if ports != "":
|
||||||
OPTS.replica_bitcell = "replica_bitcell_"+ports
|
OPTS.bitcell_suffix = "_" + ports
|
||||||
OPTS.dummy_bitcell = "dummy_bitcell_"+ports
|
OPTS.bitcell = "bitcell" + OPTS.bitcell_suffix
|
||||||
else:
|
|
||||||
OPTS.replica_bitcell = "replica_" + OPTS.bitcell
|
|
||||||
OPTS.replica_bitcell = "dummy_" + OPTS.bitcell
|
|
||||||
|
|
||||||
# See if bitcell exists
|
# See if bitcell exists
|
||||||
try:
|
try:
|
||||||
__import__(OPTS.bitcell)
|
__import__(OPTS.bitcell)
|
||||||
__import__(OPTS.replica_bitcell)
|
|
||||||
__import__(OPTS.dummy_bitcell)
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Use the pbitcell if we couldn't find a custom bitcell
|
# Use the pbitcell if we couldn't find a custom bitcell
|
||||||
# or its custom replica bitcell
|
# or its custom replica bitcell
|
||||||
# Use the pbitcell (and give a warning if not in unit test mode)
|
# Use the pbitcell (and give a warning if not in unit test mode)
|
||||||
OPTS.bitcell = "pbitcell"
|
OPTS.bitcell = "pbitcell"
|
||||||
OPTS.replica_bitcell = "replica_pbitcell"
|
|
||||||
OPTS.replica_bitcell = "dummy_pbitcell"
|
|
||||||
if not OPTS.is_unit_test:
|
if not OPTS.is_unit_test:
|
||||||
debug.warning("Using the parameterized bitcell which may have suboptimal density.")
|
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))
|
||||||
|
|
|
||||||
|
|
@ -128,23 +128,22 @@ class bank(design.design):
|
||||||
|
|
||||||
def route_rbl(self, port):
|
def route_rbl(self, port):
|
||||||
""" Route the rbl_bl and rbl_wl """
|
""" Route the rbl_bl and rbl_wl """
|
||||||
|
|
||||||
bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
# Connect the rbl to the port data pin
|
||||||
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
|
bl_pin = self.port_data_inst[port].get_pin("rbl_bl")
|
||||||
# This will ensure the pin is only on the top or bottom edge
|
|
||||||
if port % 2:
|
if port % 2:
|
||||||
via_offset = bl_pin.uc() + vector(0, 1.5 * self.m2_pitch)
|
pin_offset = bl_pin.uc()
|
||||||
left_right_offset = vector(self.max_x_offset, via_offset.y)
|
left_right_offset = vector(self.max_x_offset, pin_offset.y)
|
||||||
else:
|
else:
|
||||||
via_offset = bl_pin.bc() - vector(0, 1.5 * self.m2_pitch)
|
pin_offset = bl_pin.bc()
|
||||||
left_right_offset = vector(self.min_x_offset, via_offset.y)
|
left_right_offset = vector(self.min_x_offset, pin_offset.y)
|
||||||
self.add_via_stack_center(from_layer=bl_pin.layer,
|
self.add_via_stack_center(from_layer=bl_pin.layer,
|
||||||
to_layer="m3",
|
to_layer="m3",
|
||||||
offset=via_offset)
|
offset=pin_offset)
|
||||||
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
|
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
|
||||||
layer="m3",
|
layer="m3",
|
||||||
start=left_right_offset,
|
start=left_right_offset,
|
||||||
end=via_offset)
|
end=pin_offset)
|
||||||
|
|
||||||
def route_bitlines(self, port):
|
def route_bitlines(self, port):
|
||||||
""" Route the bitlines depending on the port type rw, w, or r. """
|
""" Route the bitlines depending on the port type rw, w, or r. """
|
||||||
|
|
@ -817,22 +816,34 @@ class bank(design.design):
|
||||||
|
|
||||||
for row in range(self.num_rows):
|
for row in range(self.num_rows):
|
||||||
# The mid guarantees we exit the input cell to the right.
|
# The mid guarantees we exit the input cell to the right.
|
||||||
driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).rc()
|
driver_wl_pin = self.port_address_inst[port].get_pin("wl_{}".format(row))
|
||||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).lc()
|
driver_wl_pos = driver_wl_pin.rc()
|
||||||
|
bitcell_wl_pin = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row))
|
||||||
|
bitcell_wl_pos = bitcell_wl_pin.lc()
|
||||||
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0)
|
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0)
|
||||||
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1)
|
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1)
|
||||||
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||||
|
self.add_via_stack_center(from_layer=driver_wl_pin.layer,
|
||||||
|
to_layer=bitcell_wl_pin.layer,
|
||||||
|
offset=bitcell_wl_pos,
|
||||||
|
directions=("H", "H"))
|
||||||
|
|
||||||
def route_port_address_right(self, port):
|
def route_port_address_right(self, port):
|
||||||
""" Connecting Wordline driver output to Bitcell WL connection """
|
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||||
|
|
||||||
for row in range(self.num_rows):
|
for row in range(self.num_rows):
|
||||||
# The mid guarantees we exit the input cell to the right.
|
# The mid guarantees we exit the input cell to the right.
|
||||||
driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).lc()
|
driver_wl_pin = self.port_address_inst[port].get_pin("wl_{}".format(row))
|
||||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).rc()
|
driver_wl_pos = driver_wl_pin.lc()
|
||||||
|
bitcell_wl_pin = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row))
|
||||||
|
bitcell_wl_pos = bitcell_wl_pin.rc()
|
||||||
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0)
|
mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0)
|
||||||
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
|
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
|
||||||
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
self.add_path(driver_wl_pin.layer, [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||||
|
self.add_via_stack_center(from_layer=driver_wl_pin.layer,
|
||||||
|
to_layer=bitcell_wl_pin.layer,
|
||||||
|
offset=bitcell_wl_pos,
|
||||||
|
directions=("H", "H"))
|
||||||
|
|
||||||
def route_column_address_lines(self, port):
|
def route_column_address_lines(self, port):
|
||||||
""" Connecting the select lines of column mux to the address bus """
|
""" Connecting the select lines of column mux to the address bus """
|
||||||
|
|
|
||||||
|
|
@ -108,16 +108,23 @@ class bitcell_base_array(design.design):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
bitcell_power_pin_directions = None
|
bitcell_power_pin_directions = None
|
||||||
|
|
||||||
|
# For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps.
|
||||||
|
try:
|
||||||
|
bitcell_no_vdd_pin = cell_properties.bitcell.no_vdd_via
|
||||||
|
except AttributeError:
|
||||||
|
bitcell_no_vdd_pin = False
|
||||||
|
|
||||||
# Add vdd/gnd via stacks
|
# Add vdd/gnd via stacks
|
||||||
for row in range(self.row_size):
|
for row in range(self.row_size):
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
inst = self.cell_inst[row,col]
|
inst = self.cell_inst[row,col]
|
||||||
for pin_name in ["vdd", "gnd"]:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
for pin in inst.get_pins(pin_name):
|
for pin in inst.get_pins(pin_name):
|
||||||
self.add_power_pin(name=pin_name,
|
if not (pin_name == "vdd" and bitcell_no_vdd_pin):
|
||||||
loc=pin.center(),
|
self.add_power_pin(name=pin_name,
|
||||||
directions=bitcell_power_pin_directions,
|
loc=pin.center(),
|
||||||
start_layer=pin.layer)
|
directions=bitcell_power_pin_directions,
|
||||||
|
start_layer=pin.layer)
|
||||||
|
|
||||||
def _adjust_x_offset(self, xoffset, col, col_offset):
|
def _adjust_x_offset(self, xoffset, col, col_offset):
|
||||||
tempx = xoffset
|
tempx = xoffset
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 Regents of the University of California
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
from bitcell_base_array import bitcell_base_array
|
||||||
|
from sram_factory import factory
|
||||||
|
from globals import OPTS
|
||||||
|
from tech import cell_properties
|
||||||
|
|
||||||
|
|
||||||
|
class col_cap_array(bitcell_base_array):
|
||||||
|
"""
|
||||||
|
Generate a dummy row/column for the replica array.
|
||||||
|
"""
|
||||||
|
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""):
|
||||||
|
super().__init__(cols, rows, name, column_offset)
|
||||||
|
self.mirror = mirror
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
self.create_layout()
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
""" Create and connect the netlist """
|
||||||
|
self.add_modules()
|
||||||
|
self.add_pins()
|
||||||
|
self.create_instances()
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
|
||||||
|
self.place_array("dummy_r{0}_c{1}", self.mirror)
|
||||||
|
self.add_layout_pins()
|
||||||
|
self.add_boundary()
|
||||||
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
def add_modules(self):
|
||||||
|
""" Add the modules used in this design """
|
||||||
|
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
||||||
|
self.add_mod(self.dummy_cell)
|
||||||
|
|
||||||
|
self.cell = factory.create(module_type="bitcell")
|
||||||
|
|
||||||
|
def create_instances(self):
|
||||||
|
""" Create the module instances used in this design """
|
||||||
|
self.cell_inst = {}
|
||||||
|
for col in range(self.column_size):
|
||||||
|
for row in range(self.row_size):
|
||||||
|
name = "bit_r{0}_c{1}".format(row, col)
|
||||||
|
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||||
|
mod=self.dummy_cell)
|
||||||
|
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||||
|
|
||||||
|
def get_bitcell_pins(self, col, row):
|
||||||
|
"""
|
||||||
|
Creates a list of connections in the bitcell,
|
||||||
|
indexed by column and row, for instance use in bitcell_array
|
||||||
|
"""
|
||||||
|
|
||||||
|
pin_name = cell_properties.bitcell.cell_1rw1r.pin
|
||||||
|
bitcell_pins = ["{0}_{1}".format(pin_name.bl0, col),
|
||||||
|
"{0}_{1}".format(pin_name.br0, col),
|
||||||
|
"{0}_{1}".format(pin_name.bl1, col),
|
||||||
|
"{0}_{1}".format(pin_name.br1, col),
|
||||||
|
"vdd"]
|
||||||
|
|
||||||
|
return bitcell_pins
|
||||||
|
|
||||||
|
def add_layout_pins(self):
|
||||||
|
""" Add the layout pins """
|
||||||
|
|
||||||
|
column_list = self.cell.get_all_bitline_names()
|
||||||
|
|
||||||
|
for col in range(self.column_size):
|
||||||
|
for cell_column in column_list:
|
||||||
|
bl_pin = self.cell_inst[0,col].get_pin(cell_column)
|
||||||
|
self.add_layout_pin(text=cell_column+"_{0}".format(col),
|
||||||
|
layer=bl_pin.layer,
|
||||||
|
offset=bl_pin.ll().scale(1,0),
|
||||||
|
width=bl_pin.width(),
|
||||||
|
height=self.height)
|
||||||
|
|
||||||
|
# Add vdd/gnd via stacks
|
||||||
|
for row in range(self.row_size):
|
||||||
|
for col in range(self.column_size):
|
||||||
|
inst = self.cell_inst[row,col]
|
||||||
|
for pin_name in ["vdd", "gnd"]:
|
||||||
|
for pin in inst.get_pins(pin_name):
|
||||||
|
self.add_power_pin(name=pin.name,
|
||||||
|
loc=pin.center(),
|
||||||
|
start_layer=pin.layer)
|
||||||
|
|
||||||
|
|
||||||
|
# def input_load(self):
|
||||||
|
# wl_wire = self.gen_wl_wire()
|
||||||
|
# return wl_wire.return_input_cap()
|
||||||
|
#
|
||||||
|
# def get_wordline_cin(self):
|
||||||
|
# """Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||||
|
# #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||||
|
# bitcell_wl_cin = self.cell.get_wl_cin()
|
||||||
|
# total_cin = bitcell_wl_cin * self.column_size
|
||||||
|
# return total_cin
|
||||||
|
|
@ -38,20 +38,19 @@ class dummy_array(bitcell_base_array):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" Add the modules used in this design """
|
||||||
self.dummy_cell = factory.create(module_type="dummy_bitcell")
|
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
|
||||||
self.add_mod(self.dummy_cell)
|
self.add_mod(self.dummy_cell)
|
||||||
|
|
||||||
self.cell = factory.create(module_type="bitcell")
|
self.cell = factory.create(module_type="bitcell")
|
||||||
|
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
self.cell_inst = {}
|
self.cell_inst = {}
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
for row in range(self.row_size):
|
for row in range(self.row_size):
|
||||||
name = "bit_r{0}_c{1}".format(row, col)
|
name = "bit_r{0}_c{1}".format(row, col)
|
||||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||||
mod=self.dummy_cell)
|
mod=self.dummy_cell)
|
||||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
|
|
@ -60,7 +59,7 @@ class dummy_array(bitcell_base_array):
|
||||||
|
|
||||||
def get_wordline_cin(self):
|
def get_wordline_cin(self):
|
||||||
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||||
#A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||||
bitcell_wl_cin = self.cell.get_wl_cin()
|
bitcell_wl_cin = self.cell.get_wl_cin()
|
||||||
total_cin = bitcell_wl_cin * self.column_size
|
total_cin = bitcell_wl_cin * self.column_size
|
||||||
return total_cin
|
return total_cin
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from errors import drc_error
|
from errors import drc_error
|
||||||
from tech import cell_properties, layer
|
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_decoder(design.design):
|
class hierarchical_decoder(design.design):
|
||||||
|
|
@ -28,12 +27,8 @@ class hierarchical_decoder(design.design):
|
||||||
self.pre3x8_inst = []
|
self.pre3x8_inst = []
|
||||||
|
|
||||||
b = factory.create(module_type="bitcell")
|
b = factory.create(module_type="bitcell")
|
||||||
try:
|
self.cell_height = b.height
|
||||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
|
||||||
except AttributeError:
|
|
||||||
self.cell_multiple = 1
|
|
||||||
self.cell_height = self.cell_multiple * b.height
|
|
||||||
|
|
||||||
self.num_outputs = num_outputs
|
self.num_outputs = num_outputs
|
||||||
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
||||||
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||||
|
|
@ -41,41 +36,6 @@ class hierarchical_decoder(design.design):
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
def find_decoder_height(self):
|
|
||||||
"""
|
|
||||||
Dead code. This would dynamically determine the bitcell multiple,
|
|
||||||
but I just decided to hard code it in the tech file if it is not 1
|
|
||||||
because a DRC tool would be required even to run in front-end mode.
|
|
||||||
"""
|
|
||||||
b = factory.create(module_type="bitcell")
|
|
||||||
|
|
||||||
# Old behavior
|
|
||||||
if OPTS.netlist_only:
|
|
||||||
return (b.height, 1)
|
|
||||||
|
|
||||||
# Search for the smallest multiple that works
|
|
||||||
cell_multiple = 1
|
|
||||||
while cell_multiple < 5:
|
|
||||||
cell_height = cell_multiple * b.height
|
|
||||||
# debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height))
|
|
||||||
try:
|
|
||||||
and3 = factory.create(module_type="pand3",
|
|
||||||
height=cell_height)
|
|
||||||
except drc_error:
|
|
||||||
# debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height))
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
(drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True)
|
|
||||||
total_errors = drc_errors + lvs_errors
|
|
||||||
if total_errors == 0:
|
|
||||||
debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple))
|
|
||||||
return (cell_height, cell_multiple)
|
|
||||||
|
|
||||||
cell_multiple += 1
|
|
||||||
|
|
||||||
else:
|
|
||||||
debug.error("Couldn't find a valid decoder height multiple.", -1)
|
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
|
@ -88,24 +48,32 @@ class hierarchical_decoder(design.design):
|
||||||
self.setup_layout_constants()
|
self.setup_layout_constants()
|
||||||
self.place_pre_decoder()
|
self.place_pre_decoder()
|
||||||
self.place_row_decoder()
|
self.place_row_decoder()
|
||||||
|
|
||||||
|
self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space
|
||||||
|
|
||||||
self.route_inputs()
|
self.route_inputs()
|
||||||
self.route_outputs()
|
self.route_outputs()
|
||||||
self.route_decoder_bus()
|
self.route_decoder_bus()
|
||||||
self.route_vdd_gnd()
|
self.route_vdd_gnd()
|
||||||
|
|
||||||
self.offset_all_coordinates()
|
self.offset_all_coordinates()
|
||||||
|
|
||||||
|
self.width = self.and_inst[0].rx() + self.m1_space
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.inv = factory.create(module_type="pinv",
|
self.and2 = factory.create(module_type="and2_dec",
|
||||||
height=self.cell_height)
|
|
||||||
self.add_mod(self.inv)
|
|
||||||
self.and2 = factory.create(module_type="pand2",
|
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and2)
|
self.add_mod(self.and2)
|
||||||
self.and3 = factory.create(module_type="pand3",
|
|
||||||
|
self.and3 = factory.create(module_type="and3_dec",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and3)
|
self.add_mod(self.and3)
|
||||||
|
# TBD
|
||||||
|
# self.and4 = factory.create(module_type="and4_dec")
|
||||||
|
# self.add_mod(self.and4)
|
||||||
|
|
||||||
self.add_decoders()
|
self.add_decoders()
|
||||||
|
|
||||||
|
|
@ -176,56 +144,49 @@ class hierarchical_decoder(design.design):
|
||||||
-1)
|
-1)
|
||||||
|
|
||||||
# Calculates height and width of pre-decoder,
|
# Calculates height and width of pre-decoder,
|
||||||
if self.no_of_pre3x8 > 0:
|
# FIXME: Update with 4x16
|
||||||
|
if self.no_of_pre3x8 > 0 and self.no_of_pre2x4 > 0:
|
||||||
|
self.predecoder_width = max(self.pre3_8.width, self.pre2_4.width)
|
||||||
|
elif self.no_of_pre3x8 > 0:
|
||||||
self.predecoder_width = self.pre3_8.width
|
self.predecoder_width = self.pre3_8.width
|
||||||
else:
|
else:
|
||||||
self.predecoder_width = self.pre2_4.width
|
self.predecoder_width = self.pre2_4.width
|
||||||
|
|
||||||
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
|
|
||||||
|
|
||||||
# We may have more than one bitcell per decoder row
|
# How much space between each predecoder
|
||||||
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
|
self.predecoder_spacing = self.and2.height
|
||||||
# We will place this many final decoders per row
|
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \
|
||||||
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
|
+ (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing
|
||||||
# We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row
|
|
||||||
if self.decoders_per_row == 1:
|
# Inputs to cells are on input layer
|
||||||
self.decoder_bus_pitch = self.m2_pitch
|
# Outputs from cells are on output layer
|
||||||
elif self.decoders_per_row == 2:
|
if OPTS.tech_name == "s8":
|
||||||
self.decoder_bus_pitch = self.m3_pitch
|
self.bus_layer = "m1"
|
||||||
|
self.bus_directions = "nonpref"
|
||||||
|
self.bus_pitch = self.m1_pitch
|
||||||
|
self.bus_space = self.m2_space
|
||||||
|
self.input_layer = "m2"
|
||||||
|
self.output_layer = "li"
|
||||||
|
self.output_layer_pitch = self.li_pitch
|
||||||
else:
|
else:
|
||||||
debug.error("Insufficient layers for multi-bit height decoder.", -1)
|
self.bus_layer = "m2"
|
||||||
|
self.bus_directions = "pref"
|
||||||
|
self.bus_pitch = self.m2_pitch
|
||||||
|
self.bus_space = self.m2_space
|
||||||
|
# These two layers being the same requires a special jog
|
||||||
|
# to ensure to conflicts with the output layers
|
||||||
|
self.input_layer = "m1"
|
||||||
|
self.output_layer = "m3"
|
||||||
|
self.output_layer_pitch = self.m3_pitch
|
||||||
|
|
||||||
# Calculates height and width of row-decoder
|
# Two extra pitches between modules on left and right
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
|
||||||
nand_width = self.and2.width
|
self.row_decoder_height = self.and2.height * self.num_outputs
|
||||||
nand_inputs = 2
|
|
||||||
else:
|
|
||||||
nand_width = self.and3.width
|
|
||||||
nand_inputs = 3
|
|
||||||
self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1)
|
|
||||||
self.row_decoder_height = self.inv.height * self.num_rows
|
|
||||||
|
|
||||||
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch
|
|
||||||
# print(self.decoders_per_row, nand_inputs)
|
|
||||||
# print(decoder_input_wire_height, self.cell_height)
|
|
||||||
if decoder_input_wire_height > self.cell_height:
|
|
||||||
debug.warning("Cannot fit multi-bit decoder routes per row.")
|
|
||||||
# debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
|
|
||||||
|
|
||||||
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
# Extra bus space for supply contacts
|
||||||
|
self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space
|
||||||
|
|
||||||
# Calculates height and width of hierarchical decoder
|
|
||||||
# Add extra pitch for good measure
|
|
||||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch
|
|
||||||
self.width = self.input_routing_width + self.predecoder_width \
|
|
||||||
+ self.internal_routing_width \
|
|
||||||
+ self.decoders_per_row * nand_width + self.inv.width
|
|
||||||
|
|
||||||
def route_inputs(self):
|
def route_inputs(self):
|
||||||
""" Create input bus for the predecoders """
|
""" Create input bus for the predecoders """
|
||||||
# inputs should be as high as the decoders
|
|
||||||
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
|
|
||||||
|
|
||||||
# Find the left-most predecoder
|
# Find the left-most predecoder
|
||||||
min_x = 0
|
min_x = 0
|
||||||
if self.no_of_pre2x4 > 0:
|
if self.no_of_pre2x4 > 0:
|
||||||
|
|
@ -235,10 +196,10 @@ class hierarchical_decoder(design.design):
|
||||||
input_offset=vector(min_x - self.input_routing_width, 0)
|
input_offset=vector(min_x - self.input_routing_width, 0)
|
||||||
|
|
||||||
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
||||||
self.input_bus = self.create_vertical_pin_bus(layer="m2",
|
self.input_bus = self.create_vertical_pin_bus(layer=self.bus_layer,
|
||||||
offset=input_offset,
|
offset=input_offset,
|
||||||
names=input_bus_names,
|
names=input_bus_names,
|
||||||
length=input_height)
|
length=self.predecoder_height)
|
||||||
|
|
||||||
self.route_input_to_predecodes()
|
self.route_input_to_predecodes()
|
||||||
|
|
||||||
|
|
@ -253,9 +214,7 @@ class hierarchical_decoder(design.design):
|
||||||
in_name = "in_{}".format(i)
|
in_name = "in_{}".format(i)
|
||||||
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
||||||
|
|
||||||
# To prevent conflicts, we will offset each input connect so
|
decoder_offset = decoder_pin.center()
|
||||||
# that it aligns with the vdd/gnd rails
|
|
||||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
|
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_bus(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
@ -269,9 +228,7 @@ class hierarchical_decoder(design.design):
|
||||||
in_name = "in_{}".format(i)
|
in_name = "in_{}".format(i)
|
||||||
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
||||||
|
|
||||||
# To prevent conflicts, we will offset each input connect so
|
decoder_offset = decoder_pin.center()
|
||||||
# that it aligns with the vdd/gnd rails
|
|
||||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
|
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_bus(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
@ -282,13 +239,14 @@ class hierarchical_decoder(design.design):
|
||||||
vertical M2 coordinate to the predecode inputs
|
vertical M2 coordinate to the predecode inputs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer="m2",
|
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||||
to_layer="m3",
|
to_layer=self.input_layer,
|
||||||
offset=input_offset)
|
offset=input_offset)
|
||||||
self.add_via_stack_center(from_layer="m2",
|
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||||
to_layer="m3",
|
to_layer=self.input_layer,
|
||||||
offset=output_offset)
|
offset=output_offset,
|
||||||
self.add_path("m3", [input_offset, output_offset])
|
directions=self.bus_directions)
|
||||||
|
self.add_path(self.input_layer, [input_offset, output_offset])
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" Add the module pins """
|
""" Add the module pins """
|
||||||
|
|
@ -363,19 +321,19 @@ class hierarchical_decoder(design.design):
|
||||||
if (self.num_inputs == 2):
|
if (self.num_inputs == 2):
|
||||||
base = vector(-self.pre2_4.width, 0)
|
base = vector(-self.pre2_4.width, 0)
|
||||||
else:
|
else:
|
||||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
|
||||||
|
|
||||||
self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0))
|
self.pre2x4_inst[num].place(base)
|
||||||
|
|
||||||
def place_pre3x8(self, num):
|
def place_pre3x8(self, num):
|
||||||
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||||
if (self.num_inputs == 3):
|
if (self.num_inputs == 3):
|
||||||
offset = vector(-self.pre_3_8.width, 0)
|
offset = vector(-self.pre_3_8.width, 0)
|
||||||
else:
|
else:
|
||||||
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
|
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) + num * (self.pre3_8.height + self.predecoder_spacing)
|
||||||
offset = vector(-self.pre3_8.width, height)
|
offset = vector(-self.pre3_8.width, height)
|
||||||
|
|
||||||
self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0))
|
self.pre3x8_inst[num].place(offset)
|
||||||
|
|
||||||
def create_row_decoder(self):
|
def create_row_decoder(self):
|
||||||
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
||||||
|
|
@ -431,7 +389,6 @@ class hierarchical_decoder(design.design):
|
||||||
if (self.num_inputs >= 4):
|
if (self.num_inputs >= 4):
|
||||||
self.place_decoder_and_array()
|
self.place_decoder_and_array()
|
||||||
|
|
||||||
|
|
||||||
def place_decoder_and_array(self):
|
def place_decoder_and_array(self):
|
||||||
"""
|
"""
|
||||||
Add a column of AND gates for final decode.
|
Add a column of AND gates for final decode.
|
||||||
|
|
@ -452,9 +409,7 @@ class hierarchical_decoder(design.design):
|
||||||
Add a column of AND gates for the decoder above the predecoders.
|
Add a column of AND gates for the decoder above the predecoders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for inst_index in range(self.num_outputs):
|
for row in range(self.num_outputs):
|
||||||
row = math.floor(inst_index / self.decoders_per_row)
|
|
||||||
dec = inst_index % self.decoders_per_row
|
|
||||||
if ((row % 2) == 0):
|
if ((row % 2) == 0):
|
||||||
y_off = and_mod.height * row
|
y_off = and_mod.height * row
|
||||||
mirror = "R0"
|
mirror = "R0"
|
||||||
|
|
@ -462,32 +417,16 @@ class hierarchical_decoder(design.design):
|
||||||
y_off = and_mod.height * (row + 1)
|
y_off = and_mod.height * (row + 1)
|
||||||
mirror = "MX"
|
mirror = "MX"
|
||||||
|
|
||||||
x_off = self.internal_routing_width + dec * and_mod.width
|
x_off = self.internal_routing_width
|
||||||
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
|
self.and_inst[row].place(offset=vector(x_off, y_off),
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def route_outputs(self):
|
def route_outputs(self):
|
||||||
""" Add the pins. """
|
""" Add the pins. """
|
||||||
|
|
||||||
max_xoffset = max(x.rx() for x in self.and_inst)
|
for row in range(self.num_outputs):
|
||||||
|
and_inst = self.and_inst[row]
|
||||||
for output_index in range(self.num_outputs):
|
self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row))
|
||||||
row_remainder = (output_index % self.decoders_per_row)
|
|
||||||
|
|
||||||
and_inst = self.and_inst[output_index]
|
|
||||||
z_pin = and_inst.get_pin("Z")
|
|
||||||
if row_remainder == 0 and self.decoders_per_row > 1:
|
|
||||||
layer = "m3"
|
|
||||||
self.add_via_stack_center(from_layer=z_pin.layer,
|
|
||||||
to_layer="m3",
|
|
||||||
offset=z_pin.center())
|
|
||||||
else:
|
|
||||||
layer = z_pin.layer
|
|
||||||
|
|
||||||
self.add_layout_pin_segment_center(text="decode_{0}".format(output_index),
|
|
||||||
layer=layer,
|
|
||||||
start=z_pin.center(),
|
|
||||||
end=vector(max_xoffset, z_pin.cy()))
|
|
||||||
|
|
||||||
def route_decoder_bus(self):
|
def route_decoder_bus(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -498,9 +437,9 @@ class hierarchical_decoder(design.design):
|
||||||
if (self.num_inputs >= 4):
|
if (self.num_inputs >= 4):
|
||||||
# This leaves an offset for the predecoder output jogs
|
# This leaves an offset for the predecoder output jogs
|
||||||
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||||
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
|
self.predecode_bus = self.create_vertical_pin_bus(layer=self.bus_layer,
|
||||||
pitch=self.decoder_bus_pitch,
|
pitch=self.bus_pitch,
|
||||||
offset=vector(0, 0),
|
offset=vector(self.bus_pitch, 0),
|
||||||
names=input_bus_names,
|
names=input_bus_names,
|
||||||
length=self.height)
|
length=self.height)
|
||||||
|
|
||||||
|
|
@ -518,8 +457,9 @@ class hierarchical_decoder(design.design):
|
||||||
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
||||||
out_name = "out_{}".format(i)
|
out_name = "out_{}".format(i)
|
||||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||||
x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch
|
x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch
|
||||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height
|
||||||
|
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
||||||
|
|
||||||
# FIXME: convert to connect_bus
|
# FIXME: convert to connect_bus
|
||||||
for pre_num in range(self.no_of_pre3x8):
|
for pre_num in range(self.no_of_pre3x8):
|
||||||
|
|
@ -527,8 +467,9 @@ class hierarchical_decoder(design.design):
|
||||||
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
||||||
out_name = "out_{}".format(i)
|
out_name = "out_{}".format(i)
|
||||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||||
x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch
|
x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch
|
||||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height
|
||||||
|
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
||||||
|
|
||||||
def route_bus_to_decoder(self):
|
def route_bus_to_decoder(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -542,12 +483,6 @@ class hierarchical_decoder(design.design):
|
||||||
and the 128th AND3 is connected to [3,7,15]
|
and the 128th AND3 is connected to [3,7,15]
|
||||||
"""
|
"""
|
||||||
output_index = 0
|
output_index = 0
|
||||||
|
|
||||||
if "li" in layer:
|
|
||||||
self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]]
|
|
||||||
else:
|
|
||||||
self.decoder_layers = [self.m2_stack[::-1]]
|
|
||||||
debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.")
|
|
||||||
|
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
for index_B in self.predec_groups[1]:
|
for index_B in self.predec_groups[1]:
|
||||||
|
|
@ -557,13 +492,11 @@ class hierarchical_decoder(design.design):
|
||||||
predecode_name = "predecode_{}".format(index_A)
|
predecode_name = "predecode_{}".format(index_A)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("A"),
|
self.and_inst[output_index].get_pin("A"),
|
||||||
output_index,
|
output_index)
|
||||||
0)
|
|
||||||
predecode_name = "predecode_{}".format(index_B)
|
predecode_name = "predecode_{}".format(index_B)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("B"),
|
self.and_inst[output_index].get_pin("B"),
|
||||||
output_index,
|
output_index)
|
||||||
1)
|
|
||||||
output_index = output_index + 1
|
output_index = output_index + 1
|
||||||
|
|
||||||
elif (self.num_inputs > 5):
|
elif (self.num_inputs > 5):
|
||||||
|
|
@ -575,18 +508,15 @@ class hierarchical_decoder(design.design):
|
||||||
predecode_name = "predecode_{}".format(index_A)
|
predecode_name = "predecode_{}".format(index_A)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("A"),
|
self.and_inst[output_index].get_pin("A"),
|
||||||
output_index,
|
output_index)
|
||||||
0)
|
|
||||||
predecode_name = "predecode_{}".format(index_B)
|
predecode_name = "predecode_{}".format(index_B)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("B"),
|
self.and_inst[output_index].get_pin("B"),
|
||||||
output_index,
|
output_index)
|
||||||
1)
|
|
||||||
predecode_name = "predecode_{}".format(index_C)
|
predecode_name = "predecode_{}".format(index_C)
|
||||||
self.route_predecode_bus_outputs(predecode_name,
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
self.and_inst[output_index].get_pin("C"),
|
self.and_inst[output_index].get_pin("C"),
|
||||||
output_index,
|
output_index)
|
||||||
2)
|
|
||||||
output_index = output_index + 1
|
output_index = output_index + 1
|
||||||
|
|
||||||
def route_vdd_gnd(self):
|
def route_vdd_gnd(self):
|
||||||
|
|
@ -594,90 +524,90 @@ class hierarchical_decoder(design.design):
|
||||||
Add a pin for each row of vdd/gnd which are
|
Add a pin for each row of vdd/gnd which are
|
||||||
must-connects next level up.
|
must-connects next level up.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
for n in ["vdd", "gnd"]:
|
||||||
|
pins = self.and_inst[0].get_pins(n)
|
||||||
|
for pin in pins:
|
||||||
|
self.add_rect(layer=pin.layer,
|
||||||
|
offset=pin.ll() + vector(0, self.bus_space),
|
||||||
|
width=pin.width(),
|
||||||
|
height=self.height - 2 * self.bus_space)
|
||||||
|
|
||||||
# The vias will be placed at the right of the cells.
|
# This adds power vias at the top of each cell
|
||||||
xoffset = max(x.rx() for x in self.and_inst)
|
# (except the last to keep them inside the boundary)
|
||||||
for num in range(0, self.num_outputs):
|
for i in self.and_inst[:-1]:
|
||||||
# Only add the power pin for the 1st in each row
|
pins = i.get_pins(n)
|
||||||
if num % self.decoders_per_row:
|
for pin in pins:
|
||||||
continue
|
self.add_power_pin(name=n,
|
||||||
|
loc=pin.uc(),
|
||||||
for pin_name in ["vdd", "gnd"]:
|
start_layer=pin.layer)
|
||||||
# The nand and inv are the same height rows...
|
self.add_power_pin(name=n,
|
||||||
supply_pin = self.and_inst[num].get_pin(pin_name)
|
loc=pin.uc(),
|
||||||
pin_pos = vector(xoffset, supply_pin.cy())
|
start_layer=pin.layer)
|
||||||
self.add_path(supply_pin.layer,
|
|
||||||
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
|
for i in self.pre2x4_inst + self.pre3x8_inst:
|
||||||
self.add_power_pin(name=pin_name,
|
self.copy_layout_pin(i, n)
|
||||||
loc=pin_pos,
|
else:
|
||||||
start_layer=supply_pin.layer)
|
# The vias will be placed at the right of the cells.
|
||||||
|
xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space
|
||||||
# Copy the pins from the predecoders
|
for row in range(0, self.num_outputs):
|
||||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
self.copy_layout_pin(pre, "vdd")
|
# The nand and inv are the same height rows...
|
||||||
self.copy_layout_pin(pre, "gnd")
|
supply_pin = self.and_inst[row].get_pin(pin_name)
|
||||||
|
pin_pos = vector(xoffset, supply_pin.cy())
|
||||||
|
self.add_power_pin(name=pin_name,
|
||||||
|
loc=pin_pos,
|
||||||
|
start_layer=supply_pin.layer)
|
||||||
|
|
||||||
|
# Copy the pins from the predecoders
|
||||||
|
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||||
|
for pin_name in ["vdd", "gnd"]:
|
||||||
|
self.copy_layout_pin(pre, pin_name)
|
||||||
|
|
||||||
def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index):
|
def route_predecode_bus_outputs(self, rail_name, pin, row):
|
||||||
"""
|
"""
|
||||||
Connect the routing rail to the given metal1 pin
|
Connect the routing rail to the given metal1 pin
|
||||||
using a routing track at the given y_offset
|
using a routing track at the given y_offset
|
||||||
"""
|
"""
|
||||||
|
|
||||||
row_index = math.floor(output_index / self.decoders_per_row)
|
|
||||||
row_remainder = (output_index % self.decoders_per_row)
|
|
||||||
row_offset = row_index * self.and_inst[0].height
|
|
||||||
|
|
||||||
pin_pos = pin.center()
|
pin_pos = pin.center()
|
||||||
|
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||||
# y_offset is the same for both the M2 and M4 routes so that the rail
|
self.add_path(self.input_layer, [rail_pos, pin_pos])
|
||||||
# contacts align and don't cause problems
|
|
||||||
if pin_index == 0:
|
|
||||||
# Bottom pitch
|
|
||||||
y_offset = row_offset
|
|
||||||
elif pin_index == 1:
|
|
||||||
# One pitch from top
|
|
||||||
y_offset = row_offset + self.and_inst[0].height - self.m3_pitch
|
|
||||||
elif pin_index == 2:
|
|
||||||
# One pitch from bottom
|
|
||||||
y_offset = row_offset + self.m3_pitch
|
|
||||||
else:
|
|
||||||
debug.error("Invalid decoder pitch.")
|
|
||||||
|
|
||||||
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
|
|
||||||
mid_pos = vector(pin_pos.x, rail_pos.y)
|
|
||||||
self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos])
|
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer="m2",
|
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||||
to_layer=self.decoder_layers[row_remainder][0],
|
to_layer=self.input_layer,
|
||||||
offset=rail_pos)
|
offset=rail_pos,
|
||||||
|
directions=self.bus_directions)
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
to_layer=self.decoder_layers[row_remainder][2],
|
to_layer=self.input_layer,
|
||||||
offset=pin_pos,
|
offset=pin_pos,
|
||||||
directions=("H", "H"))
|
directions=("H", "H"))
|
||||||
|
|
||||||
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
|
def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset):
|
||||||
"""
|
"""
|
||||||
Connect the routing rail to the given metal1 pin using a jog
|
Connect the routing rail to the given metal1 pin using a jog
|
||||||
to the right of the cell at the given x_offset.
|
to the right of the cell at the given x_offset.
|
||||||
"""
|
"""
|
||||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||||
# It would be fixed with a channel router.
|
# It would be fixed with a channel router.
|
||||||
# pin_pos = pin.center()
|
pin_pos = pin.rc()
|
||||||
# mid_point1 = vector(x_offset, pin_pos.y)
|
mid_point1 = vector(x_offset, pin_pos.y)
|
||||||
# mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
|
mid_point2 = vector(x_offset, y_offset)
|
||||||
# rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
||||||
# self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
|
self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||||
|
|
||||||
pin_pos = pin.center()
|
# pin_pos = pin.center()
|
||||||
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
# rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||||
self.add_path("m1", [pin_pos, rail_pos])
|
# self.add_path(self.output_layer, [pin_pos, rail_pos])
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
to_layer="m1",
|
to_layer=self.output_layer,
|
||||||
offset=pin_pos)
|
offset=pin_pos)
|
||||||
self.add_via_stack_center(from_layer="m1",
|
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||||
to_layer="m2",
|
to_layer=self.output_layer,
|
||||||
offset=rail_pos)
|
offset=rail_pos,
|
||||||
|
directions=self.bus_directions)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
if self.determine_predecodes(self.num_inputs)[1]==0:
|
||||||
|
|
|
||||||
|
|
@ -8,29 +8,24 @@
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
import math
|
import math
|
||||||
import contact
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from tech import cell_properties
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_predecode(design.design):
|
class hierarchical_predecode(design.design):
|
||||||
"""
|
"""
|
||||||
Pre 2x4 and 3x8 decoder shared code.
|
Pre 2x4 and 3x8 and TBD 4x16 decoder shared code.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, input_number, height=None):
|
def __init__(self, name, input_number, height=None):
|
||||||
self.number_of_inputs = input_number
|
self.number_of_inputs = input_number
|
||||||
|
|
||||||
if not height:
|
if not height:
|
||||||
b = factory.create(module_type="bitcell")
|
b = factory.create(module_type="bitcell")
|
||||||
try:
|
self.cell_height = b.height
|
||||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
|
||||||
except AttributeError:
|
|
||||||
self.cell_multiple = 1
|
|
||||||
self.cell_height = self.cell_multiple * b.height
|
|
||||||
else:
|
else:
|
||||||
self.cell_height = height
|
self.cell_height = height
|
||||||
|
|
||||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
|
@ -44,34 +39,71 @@ class hierarchical_predecode(design.design):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the INV and AND gate modules """
|
""" Add the INV and AND gate modules """
|
||||||
|
|
||||||
self.inv = factory.create(module_type="pinv",
|
|
||||||
height=self.cell_height)
|
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
self.add_and(self.number_of_inputs)
|
|
||||||
self.add_mod(self.and_mod)
|
|
||||||
|
|
||||||
def add_and(self, inputs):
|
if self.number_of_inputs == 2:
|
||||||
""" Create the NAND for the predecode input stage """
|
self.and_mod = factory.create(module_type="and2_dec",
|
||||||
if inputs==2:
|
|
||||||
self.and_mod = factory.create(module_type="pand2",
|
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
elif inputs==3:
|
elif self.number_of_inputs == 3:
|
||||||
self.and_mod = factory.create(module_type="pand3",
|
self.and_mod = factory.create(module_type="and3_dec",
|
||||||
|
height=self.cell_height)
|
||||||
|
elif self.number_of_inputs == 4:
|
||||||
|
self.and_mod = factory.create(module_type="and4_dec",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
|
debug.error("Invalid number of predecode inputs: {}".format(self.number_of_inputs), -1)
|
||||||
|
self.add_mod(self.and_mod)
|
||||||
|
|
||||||
|
# This uses the pinv_dec parameterized cell
|
||||||
|
self.inv = factory.create(module_type="inv_dec",
|
||||||
|
height=self.cell_height,
|
||||||
|
size=1)
|
||||||
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
""" The general organization is from left to right:
|
||||||
|
1) a set of M2 rails for input signals
|
||||||
|
2) a set of inverters to invert input signals
|
||||||
|
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
||||||
|
4) a set of AND gates for inversion
|
||||||
|
"""
|
||||||
|
self.setup_layout_constraints()
|
||||||
|
self.route_rails()
|
||||||
|
self.place_input_inverters()
|
||||||
|
self.place_and_array()
|
||||||
|
self.route()
|
||||||
|
self.add_boundary()
|
||||||
|
self.DRC_LVS()
|
||||||
|
|
||||||
def setup_layout_constraints(self):
|
def setup_layout_constraints(self):
|
||||||
|
|
||||||
|
# Inputs to cells are on input layer
|
||||||
|
# Outputs from cells are on output layer
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
self.bus_layer = "m1"
|
||||||
|
self.bus_directions = None
|
||||||
|
self.bus_pitch = self.m1_pitch
|
||||||
|
self.bus_space = 1.5 * self.m1_space
|
||||||
|
self.input_layer = "li"
|
||||||
|
self.output_layer = "m2"
|
||||||
|
self.output_layer_pitch = self.m2_pitch
|
||||||
|
else:
|
||||||
|
self.bus_layer = "m2"
|
||||||
|
self.bus_directions = None
|
||||||
|
self.bus_pitch = self.m2_pitch
|
||||||
|
self.bus_space = self.m2_space
|
||||||
|
# This requires a special jog to ensure to conflicts with the output layers
|
||||||
|
self.input_layer = "m1"
|
||||||
|
self.output_layer = "m1"
|
||||||
|
self.output_layer_pitch = self.m1_pitch
|
||||||
|
|
||||||
self.height = self.number_of_outputs * self.and_mod.height
|
self.height = self.number_of_outputs * self.and_mod.height
|
||||||
|
|
||||||
# x offset for input inverters
|
# x offset for input inverters
|
||||||
self.x_off_inv_1 = self.number_of_inputs * self.m2_pitch + self.m2_space
|
# +1 input for spacing for supply rail contacts
|
||||||
|
self.x_off_inv_1 = (self.number_of_inputs + 1) * self.bus_pitch + self.bus_pitch
|
||||||
|
|
||||||
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra bus pitches
|
||||||
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.m2_pitch
|
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.bus_pitch
|
||||||
|
|
||||||
# x offset to output inverters
|
# x offset to output inverters
|
||||||
self.width = self.x_off_and + self.and_mod.width
|
self.width = self.x_off_and + self.and_mod.width
|
||||||
|
|
@ -79,28 +111,30 @@ class hierarchical_predecode(design.design):
|
||||||
def route_rails(self):
|
def route_rails(self):
|
||||||
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
||||||
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
|
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
|
||||||
offset = vector(0.5 * self.m2_width, self.m3_pitch)
|
# Offsets for the perimeter spacing to other modules
|
||||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
# This uses m3 pitch to leave space for power routes
|
||||||
offset=offset,
|
offset = vector(self.bus_pitch, self.bus_pitch)
|
||||||
names=input_names,
|
self.input_rails = self.create_vertical_bus(layer=self.bus_layer,
|
||||||
length=self.height - 2 * self.m1_pitch)
|
offset=offset,
|
||||||
|
names=input_names,
|
||||||
|
length=self.height - 2 * self.bus_pitch)
|
||||||
|
|
||||||
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
|
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
|
||||||
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
|
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
|
||||||
decode_names = invert_names + non_invert_names
|
decode_names = invert_names + non_invert_names
|
||||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m3_pitch)
|
offset = vector(self.x_off_inv_1 + self.inv.width + self.bus_pitch, self.bus_pitch)
|
||||||
self.decode_rails = self.create_vertical_bus(layer="m2",
|
self.decode_rails = self.create_vertical_bus(layer=self.bus_layer,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
names=decode_names,
|
names=decode_names,
|
||||||
length=self.height - 2 * self.m1_pitch)
|
length=self.height - 2 * self.bus_pitch)
|
||||||
|
|
||||||
def create_input_inverters(self):
|
def create_input_inverters(self):
|
||||||
""" Create the input inverters to invert input signals for the decode stage. """
|
""" Create the input inverters to invert input signals for the decode stage. """
|
||||||
self.in_inst = []
|
self.inv_inst = []
|
||||||
for inv_num in range(self.number_of_inputs):
|
for inv_num in range(self.number_of_inputs):
|
||||||
name = "pre_inv_{0}".format(inv_num)
|
name = "pre_inv_{0}".format(inv_num)
|
||||||
self.in_inst.append(self.add_inst(name=name,
|
self.inv_inst.append(self.add_inst(name=name,
|
||||||
mod=self.inv))
|
mod=self.inv))
|
||||||
self.connect_inst(["in_{0}".format(inv_num),
|
self.connect_inst(["in_{0}".format(inv_num),
|
||||||
"inbar_{0}".format(inv_num),
|
"inbar_{0}".format(inv_num),
|
||||||
"vdd", "gnd"])
|
"vdd", "gnd"])
|
||||||
|
|
@ -108,6 +142,7 @@ class hierarchical_predecode(design.design):
|
||||||
def place_input_inverters(self):
|
def place_input_inverters(self):
|
||||||
""" Place the input inverters to invert input signals for the decode stage. """
|
""" Place the input inverters to invert input signals for the decode stage. """
|
||||||
for inv_num in range(self.number_of_inputs):
|
for inv_num in range(self.number_of_inputs):
|
||||||
|
|
||||||
if (inv_num % 2 == 0):
|
if (inv_num % 2 == 0):
|
||||||
y_off = inv_num * (self.inv.height)
|
y_off = inv_num * (self.inv.height)
|
||||||
mirror = "R0"
|
mirror = "R0"
|
||||||
|
|
@ -115,8 +150,8 @@ class hierarchical_predecode(design.design):
|
||||||
y_off = (inv_num + 1) * (self.inv.height)
|
y_off = (inv_num + 1) * (self.inv.height)
|
||||||
mirror="MX"
|
mirror="MX"
|
||||||
offset = vector(self.x_off_inv_1, y_off)
|
offset = vector(self.x_off_inv_1, y_off)
|
||||||
self.in_inst[inv_num].place(offset=offset,
|
self.inv_inst[inv_num].place(offset=offset,
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def create_and_array(self, connections):
|
def create_and_array(self, connections):
|
||||||
""" Create the AND stage for the decodes """
|
""" Create the AND stage for the decodes """
|
||||||
|
|
@ -151,21 +186,30 @@ class hierarchical_predecode(design.design):
|
||||||
|
|
||||||
def route_inputs_to_rails(self):
|
def route_inputs_to_rails(self):
|
||||||
""" Route the uninverted inputs to the second set of rails """
|
""" Route the uninverted inputs to the second set of rails """
|
||||||
|
|
||||||
|
top_and_gate = self.and_inst[-1]
|
||||||
for num in range(self.number_of_inputs):
|
for num in range(self.number_of_inputs):
|
||||||
# route one signal next to each vdd/gnd rail since this is
|
if num == 0:
|
||||||
# typically where the p/n devices are and there are no
|
pin = top_and_gate.get_pin("A")
|
||||||
# pins in the and gates.
|
elif num == 1:
|
||||||
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m2_via.width + self.m2_space
|
pin = top_and_gate.get_pin("B")
|
||||||
|
elif num == 2:
|
||||||
|
pin = top_and_gate.get_pin("C")
|
||||||
|
elif num == 3:
|
||||||
|
pin = top_and_gate.get_pin("D")
|
||||||
|
else:
|
||||||
|
debug.error("Too many inputs for predecoder.", -1)
|
||||||
|
y_offset = pin.cy()
|
||||||
in_pin = "in_{}".format(num)
|
in_pin = "in_{}".format(num)
|
||||||
a_pin = "A_{}".format(num)
|
a_pin = "A_{}".format(num)
|
||||||
in_pos = vector(self.input_rails[in_pin].x, y_offset)
|
in_pos = vector(self.input_rails[in_pin].x, y_offset)
|
||||||
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
|
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
|
||||||
self.add_path("m1", [in_pos, a_pos])
|
self.add_path(self.input_layer, [in_pos, a_pos])
|
||||||
self.add_via_stack_center(from_layer="m1",
|
self.add_via_stack_center(from_layer=self.input_layer,
|
||||||
to_layer="m2",
|
to_layer=self.bus_layer,
|
||||||
offset=[self.input_rails[in_pin].x, y_offset])
|
offset=[self.input_rails[in_pin].x, y_offset])
|
||||||
self.add_via_stack_center(from_layer="m1",
|
self.add_via_stack_center(from_layer=self.input_layer,
|
||||||
to_layer="m2",
|
to_layer=self.bus_layer,
|
||||||
offset=[self.decode_rails[a_pin].x, y_offset])
|
offset=[self.decode_rails[a_pin].x, y_offset])
|
||||||
|
|
||||||
def route_output_and(self):
|
def route_output_and(self):
|
||||||
|
|
@ -188,31 +232,47 @@ class hierarchical_predecode(design.design):
|
||||||
for inv_num in range(self.number_of_inputs):
|
for inv_num in range(self.number_of_inputs):
|
||||||
out_pin = "Abar_{}".format(inv_num)
|
out_pin = "Abar_{}".format(inv_num)
|
||||||
in_pin = "in_{}".format(inv_num)
|
in_pin = "in_{}".format(inv_num)
|
||||||
|
|
||||||
|
inv_out_pin = self.inv_inst[inv_num].get_pin("Z")
|
||||||
|
inv_out_pos = inv_out_pin.rc()
|
||||||
|
|
||||||
# add output so that it is just below the vdd or gnd rail
|
# add output so that it is just below the vdd or gnd rail
|
||||||
# since this is where the p/n devices are and there are no
|
# since this is where the p/n devices are and there are no
|
||||||
# pins in the and gates.
|
# pins in the and gates.
|
||||||
y_offset = (inv_num + 1) * self.inv.height - 3 * self.m1_space
|
if OPTS.tech_name == "s8":
|
||||||
inv_out_pin = self.in_inst[inv_num].get_pin("Z")
|
rail_pos = vector(self.decode_rails[out_pin].x, inv_out_pos.y)
|
||||||
inv_out_pos = inv_out_pin.rc()
|
self.add_path(self.output_layer, [inv_out_pos, rail_pos])
|
||||||
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(), 0)
|
else:
|
||||||
rail_pos = vector(self.decode_rails[out_pin].x, y_offset)
|
y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch
|
||||||
self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0)
|
||||||
|
rail_pos = vector(self.decode_rails[out_pin].x, y_offset)
|
||||||
|
self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=inv_out_pin.layer,
|
self.add_via_stack_center(from_layer=inv_out_pin.layer,
|
||||||
to_layer="m2",
|
to_layer=self.output_layer,
|
||||||
offset=rail_pos)
|
offset=inv_out_pos)
|
||||||
|
self.add_via_stack_center(from_layer=self.output_layer,
|
||||||
|
to_layer=self.bus_layer,
|
||||||
|
offset=rail_pos,
|
||||||
|
directions=self.bus_directions)
|
||||||
|
|
||||||
# route input
|
# route input
|
||||||
pin = self.in_inst[inv_num].get_pin("A")
|
pin = self.inv_inst[inv_num].get_pin("A")
|
||||||
inv_in_pos = pin.lc()
|
inv_in_pos = pin.center()
|
||||||
in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y)
|
in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y)
|
||||||
self.add_path("m1", [in_pos, inv_in_pos])
|
self.add_path(self.input_layer, [in_pos, inv_in_pos])
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
to_layer="m1",
|
to_layer=self.input_layer,
|
||||||
offset=inv_in_pos)
|
offset=inv_in_pos)
|
||||||
self.add_via_stack_center(from_layer="m1",
|
via=self.add_via_stack_center(from_layer=self.input_layer,
|
||||||
to_layer="m2",
|
to_layer=self.bus_layer,
|
||||||
offset=in_pos)
|
offset=in_pos)
|
||||||
|
# Create the input pin at this location on the rail
|
||||||
|
self.add_layout_pin_rect_center(text=in_pin,
|
||||||
|
layer=self.bus_layer,
|
||||||
|
offset=in_pos,
|
||||||
|
height=via.mod.second_layer_height,
|
||||||
|
width=via.mod.second_layer_width)
|
||||||
|
|
||||||
def route_and_to_rails(self):
|
def route_and_to_rails(self):
|
||||||
# This 2D array defines the connection mapping
|
# This 2D array defines the connection mapping
|
||||||
|
|
@ -231,43 +291,67 @@ class hierarchical_predecode(design.design):
|
||||||
pin = self.and_inst[k].get_pin(gate_pin)
|
pin = self.and_inst[k].get_pin(gate_pin)
|
||||||
pin_pos = pin.center()
|
pin_pos = pin.center()
|
||||||
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
||||||
self.add_path("m1", [rail_pos, pin_pos])
|
self.add_path(self.input_layer, [rail_pos, pin_pos])
|
||||||
self.add_via_stack_center(from_layer="m1",
|
self.add_via_stack_center(from_layer=self.input_layer,
|
||||||
to_layer="m2",
|
to_layer=self.bus_layer,
|
||||||
offset=rail_pos)
|
offset=rail_pos,
|
||||||
|
directions=self.bus_directions)
|
||||||
if gate_pin == "A":
|
if gate_pin == "A":
|
||||||
direction = None
|
direction = None
|
||||||
else:
|
else:
|
||||||
direction = ("H", "H")
|
direction = ("H", "H")
|
||||||
|
|
||||||
self.add_via_stack_center(from_layer=pin.layer,
|
self.add_via_stack_center(from_layer=pin.layer,
|
||||||
to_layer="m1",
|
to_layer=self.input_layer,
|
||||||
offset=pin_pos,
|
offset=pin_pos,
|
||||||
directions=direction)
|
directions=direction)
|
||||||
|
|
||||||
def route_vdd_gnd(self):
|
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
|
# In s8, we use hand-made decoder cells with vertical power
|
||||||
in_xoffset = self.in_inst[0].rx() + self.m1_space
|
if OPTS.tech_name == "s8":
|
||||||
# out_xoffset = self.and_inst[0].cx() + self.m1_space
|
|
||||||
for num in range(0, self.number_of_outputs):
|
|
||||||
# this will result in duplicate polygons for rails, but who cares
|
|
||||||
|
|
||||||
# Route both supplies
|
|
||||||
for n in ["vdd", "gnd"]:
|
for n in ["vdd", "gnd"]:
|
||||||
and_pin = self.and_inst[num].get_pin(n)
|
# This makes a wire from top to bottom for both inv and and gates
|
||||||
supply_offset = and_pin.ll().scale(0, 1)
|
for i in [self.inv_inst, self.and_inst]:
|
||||||
self.add_rect(layer=and_pin.layer,
|
bot_pins = i[0].get_pins(n)
|
||||||
offset=supply_offset,
|
top_pins = i[-1].get_pins(n)
|
||||||
width=self.and_inst[num].rx())
|
for (bot_pin, top_pin) in zip(bot_pins, top_pins):
|
||||||
|
self.add_rect(layer=bot_pin.layer,
|
||||||
|
offset=vector(bot_pin.lx(), self.bus_pitch),
|
||||||
|
width=bot_pin.width(),
|
||||||
|
height=top_pin.uy() - self.bus_pitch)
|
||||||
|
# This adds power vias at the top of each cell
|
||||||
|
# (except the last to keep them inside the boundary)
|
||||||
|
for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]:
|
||||||
|
pins = i.get_pins(n)
|
||||||
|
for pin in pins:
|
||||||
|
self.add_power_pin(name=n,
|
||||||
|
loc=pin.uc(),
|
||||||
|
start_layer=pin.layer)
|
||||||
|
self.add_power_pin(name=n,
|
||||||
|
loc=pin.uc(),
|
||||||
|
start_layer=pin.layer)
|
||||||
|
|
||||||
|
# In other techs, we are using standard cell decoder cells with horizontal power
|
||||||
|
else:
|
||||||
|
for num in range(0, self.number_of_outputs):
|
||||||
|
|
||||||
# Add pins in two locations
|
# Route both supplies
|
||||||
for xoffset in [in_xoffset]:
|
for n in ["vdd", "gnd"]:
|
||||||
pin_pos = vector(xoffset, and_pin.cy())
|
and_pins = self.and_inst[num].get_pins(n)
|
||||||
self.add_power_pin(name=n,
|
for and_pin in and_pins:
|
||||||
loc=pin_pos,
|
self.add_segment_center(layer=and_pin.layer,
|
||||||
start_layer=and_pin.layer)
|
start=vector(0, and_pin.cy()),
|
||||||
|
end=vector(self.width, and_pin.cy()))
|
||||||
|
|
||||||
|
# Add pins in two locations
|
||||||
|
for xoffset in [self.inv_inst[0].lx() - self.bus_space,
|
||||||
|
self.and_inst[0].lx() - self.bus_space]:
|
||||||
|
pin_pos = vector(xoffset, and_pin.cy())
|
||||||
|
self.add_power_pin(name=n,
|
||||||
|
loc=pin_pos,
|
||||||
|
start_layer=and_pin.layer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,10 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from tech import drc
|
|
||||||
import debug
|
|
||||||
import design
|
|
||||||
from vector import vector
|
|
||||||
from hierarchical_predecode import hierarchical_predecode
|
from hierarchical_predecode import hierarchical_predecode
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_predecode2x4(hierarchical_predecode):
|
class hierarchical_predecode2x4(hierarchical_predecode):
|
||||||
"""
|
"""
|
||||||
Pre 2x4 decoder used in hierarchical_decoder.
|
Pre 2x4 decoder used in hierarchical_decoder.
|
||||||
|
|
@ -33,21 +30,6 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
||||||
["in_0", "in_1", "out_3", "vdd", "gnd"]]
|
["in_0", "in_1", "out_3", "vdd", "gnd"]]
|
||||||
self.create_and_array(connections)
|
self.create_and_array(connections)
|
||||||
|
|
||||||
def create_layout(self):
|
|
||||||
""" The general organization is from left to right:
|
|
||||||
1) a set of M2 rails for input signals
|
|
||||||
2) a set of inverters to invert input signals
|
|
||||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
|
||||||
4) a set of AND gates for inversion
|
|
||||||
"""
|
|
||||||
self.setup_layout_constraints()
|
|
||||||
self.route_rails()
|
|
||||||
self.place_input_inverters()
|
|
||||||
self.place_and_array()
|
|
||||||
self.route()
|
|
||||||
self.add_boundary()
|
|
||||||
self.DRC_LVS()
|
|
||||||
|
|
||||||
def get_and_input_line_combination(self):
|
def get_and_input_line_combination(self):
|
||||||
""" These are the decoder connections of the AND gates to the A,B pins """
|
""" These are the decoder connections of the AND gates to the A,B pins """
|
||||||
combination = [["Abar_0", "Abar_1"],
|
combination = [["Abar_0", "Abar_1"],
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,10 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from tech import drc
|
|
||||||
import debug
|
|
||||||
import design
|
|
||||||
from vector import vector
|
|
||||||
from hierarchical_predecode import hierarchical_predecode
|
from hierarchical_predecode import hierarchical_predecode
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_predecode3x8(hierarchical_predecode):
|
class hierarchical_predecode3x8(hierarchical_predecode):
|
||||||
"""
|
"""
|
||||||
Pre 3x8 decoder used in hierarchical_decoder.
|
Pre 3x8 decoder used in hierarchical_decoder.
|
||||||
|
|
@ -37,22 +34,6 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
||||||
["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
|
["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
|
||||||
self.create_and_array(connections)
|
self.create_and_array(connections)
|
||||||
|
|
||||||
def create_layout(self):
|
|
||||||
"""
|
|
||||||
The general organization is from left to right:
|
|
||||||
1) a set of M2 rails for input signals
|
|
||||||
2) a set of inverters to invert input signals
|
|
||||||
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
|
|
||||||
4) a set of NAND gates for inversion
|
|
||||||
"""
|
|
||||||
self.setup_layout_constraints()
|
|
||||||
self.route_rails()
|
|
||||||
self.place_input_inverters()
|
|
||||||
self.place_and_array()
|
|
||||||
self.route()
|
|
||||||
self.add_boundary()
|
|
||||||
self.DRC_LVS()
|
|
||||||
|
|
||||||
def get_and_input_line_combination(self):
|
def get_and_input_line_combination(self):
|
||||||
""" These are the decoder connections of the NAND gates to the A,B,C pins """
|
""" These are the decoder connections of the NAND gates to the A,B,C pins """
|
||||||
combination = [["Abar_0", "Abar_1", "Abar_2"],
|
combination = [["Abar_0", "Abar_1", "Abar_2"],
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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.
|
||||||
|
#
|
||||||
|
from hierarchical_predecode import hierarchical_predecode
|
||||||
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
|
class hierarchical_predecode4x16(hierarchical_predecode):
|
||||||
|
"""
|
||||||
|
Pre 4x16 decoder used in hierarchical_decoder.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, height=None):
|
||||||
|
hierarchical_predecode.__init__(self, name, 4, height)
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
self.create_layout()
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
self.add_pins()
|
||||||
|
self.add_modules()
|
||||||
|
self.create_input_inverters()
|
||||||
|
connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"],
|
||||||
|
["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"],
|
||||||
|
["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"],
|
||||||
|
["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"],
|
||||||
|
["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"],
|
||||||
|
["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"],
|
||||||
|
["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"],
|
||||||
|
["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"],
|
||||||
|
["inbar_0", "inbar_1", "inbar_2", "in_3", "out_0", "vdd", "gnd"],
|
||||||
|
["in_0", "inbar_1", "inbar_2", "in_3", "out_1", "vdd", "gnd"],
|
||||||
|
["inbar_0", "in_1", "inbar_2", "in_3", "out_2", "vdd", "gnd"],
|
||||||
|
["in_0", "in_1", "inbar_2", "in_3", "out_3", "vdd", "gnd"],
|
||||||
|
["inbar_0", "inbar_1", "in_2", "in_3", "out_4", "vdd", "gnd"],
|
||||||
|
["in_0", "inbar_1", "in_2", "in_3", "out_5", "vdd", "gnd"],
|
||||||
|
["inbar_0", "in_1", "in_2", "in_3", "out_6", "vdd", "gnd"],
|
||||||
|
["in_0", "in_1", "in_2", "in_3", "out_7", "vdd", "gnd"] ]
|
||||||
|
|
||||||
|
self.create_and_array(connections)
|
||||||
|
|
||||||
|
def get_and_input_line_combination(self):
|
||||||
|
""" These are the decoder connections of the AND gates to the A,B pins """
|
||||||
|
combination = [["Abar_0", "Abar_1", "Abar_2", "Abar_3"],
|
||||||
|
["A_0", "Abar_1", "Abar_2", "Abar_3"],
|
||||||
|
["Abar_0", "A_1", "Abar_2", "Abar_3"],
|
||||||
|
["A_0", "A_1", "Abar_2", "Abar_3"],
|
||||||
|
["Abar_0", "Abar_1", "A_2" , "Abar_3"],
|
||||||
|
["A_0", "Abar_1", "A_2" , "Abar_3"],
|
||||||
|
["Abar_0", "A_1", "A_2" , "Abar_3"],
|
||||||
|
["A_0", "A_1", "A_2" , "Abar_3"],
|
||||||
|
["Abar_0", "Abar_1", "Abar_2", "A_3"],
|
||||||
|
["A_0", "Abar_1", "Abar_2", "A_3"],
|
||||||
|
["Abar_0", "A_1", "Abar_2", "A_3"],
|
||||||
|
["A_0", "A_1", "Abar_2", "A_3"],
|
||||||
|
["Abar_0", "Abar_1", "A_2", "A_3"],
|
||||||
|
["A_0", "Abar_1", "A_2", "A_3"],
|
||||||
|
["Abar_0", "A_1", "A_2", "A_3"],
|
||||||
|
["A_0", "A_1", "A_2", "A_3"]]
|
||||||
|
return combination
|
||||||
|
|
@ -8,7 +8,7 @@ import debug
|
||||||
import design
|
import design
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
|
from tech import layer
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -41,6 +41,10 @@ class port_address(design.design):
|
||||||
self.create_wordline_driver()
|
self.create_wordline_driver()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
|
if "li" in layer:
|
||||||
|
self.route_layer = "li"
|
||||||
|
else:
|
||||||
|
self.route_layer = "m1"
|
||||||
self.place_instances()
|
self.place_instances()
|
||||||
self.route_layout()
|
self.route_layout()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
@ -85,11 +89,19 @@ class port_address(design.design):
|
||||||
def route_internal(self):
|
def route_internal(self):
|
||||||
for row in range(self.num_rows):
|
for row in range(self.num_rows):
|
||||||
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
|
||||||
decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc()
|
decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row))
|
||||||
driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc()
|
decoder_out_pos = decoder_out_pin.rc()
|
||||||
mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0)
|
driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row))
|
||||||
mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1)
|
driver_in_pos = driver_in_pin.lc()
|
||||||
self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos])
|
self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos, var_offset=0.3)
|
||||||
|
|
||||||
|
self.add_via_stack_center(from_layer=decoder_out_pin.layer,
|
||||||
|
to_layer=self.route_layer,
|
||||||
|
offset=decoder_out_pos)
|
||||||
|
|
||||||
|
self.add_via_stack_center(from_layer=driver_in_pin.layer,
|
||||||
|
to_layer=self.route_layer,
|
||||||
|
offset=driver_in_pos)
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
|
|
||||||
|
|
@ -97,7 +109,7 @@ class port_address(design.design):
|
||||||
num_outputs=self.num_rows)
|
num_outputs=self.num_rows)
|
||||||
self.add_mod(self.row_decoder)
|
self.add_mod(self.row_decoder)
|
||||||
|
|
||||||
self.wordline_driver = factory.create(module_type="wordline_driver",
|
self.wordline_driver = factory.create(module_type="wordline_driver_array",
|
||||||
rows=self.num_rows,
|
rows=self.num_rows,
|
||||||
cols=self.num_cols)
|
cols=self.num_cols)
|
||||||
self.add_mod(self.wordline_driver)
|
self.add_mod(self.wordline_driver)
|
||||||
|
|
@ -139,7 +151,6 @@ class port_address(design.design):
|
||||||
|
|
||||||
row_decoder_offset = vector(0, 0)
|
row_decoder_offset = vector(0, 0)
|
||||||
wordline_driver_offset = vector(self.row_decoder.width, 0)
|
wordline_driver_offset = vector(self.row_decoder.width, 0)
|
||||||
|
|
||||||
self.wordline_driver_inst.place(wordline_driver_offset)
|
self.wordline_driver_inst.place(wordline_driver_offset)
|
||||||
self.row_decoder_inst.place(row_decoder_offset)
|
self.row_decoder_inst.place(row_decoder_offset)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class port_data(design.design):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, sram_config, port, name=""):
|
def __init__(self, sram_config, port, name=""):
|
||||||
|
|
||||||
sram_config.set_local_config(self)
|
sram_config.set_local_config(self)
|
||||||
self.port = port
|
self.port = port
|
||||||
if self.write_size is not None:
|
if self.write_size is not None:
|
||||||
|
|
@ -189,13 +189,17 @@ class port_data(design.design):
|
||||||
|
|
||||||
# Extra column +1 is for RBL
|
# Extra column +1 is for RBL
|
||||||
# Precharge will be shifted left if needed
|
# Precharge will be shifted left if needed
|
||||||
|
# Column offset is set to port so extra column can be on left or right
|
||||||
|
# and mirroring happens correctly
|
||||||
self.precharge_array = factory.create(module_type="precharge_array",
|
self.precharge_array = factory.create(module_type="precharge_array",
|
||||||
columns=self.num_cols + self.num_spare_cols + 1,
|
columns=self.num_cols + self.num_spare_cols + 1,
|
||||||
bitcell_bl=self.bl_names[self.port],
|
bitcell_bl=self.bl_names[self.port],
|
||||||
bitcell_br=self.br_names[self.port])
|
bitcell_br=self.br_names[self.port],
|
||||||
|
column_offset=self.port - 1)
|
||||||
self.add_mod(self.precharge_array)
|
self.add_mod(self.precharge_array)
|
||||||
|
|
||||||
if self.port in self.read_ports:
|
if self.port in self.read_ports:
|
||||||
|
# RBLs don't get a sense amp
|
||||||
self.sense_amp_array = factory.create(module_type="sense_amp_array",
|
self.sense_amp_array = factory.create(module_type="sense_amp_array",
|
||||||
word_size=self.word_size,
|
word_size=self.word_size,
|
||||||
words_per_row=self.words_per_row,
|
words_per_row=self.words_per_row,
|
||||||
|
|
@ -205,6 +209,7 @@ class port_data(design.design):
|
||||||
self.sense_amp_array = None
|
self.sense_amp_array = None
|
||||||
|
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
|
# RBLs dont get a col mux
|
||||||
self.column_mux_array = factory.create(module_type="column_mux_array",
|
self.column_mux_array = factory.create(module_type="column_mux_array",
|
||||||
columns=self.num_cols,
|
columns=self.num_cols,
|
||||||
word_size=self.word_size,
|
word_size=self.word_size,
|
||||||
|
|
@ -215,6 +220,7 @@ class port_data(design.design):
|
||||||
self.column_mux_array = None
|
self.column_mux_array = None
|
||||||
|
|
||||||
if self.port in self.write_ports:
|
if self.port in self.write_ports:
|
||||||
|
# RBLs dont get a write driver
|
||||||
self.write_driver_array = factory.create(module_type="write_driver_array",
|
self.write_driver_array = factory.create(module_type="write_driver_array",
|
||||||
columns=self.num_cols,
|
columns=self.num_cols,
|
||||||
word_size=self.word_size,
|
word_size=self.word_size,
|
||||||
|
|
@ -222,11 +228,11 @@ class port_data(design.design):
|
||||||
num_spare_cols=self.num_spare_cols)
|
num_spare_cols=self.num_spare_cols)
|
||||||
self.add_mod(self.write_driver_array)
|
self.add_mod(self.write_driver_array)
|
||||||
if self.write_size is not None:
|
if self.write_size is not None:
|
||||||
|
# RBLs don't get a write mask
|
||||||
self.write_mask_and_array = factory.create(module_type="write_mask_and_array",
|
self.write_mask_and_array = factory.create(module_type="write_mask_and_array",
|
||||||
columns=self.num_cols,
|
columns=self.num_cols,
|
||||||
word_size=self.word_size,
|
word_size=self.word_size,
|
||||||
write_size=self.write_size,
|
write_size=self.write_size)
|
||||||
port = self.port)
|
|
||||||
self.add_mod(self.write_mask_and_array)
|
self.add_mod(self.write_mask_and_array)
|
||||||
else:
|
else:
|
||||||
self.write_mask_and_array = None
|
self.write_mask_and_array = None
|
||||||
|
|
@ -259,12 +265,6 @@ class port_data(design.design):
|
||||||
self.precharge = factory.create(module_type="precharge",
|
self.precharge = factory.create(module_type="precharge",
|
||||||
bitcell_bl=self.bl_names[0],
|
bitcell_bl=self.bl_names[0],
|
||||||
bitcell_br=self.br_names[0])
|
bitcell_br=self.br_names[0])
|
||||||
# We create a dummy here to get bl/br names to add those pins to this
|
|
||||||
# module, which happens before we create the real precharge_array
|
|
||||||
self.precharge_array = factory.create(module_type="precharge_array",
|
|
||||||
columns=self.num_cols + self.num_spare_cols + 1,
|
|
||||||
bitcell_bl=self.bl_names[self.port],
|
|
||||||
bitcell_br=self.br_names[self.port])
|
|
||||||
|
|
||||||
def create_precharge_array(self):
|
def create_precharge_array(self):
|
||||||
""" Creating Precharge """
|
""" Creating Precharge """
|
||||||
|
|
@ -477,7 +477,6 @@ class port_data(design.design):
|
||||||
|
|
||||||
def route_sense_amp_out(self, port):
|
def route_sense_amp_out(self, port):
|
||||||
""" Add pins for the sense amp output """
|
""" Add pins for the sense amp output """
|
||||||
|
|
||||||
for bit in range(self.word_size + self.num_spare_cols):
|
for bit in range(self.word_size + self.num_spare_cols):
|
||||||
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit))
|
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit))
|
||||||
self.add_layout_pin_rect_center(text="dout_{0}".format(bit),
|
self.add_layout_pin_rect_center(text="dout_{0}".format(bit),
|
||||||
|
|
@ -502,45 +501,37 @@ class port_data(design.design):
|
||||||
bank_wmask_name = "bank_wmask_{}".format(bit)
|
bank_wmask_name = "bank_wmask_{}".format(bit)
|
||||||
self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name)
|
self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name)
|
||||||
|
|
||||||
def route_write_mask_and_array_to_write_driver(self,port):
|
def route_write_mask_and_array_to_write_driver(self, port):
|
||||||
""" Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write
|
"""
|
||||||
mask AND array output and via for write driver enable """
|
Routing of wdriver_sel_{} between write mask AND array and
|
||||||
|
write driver array. Adds layout pin for write
|
||||||
|
mask AND array output and via for write driver enable
|
||||||
|
"""
|
||||||
|
|
||||||
inst1 = self.write_mask_and_array_inst
|
wmask_inst = self.write_mask_and_array_inst
|
||||||
inst2 = self.write_driver_array_inst
|
wdriver_inst = self.write_driver_array_inst
|
||||||
|
|
||||||
loc = 0
|
|
||||||
for bit in range(self.num_wmasks):
|
for bit in range(self.num_wmasks):
|
||||||
# Bring write mask AND array output pin to port data level
|
# Bring write mask AND array output pin to port data level
|
||||||
self.copy_layout_pin(inst1, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit))
|
self.copy_layout_pin(wmask_inst, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit))
|
||||||
|
|
||||||
wmask_out_pin = inst1.get_pin("wmask_out_{0}".format(bit))
|
wmask_out_pin = wmask_inst.get_pin("wmask_out_{0}".format(bit))
|
||||||
wdriver_en_pin = inst2.get_pin("en_{0}".format(bit))
|
wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit))
|
||||||
|
|
||||||
# The metal2 wdriver_sel_{} wire must hit the en_{} pin after the closest bitline pin that's right of the
|
wmask_pos = wmask_out_pin.center()
|
||||||
# the wdriver_sel_{} pin in the write driver AND array.
|
wdriver_pos = wdriver_en_pin.rc() - vector(self.m2_pitch, 0)
|
||||||
if bit == 0:
|
mid_pos = vector(wdriver_pos.x, wmask_pos.y)
|
||||||
# When the write mask output pin is right of the bitline, the target is found
|
|
||||||
while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()):
|
|
||||||
loc += 1
|
|
||||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
|
||||||
debug.check(loc<=self.num_wmasks,
|
|
||||||
"Couldn't route the write mask select.")
|
|
||||||
else:
|
|
||||||
# Stride by the write size rather than finding the next pin to the right
|
|
||||||
loc += self.write_size
|
|
||||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
|
||||||
|
|
||||||
beg_pos = wmask_out_pin.center()
|
|
||||||
middle_pos = vector(length, wmask_out_pin.cy())
|
|
||||||
end_pos = vector(length, wdriver_en_pin.cy())
|
|
||||||
|
|
||||||
|
# Add driver on mask output
|
||||||
|
self.add_via_center(layers=self.m1_stack,
|
||||||
|
offset=wmask_pos)
|
||||||
# Add via for the write driver array's enable input
|
# Add via for the write driver array's enable input
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=end_pos)
|
offset=wdriver_pos)
|
||||||
|
|
||||||
# Route between write mask AND array and write driver array
|
# Route between write mask AND array and write driver array
|
||||||
self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos])
|
self.add_wire(self.m1_stack, [wmask_pos, mid_pos, wdriver_pos])
|
||||||
|
|
||||||
def route_column_mux_to_precharge_array(self, port):
|
def route_column_mux_to_precharge_array(self, port):
|
||||||
""" Routing of BL and BR between col mux and precharge array """
|
""" Routing of BL and BR between col mux and precharge array """
|
||||||
|
|
@ -549,15 +540,12 @@ class port_data(design.design):
|
||||||
if self.col_addr_size==0:
|
if self.col_addr_size==0:
|
||||||
return
|
return
|
||||||
|
|
||||||
inst1 = self.column_mux_array_inst
|
start_bit = 1 if self.port == 0 else 0
|
||||||
inst2 = self.precharge_array_inst
|
|
||||||
|
|
||||||
insn2_start_bit = 1 if self.port == 0 else 0
|
self.connect_bitlines(inst1=self.column_mux_array_inst,
|
||||||
|
inst2=self.precharge_array_inst,
|
||||||
self.connect_bitlines(inst1=inst1,
|
|
||||||
inst2=inst2,
|
|
||||||
num_bits=self.num_cols,
|
num_bits=self.num_cols,
|
||||||
inst2_start_bit=insn2_start_bit)
|
inst2_start_bit=start_bit)
|
||||||
|
|
||||||
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
|
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
|
||||||
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||||
|
|
@ -578,13 +566,13 @@ class port_data(design.design):
|
||||||
else:
|
else:
|
||||||
start_bit=0
|
start_bit=0
|
||||||
|
|
||||||
if self.port==0:
|
# spare cols connected to precharge array since they are read independently
|
||||||
off = 1
|
if self.num_spare_cols and self.col_addr_size>0:
|
||||||
else:
|
if self.port==0:
|
||||||
off = 0
|
off = 1
|
||||||
|
else:
|
||||||
|
off = 0
|
||||||
if self.num_spare_cols != 0 and self.col_addr_size>0:
|
|
||||||
self.channel_route_bitlines(inst1=self.column_mux_array_inst,
|
self.channel_route_bitlines(inst1=self.column_mux_array_inst,
|
||||||
inst1_bls_template="{inst}_out_{bit}",
|
inst1_bls_template="{inst}_out_{bit}",
|
||||||
inst2=inst2,
|
inst2=inst2,
|
||||||
|
|
@ -598,15 +586,20 @@ class port_data(design.design):
|
||||||
inst1_start_bit=self.num_cols + off,
|
inst1_start_bit=self.num_cols + off,
|
||||||
inst2_start_bit=self.word_size)
|
inst2_start_bit=self.word_size)
|
||||||
|
|
||||||
|
# This could be a channel route, but in some techs the bitlines
|
||||||
|
# are too close together.
|
||||||
|
elif OPTS.tech_name == "s8":
|
||||||
|
self.connect_bitlines(inst1=inst1,
|
||||||
|
inst1_bls_template=inst1_bls_templ,
|
||||||
|
inst2=inst2,
|
||||||
|
num_bits=self.word_size,
|
||||||
|
inst1_start_bit=start_bit)
|
||||||
else:
|
else:
|
||||||
self.channel_route_bitlines(inst1=inst1,
|
self.channel_route_bitlines(inst1=inst1,
|
||||||
inst1_bls_template=inst1_bls_templ,
|
inst1_bls_template=inst1_bls_templ,
|
||||||
inst2=inst2,
|
inst2=inst2,
|
||||||
num_bits=self.word_size + self.num_spare_cols,
|
num_bits=self.word_size + self.num_spare_cols,
|
||||||
inst1_start_bit=start_bit)
|
inst1_start_bit=start_bit)
|
||||||
|
|
||||||
|
|
||||||
# spare cols connected to precharge array since they are read independently
|
|
||||||
|
|
||||||
def route_write_driver_to_column_mux_or_precharge_array(self, port):
|
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 """
|
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||||
|
|
@ -631,8 +624,13 @@ class port_data(design.design):
|
||||||
else:
|
else:
|
||||||
off = 0
|
off = 0
|
||||||
|
|
||||||
|
# Channel route spare columns' bitlines
|
||||||
if self.num_spare_cols != 0 and self.col_addr_size>0:
|
if self.num_spare_cols and self.col_addr_size>0:
|
||||||
|
if self.port==0:
|
||||||
|
off = 1
|
||||||
|
else:
|
||||||
|
off = 0
|
||||||
|
|
||||||
self.channel_route_bitlines(inst1=self.column_mux_array_inst,
|
self.channel_route_bitlines(inst1=self.column_mux_array_inst,
|
||||||
inst1_bls_template="{inst}_out_{bit}",
|
inst1_bls_template="{inst}_out_{bit}",
|
||||||
inst2=inst2,
|
inst2=inst2,
|
||||||
|
|
@ -646,13 +644,19 @@ class port_data(design.design):
|
||||||
inst1_start_bit=self.num_cols + off,
|
inst1_start_bit=self.num_cols + off,
|
||||||
inst2_start_bit=self.word_size)
|
inst2_start_bit=self.word_size)
|
||||||
|
|
||||||
|
# This could be a channel route, but in some techs the bitlines
|
||||||
|
# are too close together.
|
||||||
|
elif OPTS.tech_name == "s8":
|
||||||
|
self.connect_bitlines(inst1=inst1, inst2=inst2,
|
||||||
|
num_bits=self.word_size,
|
||||||
|
inst1_bls_template=inst1_bls_templ,
|
||||||
|
inst1_start_bit=start_bit)
|
||||||
else:
|
else:
|
||||||
self.channel_route_bitlines(inst1=inst1,
|
self.channel_route_bitlines(inst1=inst1, inst2=inst2,
|
||||||
|
num_bits=self.word_size+self.num_spare_cols,
|
||||||
inst1_bls_template=inst1_bls_templ,
|
inst1_bls_template=inst1_bls_templ,
|
||||||
inst2=inst2,
|
|
||||||
num_bits=self.word_size + self.num_spare_cols,
|
|
||||||
inst1_start_bit=start_bit)
|
inst1_start_bit=start_bit)
|
||||||
|
|
||||||
|
|
||||||
def route_write_driver_to_sense_amp(self, port):
|
def route_write_driver_to_sense_amp(self, port):
|
||||||
""" Routing of BL and BR between write driver and sense amp """
|
""" Routing of BL and BR between write driver and sense amp """
|
||||||
|
|
@ -660,11 +664,11 @@ class port_data(design.design):
|
||||||
inst1 = self.write_driver_array_inst
|
inst1 = self.write_driver_array_inst
|
||||||
inst2 = self.sense_amp_array_inst
|
inst2 = self.sense_amp_array_inst
|
||||||
|
|
||||||
# These should be pitch matched in the cell library,
|
# This could be a channel route, but in some techs the bitlines
|
||||||
# but just in case, do a channel route.
|
# are too close together.
|
||||||
self.channel_route_bitlines(inst1=inst1,
|
self.connect_bitlines(inst1=inst1,
|
||||||
inst2=inst2,
|
inst2=inst2,
|
||||||
num_bits=self.word_size + self.num_spare_cols)
|
num_bits=self.word_size + self.num_spare_cols)
|
||||||
|
|
||||||
def route_bitline_pins(self):
|
def route_bitline_pins(self):
|
||||||
""" Add the bitline pins for the given port """
|
""" Add the bitline pins for the given port """
|
||||||
|
|
@ -787,10 +791,9 @@ class port_data(design.design):
|
||||||
Route the bl and br of two modules using the channel router.
|
Route the bl and br of two modules using the channel router.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bot_inst_group, top_inst_group = self._group_bitline_instances(
|
bot_inst_group, top_inst_group = self._group_bitline_instances(inst1, inst2, num_bits,
|
||||||
inst1, inst2, num_bits,
|
inst1_bls_template, inst1_start_bit,
|
||||||
inst1_bls_template, inst1_start_bit,
|
inst2_bls_template, inst2_start_bit)
|
||||||
inst2_bls_template, inst2_start_bit)
|
|
||||||
|
|
||||||
# Channel route each mux separately since we don't minimize the number
|
# 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!
|
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
||||||
|
|
@ -799,13 +802,8 @@ class port_data(design.design):
|
||||||
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
|
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
|
||||||
top_names = self._get_bitline_pins(top_inst_group, bit)
|
top_names = self._get_bitline_pins(top_inst_group, bit)
|
||||||
|
|
||||||
if bottom_names[0].layer == "m2":
|
|
||||||
bitline_dirs = ("H", "V")
|
|
||||||
elif bottom_names[0].layer == "m1":
|
|
||||||
bitline_dirs = ("V", "H")
|
|
||||||
|
|
||||||
route_map = list(zip(bottom_names, top_names))
|
route_map = list(zip(bottom_names, top_names))
|
||||||
self.create_horizontal_channel_route(route_map, offset, self.m1_stack, bitline_dirs)
|
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
|
||||||
|
|
||||||
def connect_bitlines(self, inst1, inst2, num_bits,
|
def connect_bitlines(self, inst1, inst2, num_bits,
|
||||||
inst1_bls_template="{inst}_{bit}",
|
inst1_bls_template="{inst}_{bit}",
|
||||||
|
|
@ -817,11 +815,10 @@ class port_data(design.design):
|
||||||
This assumes that they have sufficient space to create a jog
|
This assumes that they have sufficient space to create a jog
|
||||||
in the middle between the two modules (if needed).
|
in the middle between the two modules (if needed).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bot_inst_group, top_inst_group = self._group_bitline_instances(
|
bot_inst_group, top_inst_group = self._group_bitline_instances(inst1, inst2, num_bits,
|
||||||
inst1, inst2, num_bits,
|
inst1_bls_template, inst1_start_bit,
|
||||||
inst1_bls_template, inst1_start_bit,
|
inst2_bls_template, inst2_start_bit)
|
||||||
inst2_bls_template, inst2_start_bit)
|
|
||||||
|
|
||||||
for col in range(num_bits):
|
for col in range(num_bits):
|
||||||
bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col)
|
bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col)
|
||||||
|
|
@ -829,18 +826,11 @@ class port_data(design.design):
|
||||||
bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc()
|
bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc()
|
||||||
top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc()
|
top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc()
|
||||||
|
|
||||||
yoffset = 0.5 * (top_bl.y + bot_bl.y)
|
layer_pitch = getattr(self, "{}_pitch".format(top_bl_pin.layer))
|
||||||
self.add_path("m2", [bot_bl,
|
self.add_zjog(bot_bl_pin.layer, bot_bl, top_bl, "V", fixed_offset=top_bl_pin.by() - layer_pitch)
|
||||||
vector(bot_bl.x, yoffset),
|
self.add_zjog(bot_br_pin.layer, bot_br, top_br, "V", fixed_offset=top_bl_pin.by() - 2 * layer_pitch)
|
||||||
vector(top_bl.x, yoffset),
|
|
||||||
top_bl])
|
|
||||||
self.add_path("m2", [bot_br,
|
|
||||||
vector(bot_br.x, yoffset),
|
|
||||||
vector(top_br.x, yoffset),
|
|
||||||
top_br])
|
|
||||||
|
|
||||||
def graph_exclude_precharge(self):
|
def graph_exclude_precharge(self):
|
||||||
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
|
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
|
||||||
if self.precharge_array_inst:
|
if self.precharge_array_inst:
|
||||||
self.graph_inst_exclude.add(self.precharge_array_inst)
|
self.graph_inst_exclude.add(self.precharge_array_inst)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
#
|
#
|
||||||
import design
|
import design
|
||||||
import debug
|
import debug
|
||||||
from tech import drc
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
@ -19,7 +18,7 @@ class precharge_array(design.design):
|
||||||
of bit line columns, height is the height of the bit-cell array.
|
of bit line columns, height is the height of the bit-cell array.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br"):
|
def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0):
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(1, "Creating {0}".format(self.name))
|
debug.info(1, "Creating {0}".format(self.name))
|
||||||
self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br))
|
self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br))
|
||||||
|
|
@ -28,6 +27,7 @@ class precharge_array(design.design):
|
||||||
self.size = size
|
self.size = size
|
||||||
self.bitcell_bl = bitcell_bl
|
self.bitcell_bl = bitcell_bl
|
||||||
self.bitcell_br = bitcell_br
|
self.bitcell_br = bitcell_br
|
||||||
|
self.column_offset = column_offset
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
|
|
@ -106,7 +106,7 @@ class precharge_array(design.design):
|
||||||
xoffset = 0
|
xoffset = 0
|
||||||
for i in range(self.columns):
|
for i in range(self.columns):
|
||||||
tempx = xoffset
|
tempx = xoffset
|
||||||
if cell_properties.bitcell.mirror.y and (i + 1) % 2:
|
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
|
||||||
mirror = "MY"
|
mirror = "MY"
|
||||||
tempx = tempx + self.pc_cell.width
|
tempx = tempx + self.pc_cell.width
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
# See LICENSE for licensing information.
|
# See LICENSE for licensing information.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California
|
# Copyright (c) 2016-2019 Regents of the University of California
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import drc, spice
|
from tech import drc, spice, cell_properties
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
@ -32,22 +32,23 @@ class replica_bitcell_array(design.design):
|
||||||
self.right_rbl = right_rbl
|
self.right_rbl = right_rbl
|
||||||
self.bitcell_ports = bitcell_ports
|
self.bitcell_ports = bitcell_ports
|
||||||
|
|
||||||
debug.check(left_rbl+right_rbl==len(self.all_ports),"Invalid number of RBLs for port configuration.")
|
debug.check(left_rbl + right_rbl == len(self.all_ports),
|
||||||
debug.check(left_rbl+right_rbl==len(self.bitcell_ports),"Bitcell ports must match total RBLs.")
|
"Invalid number of RBLs for port configuration.")
|
||||||
|
debug.check(left_rbl + right_rbl == len(self.bitcell_ports),
|
||||||
|
"Bitcell ports must match total RBLs.")
|
||||||
|
|
||||||
# Two dummy rows/cols plus replica for each port
|
# Two dummy rows/cols plus replica for each port
|
||||||
self.extra_rows = 2 + left_rbl + right_rbl
|
self.extra_rows = 2 + left_rbl + right_rbl
|
||||||
self.extra_cols = 2 + left_rbl + right_rbl
|
self.extra_cols = 2 + left_rbl + right_rbl
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
# We don't offset this because we need to align
|
# We don't offset this because we need to align
|
||||||
# the replica bitcell in the control logic
|
# the replica bitcell in the control logic
|
||||||
#self.offset_all_coordinates()
|
# self.offset_all_coordinates()
|
||||||
|
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
""" Create and connect the netlist """
|
""" Create and connect the netlist """
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
|
@ -55,15 +56,15 @@ class replica_bitcell_array(design.design):
|
||||||
self.create_instances()
|
self.create_instances()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Array and dummy/replica columns
|
""" Array and dummy/replica columns
|
||||||
|
|
||||||
d or D = dummy cell (caps to distinguish grouping)
|
d or D = dummy cell (caps to distinguish grouping)
|
||||||
r or R = replica cell (caps to distinguish grouping)
|
r or R = replica cell (caps to distinguish grouping)
|
||||||
b or B = bitcell
|
b or B = bitcell
|
||||||
replica columns 1
|
replica columns 1
|
||||||
v v
|
v v
|
||||||
bdDDDDDDDDDDDDDDdb <- Dummy row
|
bdDDDDDDDDDDDDDDdb <- Dummy row
|
||||||
bdDDDDDDDDDDDDDDrb <- Dummy row
|
bdDDDDDDDDDDDDDDrb <- Dummy row
|
||||||
br--------------rb
|
br--------------rb
|
||||||
br| Array |rb
|
br| Array |rb
|
||||||
br| row x col |rb
|
br| row x col |rb
|
||||||
|
|
@ -90,15 +91,17 @@ class replica_bitcell_array(design.design):
|
||||||
|
|
||||||
# Replica bitlines
|
# Replica bitlines
|
||||||
self.replica_columns = {}
|
self.replica_columns = {}
|
||||||
for bit in range(self.left_rbl+self.right_rbl):
|
for bit in range(self.left_rbl + self.right_rbl):
|
||||||
|
# Creating left_rbl
|
||||||
if bit<self.left_rbl:
|
if bit<self.left_rbl:
|
||||||
replica_bit = bit+1
|
replica_bit = bit + 1
|
||||||
# dummy column
|
# dummy column
|
||||||
column_offset = 1
|
column_offset = self.left_rbl - bit
|
||||||
|
# Creating right_rbl
|
||||||
else:
|
else:
|
||||||
replica_bit = bit+self.row_size+1
|
replica_bit = bit + self.row_size + 1
|
||||||
# dummy column + replica column + bitcell colums
|
# dummy column + replica column + bitcell colums
|
||||||
column_offset = 3 + self.row_size
|
column_offset = self.left_rbl - bit + self.row_size
|
||||||
self.replica_columns[bit] = factory.create(module_type="replica_column",
|
self.replica_columns[bit] = factory.create(module_type="replica_column",
|
||||||
rows=self.row_size,
|
rows=self.row_size,
|
||||||
left_rbl=self.left_rbl,
|
left_rbl=self.left_rbl,
|
||||||
|
|
@ -106,7 +109,7 @@ class replica_bitcell_array(design.design):
|
||||||
column_offset=column_offset,
|
column_offset=column_offset,
|
||||||
replica_bit=replica_bit)
|
replica_bit=replica_bit)
|
||||||
self.add_mod(self.replica_columns[bit])
|
self.add_mod(self.replica_columns[bit])
|
||||||
|
|
||||||
# Dummy row
|
# Dummy row
|
||||||
self.dummy_row = factory.create(module_type="dummy_array",
|
self.dummy_row = factory.create(module_type="dummy_array",
|
||||||
cols=self.column_size,
|
cols=self.column_size,
|
||||||
|
|
@ -116,57 +119,74 @@ class replica_bitcell_array(design.design):
|
||||||
mirror=0)
|
mirror=0)
|
||||||
self.add_mod(self.dummy_row)
|
self.add_mod(self.dummy_row)
|
||||||
|
|
||||||
# Dummy col (mirror starting at first if odd replica+dummy rows)
|
# If there are bitcell end caps, replace the dummy cells on the edge of the bitcell array with end caps.
|
||||||
self.dummy_col_left = factory.create(module_type="dummy_array",
|
try:
|
||||||
|
end_caps_enabled = cell_properties.bitcell.end_caps
|
||||||
|
except AttributeError:
|
||||||
|
end_caps_enabled = False
|
||||||
|
|
||||||
|
# Dummy Row or Col Cap, depending on bitcell array properties
|
||||||
|
edge_row_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array")
|
||||||
|
|
||||||
|
self.edge_row = factory.create(module_type=edge_row_module_type,
|
||||||
|
cols=self.column_size,
|
||||||
|
rows=1,
|
||||||
|
# dummy column + left replica column(s)
|
||||||
|
column_offset=1 + self.left_rbl,
|
||||||
|
mirror=0)
|
||||||
|
self.add_mod(self.edge_row)
|
||||||
|
|
||||||
|
# Dummy Col or Row Cap, depending on bitcell array properties
|
||||||
|
edge_col_module_type = ("row_cap_array" if end_caps_enabled else "dummy_array")
|
||||||
|
|
||||||
|
self.edge_col_left = factory.create(module_type=edge_col_module_type,
|
||||||
cols=1,
|
cols=1,
|
||||||
column_offset=0,
|
column_offset=0,
|
||||||
rows=self.row_size + self.extra_rows,
|
rows=self.row_size + self.extra_rows,
|
||||||
mirror=(self.left_rbl+1)%2)
|
mirror=(self.left_rbl + 1) % 2)
|
||||||
self.add_mod(self.dummy_col_left)
|
self.add_mod(self.edge_col_left)
|
||||||
|
|
||||||
self.dummy_col_right = factory.create(module_type="dummy_array",
|
self.edge_col_right = factory.create(module_type=edge_col_module_type,
|
||||||
cols=1,
|
cols=1,
|
||||||
# dummy column
|
# dummy column
|
||||||
# + left replica column
|
# + left replica column(s)
|
||||||
# + bitcell columns
|
# + bitcell columns
|
||||||
# + right replica column
|
# + right replica column(s)
|
||||||
column_offset=1 + self.left_rbl + self.column_size + self.right_rbl,
|
column_offset = 1 + self.left_rbl + self.column_size + self.right_rbl,
|
||||||
rows=self.row_size + self.extra_rows,
|
rows=self.row_size + self.extra_rows,
|
||||||
mirror=(self.left_rbl+1)%2)
|
mirror=(self.left_rbl + 1) %2)
|
||||||
self.add_mod(self.dummy_col_right)
|
self.add_mod(self.edge_col_right)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.bitcell_array_wl_names = self.bitcell_array.get_all_wordline_names()
|
self.bitcell_array_wl_names = self.bitcell_array.get_all_wordline_names()
|
||||||
self.bitcell_array_bl_names = self.bitcell_array.get_all_bitline_names()
|
self.bitcell_array_bl_names = self.bitcell_array.get_all_bitline_names()
|
||||||
|
|
||||||
# These are the non-indexed names
|
# These are the non-indexed names
|
||||||
self.dummy_cell_wl_names = ["dummy_"+x for x in self.cell.get_all_wl_names()]
|
self.dummy_cell_wl_names = ["dummy_" + x for x in self.cell.get_all_wl_names()]
|
||||||
self.dummy_cell_bl_names = ["dummy_"+x for x in self.cell.get_all_bitline_names()]
|
self.dummy_cell_bl_names = ["dummy_" + x for x in self.cell.get_all_bitline_names()]
|
||||||
self.dummy_row_bl_names = self.bitcell_array_bl_names
|
self.dummy_row_bl_names = self.bitcell_array_bl_names
|
||||||
|
|
||||||
# A dictionary because some ports may have nothing
|
# A dictionary because some ports may have nothing
|
||||||
self.rbl_bl_names = {}
|
self.rbl_bl_names = {}
|
||||||
self.rbl_br_names = {}
|
self.rbl_br_names = {}
|
||||||
self.rbl_wl_names = {}
|
self.rbl_wl_names = {}
|
||||||
|
|
||||||
# Create the full WL names include dummy, replica, and regular bit cells
|
# Create the full WL names include dummy, replica, and regular bit cells
|
||||||
self.replica_col_wl_names = []
|
self.replica_col_wl_names = []
|
||||||
self.replica_col_wl_names.extend(["{0}_bot".format(x) for x in self.dummy_cell_wl_names])
|
self.replica_col_wl_names.extend(["{0}_bot".format(x) for x in self.dummy_cell_wl_names])
|
||||||
# Left port WLs (one dummy for each port when we allow >1 port)
|
# Left port WLs (one dummy for each port when we allow >1 port)
|
||||||
for port in range(self.left_rbl):
|
for port in range(self.left_rbl):
|
||||||
# Make names for all RBLs
|
# Make names for all RBLs
|
||||||
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))]
|
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))]
|
||||||
# Keep track of the pin that is the RBL
|
# Keep track of the pin that is the RBL
|
||||||
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
|
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
|
||||||
self.replica_col_wl_names.extend(wl_names)
|
self.replica_col_wl_names.extend(wl_names)
|
||||||
# Regular WLs
|
# Regular WLs
|
||||||
self.replica_col_wl_names.extend(self.bitcell_array_wl_names)
|
self.replica_col_wl_names.extend(self.bitcell_array_wl_names)
|
||||||
# Right port WLs (one dummy for each port when we allow >1 port)
|
# Right port WLs (one dummy for each port when we allow >1 port)
|
||||||
for port in range(self.left_rbl,self.left_rbl+self.right_rbl):
|
for port in range(self.left_rbl, self.left_rbl + self.right_rbl):
|
||||||
# Make names for all RBLs
|
# Make names for all RBLs
|
||||||
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))]
|
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))]
|
||||||
# Keep track of the pin that is the RBL
|
# Keep track of the pin that is the RBL
|
||||||
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
|
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
|
||||||
self.replica_col_wl_names.extend(wl_names)
|
self.replica_col_wl_names.extend(wl_names)
|
||||||
|
|
@ -175,14 +195,13 @@ class replica_bitcell_array(design.design):
|
||||||
# Left/right dummy columns are connected identically to the replica column
|
# Left/right dummy columns are connected identically to the replica column
|
||||||
self.dummy_col_wl_names = self.replica_col_wl_names
|
self.dummy_col_wl_names = self.replica_col_wl_names
|
||||||
|
|
||||||
|
|
||||||
# Per port bitline names
|
# Per port bitline names
|
||||||
self.replica_bl_names = {}
|
self.replica_bl_names = {}
|
||||||
self.replica_wl_names = {}
|
self.replica_wl_names = {}
|
||||||
# Array of all port bitline names
|
# Array of all port bitline names
|
||||||
for port in range(self.left_rbl+self.right_rbl):
|
for port in range(self.left_rbl + self.right_rbl):
|
||||||
left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x),port) for x in range(len(self.all_ports))]
|
left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x), port) for x in range(len(self.all_ports))]
|
||||||
right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x),port) for x in range(len(self.all_ports))]
|
right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x), port) for x in range(len(self.all_ports))]
|
||||||
# Keep track of the left pins that are the RBL
|
# Keep track of the left pins that are the RBL
|
||||||
self.rbl_bl_names[port]=left_names[self.bitcell_ports[port]]
|
self.rbl_bl_names[port]=left_names[self.bitcell_ports[port]]
|
||||||
self.rbl_br_names[port]=right_names[self.bitcell_ports[port]]
|
self.rbl_br_names[port]=right_names[self.bitcell_ports[port]]
|
||||||
|
|
@ -190,36 +209,33 @@ class replica_bitcell_array(design.design):
|
||||||
bl_names = [x for t in zip(left_names, right_names) for x in t]
|
bl_names = [x for t in zip(left_names, right_names) for x in t]
|
||||||
self.replica_bl_names[port] = bl_names
|
self.replica_bl_names[port] = bl_names
|
||||||
|
|
||||||
wl_names = ["rbl_{0}_{1}".format(x,port) for x in self.cell.get_all_wl_names()]
|
wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
|
||||||
#wl_names[port] = "rbl_wl{}".format(port)
|
|
||||||
self.replica_wl_names[port] = wl_names
|
self.replica_wl_names[port] = wl_names
|
||||||
|
|
||||||
|
|
||||||
# External pins
|
# External pins
|
||||||
self.add_pin_list(self.bitcell_array_bl_names, "INOUT")
|
self.add_pin_list(self.bitcell_array_bl_names, "INOUT")
|
||||||
# Need to sort by port order since dictionary values may not be in order
|
# Need to sort by port order since dictionary values may not be in order
|
||||||
bl_names = [self.rbl_bl_names[x] for x in sorted(self.rbl_bl_names.keys())]
|
bl_names = [self.rbl_bl_names[x] for x in sorted(self.rbl_bl_names.keys())]
|
||||||
br_names = [self.rbl_br_names[x] for x in sorted(self.rbl_br_names.keys())]
|
br_names = [self.rbl_br_names[x] for x in sorted(self.rbl_br_names.keys())]
|
||||||
for (bl_name,br_name) in zip(bl_names,br_names):
|
for (bl_name, br_name) in zip(bl_names, br_names):
|
||||||
self.add_pin(bl_name,"OUTPUT")
|
self.add_pin(bl_name, "OUTPUT")
|
||||||
self.add_pin(br_name,"OUTPUT")
|
self.add_pin(br_name, "OUTPUT")
|
||||||
self.add_pin_list(self.bitcell_array_wl_names, "INPUT")
|
self.add_pin_list(self.bitcell_array_wl_names, "INPUT")
|
||||||
# Need to sort by port order since dictionary values may not be in order
|
# Need to sort by port order since dictionary values may not be in order
|
||||||
wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())]
|
wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())]
|
||||||
for pin_name in wl_names:
|
for pin_name in wl_names:
|
||||||
self.add_pin(pin_name,"INPUT")
|
self.add_pin(pin_name, "INPUT")
|
||||||
self.add_pin("vdd", "POWER")
|
self.add_pin("vdd", "POWER")
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
|
|
||||||
supplies = ["vdd", "gnd"]
|
supplies = ["vdd", "gnd"]
|
||||||
|
|
||||||
# Used for names/dimensions only
|
# Used for names/dimensions only
|
||||||
self.cell = factory.create(module_type="bitcell")
|
self.cell = factory.create(module_type="bitcell")
|
||||||
|
|
||||||
# Main array
|
# Main array
|
||||||
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
|
||||||
mod=self.bitcell_array)
|
mod=self.bitcell_array)
|
||||||
|
|
@ -227,89 +243,82 @@ class replica_bitcell_array(design.design):
|
||||||
|
|
||||||
# Replica columns
|
# Replica columns
|
||||||
self.replica_col_inst = {}
|
self.replica_col_inst = {}
|
||||||
for port in range(self.left_rbl+self.right_rbl):
|
for port in range(self.left_rbl + self.right_rbl):
|
||||||
self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port),
|
self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port),
|
||||||
mod=self.replica_columns[port])
|
mod=self.replica_columns[port])
|
||||||
self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies)
|
self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies)
|
||||||
|
|
||||||
|
|
||||||
# Dummy rows under the bitcell array (connected with with the replica cell wl)
|
# Dummy rows under the bitcell array (connected with with the replica cell wl)
|
||||||
self.dummy_row_replica_inst = {}
|
self.dummy_row_replica_inst = {}
|
||||||
for port in range(self.left_rbl+self.right_rbl):
|
for port in range(self.left_rbl + self.right_rbl):
|
||||||
self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port),
|
self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port),
|
||||||
mod=self.dummy_row)
|
mod=self.dummy_row)
|
||||||
self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies)
|
self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies)
|
||||||
|
|
||||||
|
|
||||||
# Top/bottom dummy rows
|
|
||||||
self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot",
|
|
||||||
mod=self.dummy_row)
|
|
||||||
self.connect_inst(self.dummy_row_bl_names + [x+"_bot" for x in self.dummy_cell_wl_names] + supplies)
|
|
||||||
self.dummy_row_top_inst=self.add_inst(name="dummy_row_top",
|
|
||||||
mod=self.dummy_row)
|
|
||||||
self.connect_inst(self.dummy_row_bl_names + [x+"_top" for x in self.dummy_cell_wl_names] + supplies)
|
|
||||||
|
|
||||||
|
# Top/bottom dummy rows or col caps
|
||||||
|
self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot",
|
||||||
|
mod=self.edge_row)
|
||||||
|
self.connect_inst(self.dummy_row_bl_names + [x + "_bot" for x in self.dummy_cell_wl_names] + supplies)
|
||||||
|
self.dummy_row_top_inst=self.add_inst(name="dummy_row_top",
|
||||||
|
mod=self.edge_row)
|
||||||
|
self.connect_inst(self.dummy_row_bl_names + [x + "_top" for x in self.dummy_cell_wl_names] + supplies)
|
||||||
|
|
||||||
# Left/right Dummy columns
|
# Left/right Dummy columns
|
||||||
self.dummy_col_left_inst=self.add_inst(name="dummy_col_left",
|
self.dummy_col_left_inst=self.add_inst(name="dummy_col_left",
|
||||||
mod=self.dummy_col_left)
|
mod=self.edge_col_left)
|
||||||
self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
|
self.connect_inst([x + "_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
|
||||||
self.dummy_col_right_inst=self.add_inst(name="dummy_col_right",
|
self.dummy_col_right_inst=self.add_inst(name="dummy_col_right",
|
||||||
mod=self.dummy_col_right)
|
mod=self.edge_col_right)
|
||||||
self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
|
self.connect_inst([x + "_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
|
|
||||||
self.height = (self.row_size+self.extra_rows)*self.dummy_row.height
|
self.height = (self.row_size + self.extra_rows) * self.dummy_row.height
|
||||||
self.width = (self.column_size+self.extra_cols)*self.cell.width
|
self.width = (self.column_size + self.extra_cols) * self.cell.width
|
||||||
|
|
||||||
# This is a bitcell x bitcell offset to scale
|
# This is a bitcell x bitcell offset to scale
|
||||||
offset = vector(self.cell.width, self.cell.height)
|
offset = vector(self.cell.width, self.cell.height)
|
||||||
|
|
||||||
self.bitcell_array_inst.place(offset=[0,0])
|
self.bitcell_array_inst.place(offset=[0, 0])
|
||||||
|
|
||||||
# To the left of the bitcell array
|
# To the left of the bitcell array
|
||||||
for bit in range(self.left_rbl):
|
for bit in range(self.left_rbl):
|
||||||
self.replica_col_inst[bit].place(offset=offset.scale(-bit-1,-self.left_rbl-1))
|
self.replica_col_inst[bit].place(offset=offset.scale(-bit - 1, -self.left_rbl - 1))
|
||||||
# To the right of the bitcell array
|
# To the right of the bitcell array
|
||||||
for bit in range(self.right_rbl):
|
for bit in range(self.right_rbl):
|
||||||
self.replica_col_inst[self.left_rbl+bit].place(offset=offset.scale(bit,-self.left_rbl-1)+self.bitcell_array_inst.lr())
|
self.replica_col_inst[self.left_rbl + bit].place(offset=offset.scale(bit, -self.left_rbl - 1) + self.bitcell_array_inst.lr())
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME: These depend on the array size itself
|
||||||
# Far top dummy row (first row above array is NOT flipped)
|
# Far top dummy row (first row above array is NOT flipped)
|
||||||
flip_dummy = self.right_rbl%2
|
flip_dummy = self.right_rbl % 2
|
||||||
odd_rows = self.row_size%2
|
self.dummy_row_top_inst.place(offset=offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul(),
|
||||||
self.dummy_row_top_inst.place(offset=offset.scale(0,self.right_rbl+(flip_dummy ^ odd_rows))+self.bitcell_array_inst.ul(),
|
mirror="MX" if flip_dummy else "R0")
|
||||||
mirror="MX" if (flip_dummy ^ odd_rows) else "R0")
|
# FIXME: These depend on the array size itself
|
||||||
# Far bottom dummy row (first row below array IS flipped)
|
# Far bottom dummy row (first row below array IS flipped)
|
||||||
flip_dummy = (self.left_rbl+1)%2
|
flip_dummy = (self.left_rbl + 1) % 2
|
||||||
self.dummy_row_bot_inst.place(offset=offset.scale(0,-self.left_rbl-1+flip_dummy),
|
self.dummy_row_bot_inst.place(offset=offset.scale(0, -self.left_rbl - 1 + flip_dummy),
|
||||||
mirror="MX" if flip_dummy else "R0")
|
mirror="MX" if flip_dummy else "R0")
|
||||||
# Far left dummy col
|
# Far left dummy col
|
||||||
self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl-1,-self.left_rbl-1))
|
self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl - 1, -self.left_rbl - 1))
|
||||||
# Far right dummy col
|
# Far right dummy col
|
||||||
self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl,-self.left_rbl-1)+self.bitcell_array_inst.lr())
|
self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr())
|
||||||
|
|
||||||
# Replica dummy rows
|
# Replica dummy rows
|
||||||
for bit in range(self.left_rbl):
|
for bit in range(self.left_rbl):
|
||||||
self.dummy_row_replica_inst[bit].place(offset=offset.scale(0,-bit-bit%2),
|
self.dummy_row_replica_inst[bit].place(offset=offset.scale(0, -bit - bit % 2),
|
||||||
mirror="R0" if bit%2 else "MX")
|
mirror="R0" if bit % 2 else "MX")
|
||||||
for bit in range(self.right_rbl):
|
for bit in range(self.right_rbl):
|
||||||
self.dummy_row_replica_inst[self.left_rbl+bit].place(offset=offset.scale(0,bit+bit%2+odd_rows)+self.bitcell_array_inst.ul(),
|
self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(),
|
||||||
mirror="MX" if (bit%2 or odd_rows) else "R0")
|
mirror="MX" if bit % 2 else "R0")
|
||||||
|
|
||||||
|
self.translate_all(offset.scale(-1 - self.left_rbl, -1 - self.left_rbl))
|
||||||
|
|
||||||
self.translate_all(offset.scale(-1-self.left_rbl,-1-self.left_rbl))
|
|
||||||
|
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
""" Add the layout pins """
|
""" Add the layout pins """
|
||||||
|
|
||||||
|
|
@ -322,7 +331,7 @@ class replica_bitcell_array(design.design):
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
self.add_layout_pin(text=pin_name,
|
self.add_layout_pin(text=pin_name,
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
offset=pin.ll().scale(0,1),
|
offset=pin.ll().scale(0, 1),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=pin.height())
|
height=pin.height())
|
||||||
for bitline in self.bitcell_array_bl_names:
|
for bitline in self.bitcell_array_bl_names:
|
||||||
|
|
@ -331,27 +340,26 @@ class replica_bitcell_array(design.design):
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
self.add_layout_pin(text=pin_name,
|
self.add_layout_pin(text=pin_name,
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
offset=pin.ll().scale(1,0),
|
offset=pin.ll().scale(1, 0),
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
|
|
||||||
# Replica wordlines
|
# Replica wordlines
|
||||||
for port in range(self.left_rbl+self.right_rbl):
|
for port in range(self.left_rbl + self.right_rbl):
|
||||||
inst = self.replica_col_inst[port]
|
inst = self.replica_col_inst[port]
|
||||||
for (pin_name,wl_name) in zip(self.cell.get_all_wl_names(),self.replica_wl_names[port]):
|
for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wl_names[port]):
|
||||||
# +1 for dummy row
|
# +1 for dummy row
|
||||||
pin_bit = port+1
|
pin_bit = port + 1
|
||||||
# +row_size if above the array
|
# +row_size if above the array
|
||||||
if port>=self.left_rbl:
|
if port>=self.left_rbl:
|
||||||
pin_bit += self.row_size
|
pin_bit += self.row_size
|
||||||
|
|
||||||
pin_name += "_{}".format(pin_bit)
|
pin_name += "_{}".format(pin_bit)
|
||||||
pin = inst.get_pin(pin_name)
|
pin = inst.get_pin(pin_name)
|
||||||
if wl_name in self.rbl_wl_names.values():
|
if wl_name in self.rbl_wl_names.values():
|
||||||
self.add_layout_pin(text=wl_name,
|
self.add_layout_pin(text=wl_name,
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
offset=pin.ll().scale(0,1),
|
offset=pin.ll().scale(0, 1),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=pin.height())
|
height=pin.height())
|
||||||
|
|
||||||
|
|
@ -369,20 +377,27 @@ class replica_bitcell_array(design.design):
|
||||||
offset=pin.ll().scale(1, 0),
|
offset=pin.ll().scale(1, 0),
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
|
# For specific technologies, there is no vdd via within the bitcell. Instead vdd is connect via end caps.
|
||||||
|
try:
|
||||||
|
bitcell_no_vdd_pin = cell_properties.bitcell.no_vdd_via
|
||||||
|
except AttributeError:
|
||||||
|
bitcell_no_vdd_pin = False
|
||||||
|
|
||||||
for pin_name in ["vdd", "gnd"]:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
for inst in self.insts:
|
for inst in self.insts:
|
||||||
pin_list = inst.get_pins(pin_name)
|
pin_list = inst.get_pins(pin_name)
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
self.add_power_pin(name=pin_name,
|
if not (pin_name == "vdd" and bitcell_no_vdd_pin):
|
||||||
loc=pin.center(),
|
self.add_power_pin(name=pin_name,
|
||||||
directions=("V", "V"),
|
loc=pin.center(),
|
||||||
start_layer=pin.layer)
|
directions=("V", "V"),
|
||||||
|
start_layer=pin.layer)
|
||||||
|
|
||||||
def get_rbl_wl_name(self, port):
|
def get_rbl_wl_name(self, port):
|
||||||
""" Return the WL for the given RBL port """
|
""" Return the WL for the given RBL port """
|
||||||
return self.rbl_wl_names[port]
|
return self.rbl_wl_names[port]
|
||||||
|
|
||||||
def get_rbl_bl_name(self, port):
|
def get_rbl_bl_name(self, port):
|
||||||
""" Return the BL for the given RBL port """
|
""" Return the BL for the given RBL port """
|
||||||
return self.rbl_bl_names[port]
|
return self.rbl_bl_names[port]
|
||||||
|
|
@ -393,19 +408,17 @@ class replica_bitcell_array(design.design):
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Power of Bitcell array and bitline in nW."""
|
"""Power of Bitcell array and bitline in nW."""
|
||||||
from tech import drc, parameter
|
|
||||||
|
|
||||||
# Dynamic Power from Bitline
|
# Dynamic Power from Bitline
|
||||||
bl_wire = self.gen_bl_wire()
|
bl_wire = self.gen_bl_wire()
|
||||||
cell_load = 2 * bl_wire.return_input_cap()
|
cell_load = 2 * bl_wire.return_input_cap()
|
||||||
bl_swing = OPTS.rbl_delay_percentage
|
bl_swing = OPTS.rbl_delay_percentage
|
||||||
freq = spice["default_event_frequency"]
|
freq = spice["default_event_frequency"]
|
||||||
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
|
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
|
||||||
|
|
||||||
#Calculate the bitcell power which currently only includes leakage
|
# Calculate the bitcell power which currently only includes leakage
|
||||||
cell_power = self.cell.analytical_power(corner, load)
|
cell_power = self.cell.analytical_power(corner, load)
|
||||||
|
|
||||||
#Leakage power grows with entire array and bitlines.
|
# Leakage power grows with entire array and bitlines.
|
||||||
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
|
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
|
||||||
cell_power.leakage * self.column_size * self.row_size)
|
cell_power.leakage * self.column_size * self.row_size)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
@ -416,13 +429,13 @@ class replica_bitcell_array(design.design):
|
||||||
else:
|
else:
|
||||||
height = self.height
|
height = self.height
|
||||||
bl_pos = 0
|
bl_pos = 0
|
||||||
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1"))
|
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1"))
|
||||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||||
return bl_wire
|
return bl_wire
|
||||||
|
|
||||||
def get_wordline_cin(self):
|
def get_wordline_cin(self):
|
||||||
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||||
#A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||||
bitcell_wl_cin = self.cell.get_wl_cin()
|
bitcell_wl_cin = self.cell.get_wl_cin()
|
||||||
total_cin = bitcell_wl_cin * self.column_size
|
total_cin = bitcell_wl_cin * self.column_size
|
||||||
return total_cin
|
return total_cin
|
||||||
|
|
@ -430,13 +443,13 @@ class replica_bitcell_array(design.design):
|
||||||
def graph_exclude_bits(self, targ_row, targ_col):
|
def graph_exclude_bits(self, targ_row, targ_col):
|
||||||
"""Excludes bits in column from being added to graph except target"""
|
"""Excludes bits in column from being added to graph except target"""
|
||||||
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
|
self.bitcell_array.graph_exclude_bits(targ_row, targ_col)
|
||||||
|
|
||||||
def graph_exclude_replica_col_bits(self):
|
def graph_exclude_replica_col_bits(self):
|
||||||
"""Exclude all replica/dummy cells in the replica columns except the replica bit."""
|
"""Exclude all replica/dummy cells in the replica columns except the replica bit."""
|
||||||
|
|
||||||
for port in range(self.left_rbl+self.right_rbl):
|
for port in range(self.left_rbl + self.right_rbl):
|
||||||
self.replica_columns[port].exclude_all_but_replica()
|
self.replica_columns[port].exclude_all_but_replica()
|
||||||
|
|
||||||
def get_cell_name(self, inst_name, row, col):
|
def get_cell_name(self, inst_name, row, col):
|
||||||
"""Gets the spice name of the target bitcell."""
|
"""Gets the spice name of the target bitcell."""
|
||||||
return self.bitcell_array.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col)
|
return self.bitcell_array.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name, row, col)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
# See LICENSE for licensing information.
|
# See LICENSE for licensing information.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California
|
# Copyright (c) 2016-2019 Regents of the University of California
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
from tech import drc
|
from tech import cell_properties
|
||||||
import contact
|
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class replica_column(design.design):
|
class replica_column(design.design):
|
||||||
"""
|
"""
|
||||||
Generate a replica bitline column for the replica array.
|
Generate a replica bitline column for the replica array.
|
||||||
Rows is the total number of rows i the main array.
|
Rows is the total number of rows i the main array.
|
||||||
Left_rbl and right_rbl are the number of left and right replica bitlines.
|
Left_rbl and right_rbl are the number of left and right replica bitlines.
|
||||||
Replica bit specifies which replica column this is (to determine where to put the
|
Replica bit specifies which replica column this is (to determine where to put the
|
||||||
replica cell.
|
replica cell.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -29,25 +29,26 @@ class replica_column(design.design):
|
||||||
self.right_rbl = right_rbl
|
self.right_rbl = right_rbl
|
||||||
self.replica_bit = replica_bit
|
self.replica_bit = replica_bit
|
||||||
# left, right, regular rows plus top/bottom dummy cells
|
# left, right, regular rows plus top/bottom dummy cells
|
||||||
self.total_size = self.left_rbl+rows+self.right_rbl+2
|
self.total_size = self.left_rbl + rows + self.right_rbl + 2
|
||||||
self.column_offset = column_offset
|
self.column_offset = column_offset
|
||||||
|
|
||||||
debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.")
|
debug.check(replica_bit != 0 and replica_bit != rows,
|
||||||
debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1,
|
"Replica bit cannot be the dummy row.")
|
||||||
"Replica bit cannot be in the regular array.")
|
debug.check(replica_bit <= left_rbl or replica_bit >= self.total_size - right_rbl - 1,
|
||||||
|
"Replica bit cannot be in the regular array.")
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_instances()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.height = self.total_size*self.cell.height
|
self.height = self.total_size * self.cell.height
|
||||||
self.width = self.cell.width
|
self.width = self.cell.width
|
||||||
|
|
||||||
self.place_instances()
|
self.place_instances()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
@ -55,49 +56,70 @@ class replica_column(design.design):
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
||||||
for bl_name in self.cell.get_all_bitline_names():
|
for bl_name in self.cell.get_all_bitline_names():
|
||||||
# In the replica column, these are only outputs!
|
# In the replica column, these are only outputs!
|
||||||
self.add_pin("{0}_{1}".format(bl_name,0), "OUTPUT")
|
self.add_pin("{0}_{1}".format(bl_name, 0), "OUTPUT")
|
||||||
|
|
||||||
for row in range(self.total_size):
|
for row in range(self.total_size):
|
||||||
for wl_name in self.cell.get_all_wl_names():
|
for wl_name in self.cell.get_all_wl_names():
|
||||||
self.add_pin("{0}_{1}".format(wl_name,row), "INPUT")
|
self.add_pin("{0}_{1}".format(wl_name, row), "INPUT")
|
||||||
|
|
||||||
self.add_pin("vdd", "POWER")
|
self.add_pin("vdd", "POWER")
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.replica_cell = factory.create(module_type="replica_bitcell")
|
self.replica_cell = factory.create(module_type="replica_{}".format(OPTS.bitcell))
|
||||||
self.add_mod(self.replica_cell)
|
self.add_mod(self.replica_cell)
|
||||||
self.dummy_cell = factory.create(module_type="dummy_bitcell")
|
self.dummy_cell = factory.create(module_type="dummy_{}".format(OPTS.bitcell))
|
||||||
self.add_mod(self.dummy_cell)
|
self.add_mod(self.dummy_cell)
|
||||||
|
try:
|
||||||
|
edge_module_type = ("col_cap" if cell_properties.bitcell.end_caps else "dummy")
|
||||||
|
except AttributeError:
|
||||||
|
edge_module_type = "dummy"
|
||||||
|
self.edge_cell = factory.create(module_type=edge_module_type + "_" + OPTS.bitcell)
|
||||||
|
self.add_mod(self.edge_cell)
|
||||||
# Used for pin names only
|
# Used for pin names only
|
||||||
self.cell = factory.create(module_type="bitcell")
|
self.cell = factory.create(module_type="bitcell")
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
|
|
||||||
|
try:
|
||||||
|
end_caps_enabled = cell_properties.bitcell.end_caps
|
||||||
|
except AttributeError:
|
||||||
|
end_caps_enabled = False
|
||||||
|
|
||||||
self.cell_inst = {}
|
self.cell_inst = {}
|
||||||
for row in range(self.total_size):
|
for row in range(self.total_size):
|
||||||
name="rbc_{0}".format(row)
|
name="rbc_{0}".format(row)
|
||||||
# Top/bottom cell are always dummy cells.
|
# Top/bottom cell are always dummy cells.
|
||||||
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl)
|
# Regular array cells are replica cells (>left_rbl and <rows-right_rbl)
|
||||||
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell.
|
# Replic bit specifies which other bit (in the full range (0,rows) to make a replica cell.
|
||||||
if (row>self.left_rbl and row<self.total_size-self.right_rbl-1):
|
if (row > self.left_rbl and row < self.total_size - self.right_rbl - 1):
|
||||||
self.cell_inst[row]=self.add_inst(name=name,
|
self.cell_inst[row]=self.add_inst(name=name,
|
||||||
mod=self.replica_cell)
|
mod=self.replica_cell)
|
||||||
|
self.connect_inst(self.get_bitcell_pins(0, row))
|
||||||
elif row==self.replica_bit:
|
elif row==self.replica_bit:
|
||||||
self.cell_inst[row]=self.add_inst(name=name,
|
self.cell_inst[row]=self.add_inst(name=name,
|
||||||
mod=self.replica_cell)
|
mod=self.replica_cell)
|
||||||
|
self.connect_inst(self.get_bitcell_pins(0, row))
|
||||||
|
elif (row == 0 or row == self.total_size - 1):
|
||||||
|
self.cell_inst[row]=self.add_inst(name=name,
|
||||||
|
mod=self.edge_cell)
|
||||||
|
if end_caps_enabled:
|
||||||
|
self.connect_inst(self.get_bitcell_pins_col_cap(0, row))
|
||||||
|
else:
|
||||||
|
self.connect_inst(self.get_bitcell_pins(0, row))
|
||||||
else:
|
else:
|
||||||
self.cell_inst[row]=self.add_inst(name=name,
|
self.cell_inst[row]=self.add_inst(name=name,
|
||||||
mod=self.dummy_cell)
|
mod=self.dummy_cell)
|
||||||
self.connect_inst(self.get_bitcell_pins(0, row))
|
self.connect_inst(self.get_bitcell_pins(0, row))
|
||||||
|
|
||||||
def place_instances(self):
|
def place_instances(self):
|
||||||
from tech import cell_properties
|
from tech import cell_properties
|
||||||
# Flip the mirrors if we have an odd number of replica+dummy rows at the bottom
|
# Flip the mirrors if we have an odd number of replica+dummy rows at the bottom
|
||||||
# so that we will start with mirroring rather than not mirroring
|
# so that we will start with mirroring rather than not mirroring
|
||||||
rbl_offset = (self.left_rbl+1)%2
|
rbl_offset = (self.left_rbl + 1) %2
|
||||||
|
|
||||||
# if our bitcells are mirrored on the y axis, check if we are in global
|
# if our bitcells are mirrored on the y axis, check if we are in global
|
||||||
# column that needs to be flipped.
|
# column that needs to be flipped.
|
||||||
|
|
@ -108,12 +130,10 @@ class replica_column(design.design):
|
||||||
xoffset = self.replica_cell.width
|
xoffset = self.replica_cell.width
|
||||||
|
|
||||||
for row in range(self.total_size):
|
for row in range(self.total_size):
|
||||||
dir_x = False
|
# name = "bit_r{0}_{1}".format(row, "rbl")
|
||||||
name = "bit_r{0}_{1}".format(row,"rbl")
|
dir_x = cell_properties.bitcell.mirror.x and (row + rbl_offset) % 2
|
||||||
if cell_properties.bitcell.mirror.x and (row+rbl_offset)%2:
|
|
||||||
dir_x = True
|
|
||||||
|
|
||||||
offset = vector(xoffset,self.cell.height*(row+(row+rbl_offset)%2))
|
offset = vector(xoffset, self.cell.height * (row + (row + rbl_offset) % 2))
|
||||||
|
|
||||||
if dir_x and dir_y:
|
if dir_x and dir_y:
|
||||||
dir_key = "XY"
|
dir_key = "XY"
|
||||||
|
|
@ -127,54 +147,77 @@ class replica_column(design.design):
|
||||||
self.cell_inst[row].place(offset=offset,
|
self.cell_inst[row].place(offset=offset,
|
||||||
mirror=dir_key)
|
mirror=dir_key)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
""" Add the layout pins """
|
""" Add the layout pins """
|
||||||
|
|
||||||
for bl_name in self.cell.get_all_bitline_names():
|
for bl_name in self.cell.get_all_bitline_names():
|
||||||
bl_pin = self.cell_inst[0].get_pin(bl_name)
|
bl_pin = self.cell_inst[0].get_pin(bl_name)
|
||||||
self.add_layout_pin(text=bl_name,
|
self.add_layout_pin(text=bl_name,
|
||||||
layer="m2",
|
layer=bl_pin.layer,
|
||||||
offset=bl_pin.ll(),
|
offset=bl_pin.ll(),
|
||||||
width=bl_pin.width(),
|
width=bl_pin.width(),
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
for row in range(self.total_size):
|
try:
|
||||||
|
end_caps_enabled = cell_properties.bitcell.end_caps
|
||||||
|
except AttributeError:
|
||||||
|
end_caps_enabled = False
|
||||||
|
|
||||||
|
if end_caps_enabled:
|
||||||
|
row_range_max = self.total_size - 1
|
||||||
|
row_range_min = 1
|
||||||
|
else:
|
||||||
|
row_range_max = self.total_size
|
||||||
|
row_range_min = 0
|
||||||
|
|
||||||
|
for row in range(row_range_min, row_range_max):
|
||||||
for wl_name in self.cell.get_all_wl_names():
|
for wl_name in self.cell.get_all_wl_names():
|
||||||
wl_pin = self.cell_inst[row].get_pin(wl_name)
|
wl_pin = self.cell_inst[row].get_pin(wl_name)
|
||||||
self.add_layout_pin(text="{0}_{1}".format(wl_name,row),
|
self.add_layout_pin(text="{0}_{1}".format(wl_name, row),
|
||||||
layer="m1",
|
layer=wl_pin.layer,
|
||||||
offset=wl_pin.ll().scale(0,1),
|
offset=wl_pin.ll().scale(0, 1),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=wl_pin.height())
|
height=wl_pin.height())
|
||||||
|
|
||||||
# For every second row and column, add a via for gnd and vdd
|
# For every second row and column, add a via for gnd and vdd
|
||||||
for row in range(self.total_size):
|
for row in range(row_range_min, row_range_max):
|
||||||
inst = self.cell_inst[row]
|
inst = self.cell_inst[row]
|
||||||
for pin_name in ["vdd", "gnd"]:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
self.copy_layout_pin(inst, pin_name)
|
self.copy_layout_pin(inst, pin_name)
|
||||||
|
|
||||||
def get_bitcell_pins(self, col, row):
|
def get_bitcell_pins(self, col, row):
|
||||||
""" Creates a list of connections in the bitcell,
|
""" Creates a list of connections in the bitcell,
|
||||||
indexed by column and row, for instance use in bitcell_array """
|
indexed by column and row, for instance use in bitcell_array """
|
||||||
|
|
||||||
bitcell_pins = []
|
bitcell_pins = []
|
||||||
|
|
||||||
pin_names = self.cell.get_all_bitline_names()
|
pin_names = self.cell.get_all_bitline_names()
|
||||||
for pin in pin_names:
|
for pin in pin_names:
|
||||||
bitcell_pins.append(pin+"_{0}".format(col))
|
bitcell_pins.append(pin + "_{0}".format(col))
|
||||||
pin_names = self.cell.get_all_wl_names()
|
pin_names = self.cell.get_all_wl_names()
|
||||||
for pin in pin_names:
|
for pin in pin_names:
|
||||||
bitcell_pins.append(pin+"_{0}".format(row))
|
bitcell_pins.append(pin + "_{0}".format(row))
|
||||||
bitcell_pins.append("vdd")
|
bitcell_pins.append("vdd")
|
||||||
bitcell_pins.append("gnd")
|
bitcell_pins.append("gnd")
|
||||||
|
|
||||||
return bitcell_pins
|
return bitcell_pins
|
||||||
|
|
||||||
|
def get_bitcell_pins_col_cap(self, col, row):
|
||||||
|
""" Creates a list of connections in the bitcell,
|
||||||
|
indexed by column and row, for instance use in bitcell_array """
|
||||||
|
|
||||||
|
bitcell_pins = []
|
||||||
|
|
||||||
|
pin_names = self.cell.get_all_bitline_names()
|
||||||
|
for pin in pin_names:
|
||||||
|
bitcell_pins.append(pin + "_{0}".format(col))
|
||||||
|
bitcell_pins.append("vdd")
|
||||||
|
|
||||||
|
return bitcell_pins
|
||||||
|
|
||||||
def exclude_all_but_replica(self):
|
def exclude_all_but_replica(self):
|
||||||
"""Excludes all bits except the replica cell (self.replica_bit)."""
|
"""Excludes all bits except the replica cell (self.replica_bit)."""
|
||||||
|
|
||||||
for row, cell in self.cell_inst.items():
|
for row, cell in self.cell_inst.items():
|
||||||
if row != self.replica_bit:
|
if row != self.replica_bit:
|
||||||
self.graph_inst_exclude.add(cell)
|
self.graph_inst_exclude.add(cell)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 Regents of the University of California
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
from bitcell_base_array import bitcell_base_array
|
||||||
|
from sram_factory import factory
|
||||||
|
from globals import OPTS
|
||||||
|
from tech import cell_properties
|
||||||
|
|
||||||
|
class row_cap_array(bitcell_base_array):
|
||||||
|
"""
|
||||||
|
Generate a dummy row/column for the replica array.
|
||||||
|
"""
|
||||||
|
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""):
|
||||||
|
super().__init__(cols, rows, name, column_offset)
|
||||||
|
self.mirror = mirror
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
self.create_layout()
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
""" Create and connect the netlist """
|
||||||
|
self.add_modules()
|
||||||
|
self.add_pins()
|
||||||
|
self.create_instances()
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
|
||||||
|
self.place_array("dummy_r{0}_c{1}", self.mirror)
|
||||||
|
self.add_layout_pins()
|
||||||
|
self.add_boundary()
|
||||||
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
def add_modules(self):
|
||||||
|
""" Add the modules used in this design """
|
||||||
|
self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell))
|
||||||
|
self.add_mod(self.dummy_cell)
|
||||||
|
|
||||||
|
self.cell = factory.create(module_type="bitcell")
|
||||||
|
|
||||||
|
def create_instances(self):
|
||||||
|
""" Create the module instances used in this design """
|
||||||
|
self.cell_inst = {}
|
||||||
|
for col in range(self.column_size):
|
||||||
|
for row in range(1, self.row_size - 1):
|
||||||
|
name = "bit_r{0}_c{1}".format(row, col)
|
||||||
|
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||||
|
mod=self.dummy_cell)
|
||||||
|
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||||
|
|
||||||
|
def get_bitcell_pins(self, col, row):
|
||||||
|
"""
|
||||||
|
Creates a list of connections in the bitcell,
|
||||||
|
indexed by column and row, for instance use in bitcell_array
|
||||||
|
"""
|
||||||
|
|
||||||
|
pin_name = cell_properties.bitcell.cell_1rw1r.pin
|
||||||
|
bitcell_pins = ["{0}_{1}".format(pin_name.wl0, row),
|
||||||
|
"{0}_{1}".format(pin_name.wl1, row),
|
||||||
|
"gnd"]
|
||||||
|
|
||||||
|
return bitcell_pins
|
||||||
|
|
||||||
|
def place_array(self, name_template, row_offset=0):
|
||||||
|
# We increase it by a well enclosure so the precharges don't overlap our wells
|
||||||
|
self.height = self.row_size*self.cell.height
|
||||||
|
self.width = self.column_size*self.cell.width
|
||||||
|
|
||||||
|
xoffset = 0.0
|
||||||
|
for col in range(self.column_size):
|
||||||
|
yoffset = self.cell.height
|
||||||
|
tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset)
|
||||||
|
|
||||||
|
for row in range(1, self.row_size - 1):
|
||||||
|
name = name_template.format(row, col)
|
||||||
|
tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset)
|
||||||
|
|
||||||
|
if dir_x and dir_y:
|
||||||
|
dir_key = "XY"
|
||||||
|
elif dir_x:
|
||||||
|
dir_key = "MX"
|
||||||
|
elif dir_y:
|
||||||
|
dir_key = "MY"
|
||||||
|
else:
|
||||||
|
dir_key = ""
|
||||||
|
|
||||||
|
self.cell_inst[row,col].place(offset=[tempx, tempy],
|
||||||
|
mirror=dir_key)
|
||||||
|
yoffset += self.cell.height
|
||||||
|
xoffset += self.cell.width
|
||||||
|
|
||||||
|
def add_layout_pins(self):
|
||||||
|
""" Add the layout pins """
|
||||||
|
|
||||||
|
row_list = self.cell.get_all_wl_names()
|
||||||
|
|
||||||
|
for row in range(1, self.row_size - 1):
|
||||||
|
for cell_row in row_list:
|
||||||
|
wl_pin = self.cell_inst[row,0].get_pin(cell_row)
|
||||||
|
self.add_layout_pin(text=cell_row+"_{0}".format(row),
|
||||||
|
layer=wl_pin.layer,
|
||||||
|
offset=wl_pin.ll().scale(0,1),
|
||||||
|
width=self.width,
|
||||||
|
height=wl_pin.height())
|
||||||
|
|
||||||
|
# Add vdd/gnd via stacks
|
||||||
|
for row in range(1, self.row_size - 1):
|
||||||
|
for col in range(self.column_size):
|
||||||
|
inst = self.cell_inst[row,col]
|
||||||
|
for pin_name in ["vdd", "gnd"]:
|
||||||
|
for pin in inst.get_pins(pin_name):
|
||||||
|
self.add_power_pin(name=pin.name,
|
||||||
|
loc=pin.center(),
|
||||||
|
start_layer=pin.layer)
|
||||||
|
|
||||||
|
|
||||||
|
# def input_load(self):
|
||||||
|
# wl_wire = self.gen_wl_wire()
|
||||||
|
# return wl_wire.return_input_cap()
|
||||||
|
#
|
||||||
|
# def get_wordline_cin(self):
|
||||||
|
# """Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||||
|
# #A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||||
|
# bitcell_wl_cin = self.cell.get_wl_cin()
|
||||||
|
# total_cin = bitcell_wl_cin * self.column_size
|
||||||
|
# return total_cin
|
||||||
|
|
@ -20,20 +20,23 @@ class sense_amp_array(design.design):
|
||||||
Dynamically generated sense amp array for all bitlines.
|
Dynamically generated sense amp array for all bitlines.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, word_size, words_per_row, num_spare_cols=None):
|
def __init__(self, name, word_size, words_per_row, num_spare_cols=None, column_offset=0):
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(1, "Creating {0}".format(self.name))
|
debug.info(1, "Creating {0}".format(self.name))
|
||||||
self.add_comment("word_size {0}".format(word_size))
|
self.add_comment("word_size {0}".format(word_size))
|
||||||
self.add_comment("words_per_row: {0}".format(words_per_row))
|
self.add_comment("words_per_row: {0}".format(words_per_row))
|
||||||
|
|
||||||
self.word_size = word_size
|
self.word_size = word_size
|
||||||
self.words_per_row = words_per_row
|
self.words_per_row = words_per_row
|
||||||
|
|
||||||
if not num_spare_cols:
|
if not num_spare_cols:
|
||||||
self.num_spare_cols = 0
|
self.num_spare_cols = 0
|
||||||
else:
|
else:
|
||||||
self.num_spare_cols = num_spare_cols
|
self.num_spare_cols = num_spare_cols
|
||||||
|
|
||||||
|
self.column_offset = column_offset
|
||||||
|
self.row_size = self.word_size * self.words_per_row
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -105,43 +108,36 @@ class sense_amp_array(design.design):
|
||||||
def place_sense_amp_array(self):
|
def place_sense_amp_array(self):
|
||||||
from tech import cell_properties
|
from tech import cell_properties
|
||||||
if self.bitcell.width > self.amp.width:
|
if self.bitcell.width > self.amp.width:
|
||||||
amp_spacing = self.bitcell.width * self.words_per_row
|
amp_spacing = self.bitcell.width
|
||||||
spare_cols_spacing = self.bitcell.width
|
|
||||||
else:
|
else:
|
||||||
amp_spacing = self.amp.width * self.words_per_row
|
amp_spacing = self.amp.width
|
||||||
spare_cols_spacing = self.amp.width
|
|
||||||
|
|
||||||
for i in range(0, self.word_size):
|
for i in range(0, self.row_size, self.words_per_row):
|
||||||
xoffset = amp_spacing * i
|
index = int(i / self.words_per_row)
|
||||||
# align the xoffset to the grid of bitcells. This way we
|
xoffset = i * amp_spacing
|
||||||
# know when to do the mirroring.
|
|
||||||
grid_x = int(xoffset / self.amp.width)
|
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
|
||||||
|
|
||||||
if cell_properties.bitcell.mirror.y and grid_x % 2:
|
|
||||||
mirror = "MY"
|
mirror = "MY"
|
||||||
xoffset = xoffset + self.amp.width
|
xoffset = xoffset + self.amp.width
|
||||||
else:
|
else:
|
||||||
mirror = ""
|
mirror = ""
|
||||||
|
|
||||||
amp_position = vector(xoffset, 0)
|
amp_position = vector(xoffset, 0)
|
||||||
self.local_insts[i].place(offset=amp_position,mirror=mirror)
|
self.local_insts[index].place(offset=amp_position, mirror=mirror)
|
||||||
|
|
||||||
# place spare sense amps (will share the same enable as regular sense amps)
|
# place spare sense amps (will share the same enable as regular sense amps)
|
||||||
for i in range(0,self.num_spare_cols):
|
for i in range(0,self.num_spare_cols):
|
||||||
index = self.word_size + i
|
index = self.word_size + i
|
||||||
xoffset = ((self.word_size * self.words_per_row) + i) * spare_cols_spacing
|
xoffset = ((self.word_size * self.words_per_row) + i) * amp_spacing
|
||||||
# align the xoffset to the grid of bitcells. This way we
|
|
||||||
# know when to do the mirroring.
|
|
||||||
grid_x = int(xoffset / self.amp.width)
|
|
||||||
|
|
||||||
if cell_properties.bitcell.mirror.y and grid_x % 2:
|
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
|
||||||
mirror = "MY"
|
mirror = "MY"
|
||||||
xoffset = xoffset + self.amp.width
|
xoffset = xoffset + self.amp.width
|
||||||
else:
|
else:
|
||||||
mirror = ""
|
mirror = ""
|
||||||
|
|
||||||
amp_position = vector(xoffset, 0)
|
amp_position = vector(xoffset, 0)
|
||||||
self.local_insts[index].place(offset=amp_position,mirror=mirror)
|
self.local_insts[index].place(offset=amp_position, mirror=mirror)
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
for i in range(len(self.local_insts)):
|
for i in range(len(self.local_insts)):
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#
|
#
|
||||||
import design
|
import design
|
||||||
import debug
|
import debug
|
||||||
from tech import layer
|
from tech import layer, preferred_directions
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
@ -20,7 +20,7 @@ class single_level_column_mux_array(design.design):
|
||||||
Array of column mux to read the bitlines through the 6T.
|
Array of column mux to read the bitlines through the 6T.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br"):
|
def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br", column_offset=0):
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(1, "Creating {0}".format(self.name))
|
debug.info(1, "Creating {0}".format(self.name))
|
||||||
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))
|
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))
|
||||||
|
|
@ -30,13 +30,19 @@ class single_level_column_mux_array(design.design):
|
||||||
self.words_per_row = int(self.columns / self.word_size)
|
self.words_per_row = int(self.columns / self.word_size)
|
||||||
self.bitcell_bl = bitcell_bl
|
self.bitcell_bl = bitcell_bl
|
||||||
self.bitcell_br = bitcell_br
|
self.bitcell_br = bitcell_br
|
||||||
|
self.column_offset = column_offset
|
||||||
|
|
||||||
if "li" in layer:
|
if "li" in layer:
|
||||||
self.col_mux_stack = self.li_stack
|
self.col_mux_stack = self.li_stack
|
||||||
self.col_mux_stack_pitch = self.li_pitch
|
self.col_mux_stack_pitch = self.m1_pitch
|
||||||
else:
|
else:
|
||||||
self.col_mux_stack = self.m1_stack
|
self.col_mux_stack = self.m1_stack
|
||||||
self.col_mux_stack_pitch = self.m1_pitch
|
self.col_mux_stack_pitch = self.m1_pitch
|
||||||
|
|
||||||
|
if preferred_directions[self.col_mux_stack[0]] == "V":
|
||||||
|
self.via_directions = ("H", "H")
|
||||||
|
else:
|
||||||
|
self.via_directions = "pref"
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
|
|
@ -112,7 +118,7 @@ class single_level_column_mux_array(design.design):
|
||||||
# For every column, add a pass gate
|
# For every column, add a pass gate
|
||||||
for col_num in range(self.columns):
|
for col_num in range(self.columns):
|
||||||
xoffset = col_num * self.mux.width
|
xoffset = col_num * self.mux.width
|
||||||
if cell_properties.bitcell.mirror.y and col_num % 2:
|
if cell_properties.bitcell.mirror.y and (col_num + self.column_offset) % 2:
|
||||||
mirror = "MY"
|
mirror = "MY"
|
||||||
xoffset = xoffset + self.mux.width
|
xoffset = xoffset + self.mux.width
|
||||||
else:
|
else:
|
||||||
|
|
@ -173,73 +179,53 @@ class single_level_column_mux_array(design.design):
|
||||||
self.get_pin("sel_{}".format(sel_index)).cy())
|
self.get_pin("sel_{}".format(sel_index)).cy())
|
||||||
# Add the poly contact with a shift to account for the rotation
|
# Add the poly contact with a shift to account for the rotation
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=offset)
|
offset=offset,
|
||||||
|
directions=self.via_directions)
|
||||||
self.add_path("poly", [offset, gate_offset])
|
self.add_path("poly", [offset, gate_offset])
|
||||||
|
|
||||||
def route_bitlines(self):
|
def route_bitlines(self):
|
||||||
""" Connect the output bit-lines to form the appropriate width mux """
|
""" Connect the output bit-lines to form the appropriate width mux """
|
||||||
from tech import cell_properties
|
|
||||||
for j in range(self.columns):
|
for j in range(self.columns):
|
||||||
bl_offset = self.mux_inst[j].get_pin("bl_out").bc()
|
|
||||||
br_offset = self.mux_inst[j].get_pin("br_out").bc()
|
|
||||||
|
|
||||||
bl_out_offset = bl_offset - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch)
|
bl_offset_begin = self.mux_inst[j].get_pin("bl_out").bc()
|
||||||
br_out_offset = br_offset - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch)
|
br_offset_begin = self.mux_inst[j].get_pin("br_out").bc()
|
||||||
|
|
||||||
bl_out_offset_end = bl_out_offset + vector(0, self.route_height)
|
bl_out_offset_begin = bl_offset_begin - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch)
|
||||||
br_out_offset_end = br_out_offset + vector(0, self.route_height)
|
br_out_offset_begin = br_offset_begin - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch)
|
||||||
|
|
||||||
if cell_properties.bitcell.mirror.y and j % 2:
|
# Add the horizontal wires for the first bit
|
||||||
tmp_bl_out_end = br_out_offset_end
|
if j % self.words_per_row == 0:
|
||||||
tmp_br_out_end = bl_out_offset_end
|
bl_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("bl_out").bc()
|
||||||
else:
|
br_offset_end = self.mux_inst[j + self.words_per_row - 1].get_pin("br_out").bc()
|
||||||
tmp_bl_out_end = bl_out_offset_end
|
bl_out_offset_end = bl_offset_end - vector(0, (self.words_per_row + 1) * self.col_mux_stack_pitch)
|
||||||
tmp_br_out_end = br_out_offset_end
|
br_out_offset_end = br_offset_end - vector(0, (self.words_per_row + 2) * self.col_mux_stack_pitch)
|
||||||
|
|
||||||
if (j % self.words_per_row) == 0:
|
self.add_path(self.col_mux_stack[0], [bl_out_offset_begin, bl_out_offset_end])
|
||||||
# Create the metal1 to connect the n-way mux output from the pass gate
|
self.add_path(self.col_mux_stack[0], [br_out_offset_begin, br_out_offset_end])
|
||||||
# These will be located below the select lines. Yes, these are M2 width
|
|
||||||
# to ensure vias are enclosed and M1 min width rules.
|
|
||||||
width = self.m2_width + self.mux.width * (self.words_per_row - 1)
|
|
||||||
|
|
||||||
if cell_properties.bitcell.mirror.y and (j % 2) == 0:
|
|
||||||
bl = self.mux.get_pin("bl")
|
|
||||||
br = self.mux.get_pin("br")
|
|
||||||
dist = abs(bl.ll().x - br.ll().x)
|
|
||||||
else:
|
|
||||||
dist = 0
|
|
||||||
|
|
||||||
self.add_path(self.col_mux_stack[0], [bl_out_offset, bl_out_offset + vector(width + dist, 0)])
|
|
||||||
self.add_path(self.col_mux_stack[0], [br_out_offset, br_out_offset + vector(width - dist, 0)])
|
|
||||||
|
|
||||||
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux
|
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux
|
||||||
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)),
|
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j / self.words_per_row)),
|
||||||
layer=self.col_mux_stack[2],
|
layer=self.col_mux_stack[2],
|
||||||
start=bl_out_offset,
|
start=bl_offset_begin,
|
||||||
end=tmp_bl_out_end)
|
end=bl_out_offset_begin)
|
||||||
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)),
|
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j / self.words_per_row)),
|
||||||
layer=self.col_mux_stack[2],
|
layer=self.col_mux_stack[2],
|
||||||
start=br_out_offset,
|
start=br_offset_begin,
|
||||||
end=tmp_br_out_end)
|
end=br_out_offset_begin)
|
||||||
|
|
||||||
# This via is on the right of the wire
|
|
||||||
self.add_via_center(layers=self.col_mux_stack,
|
|
||||||
offset=bl_out_offset)
|
|
||||||
|
|
||||||
# This via is on the left of the wire
|
|
||||||
self.add_via_center(layers=self.col_mux_stack,
|
|
||||||
offset=br_out_offset)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.add_path(self.col_mux_stack[2], [bl_out_offset, bl_offset])
|
self.add_path(self.col_mux_stack[2], [bl_out_offset_begin, bl_offset_begin])
|
||||||
self.add_path(self.col_mux_stack[2], [br_out_offset, br_offset])
|
self.add_path(self.col_mux_stack[2], [br_out_offset_begin, br_offset_begin])
|
||||||
|
|
||||||
# This via is on the right of the wire
|
# This via is on the right of the wire
|
||||||
self.add_via_center(layers=self.col_mux_stack,
|
self.add_via_center(layers=self.col_mux_stack,
|
||||||
offset=bl_out_offset)
|
offset=bl_out_offset_begin,
|
||||||
# This via is on the left of the wire
|
directions=self.via_directions)
|
||||||
self.add_via_center(layers=self.col_mux_stack,
|
|
||||||
offset=br_out_offset)
|
# This via is on the left of the wire
|
||||||
|
self.add_via_center(layers=self.col_mux_stack,
|
||||||
|
offset=br_out_offset_begin,
|
||||||
|
directions=self.via_directions)
|
||||||
|
|
||||||
def get_drain_cin(self):
|
def get_drain_cin(self):
|
||||||
"""Get the relative capacitance of the drain of the NMOS pass TX"""
|
"""Get the relative capacitance of the drain of the NMOS pass TX"""
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,12 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
import design
|
||||||
import math
|
from tech import drc, layer
|
||||||
from tech import drc
|
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import cell_properties
|
|
||||||
|
|
||||||
|
class wordline_driver_array(design.design):
|
||||||
class wordline_driver(design.design):
|
|
||||||
"""
|
"""
|
||||||
Creates a Wordline Driver
|
Creates a Wordline Driver
|
||||||
Generates the wordline-driver to drive the bitcell
|
Generates the wordline-driver to drive the bitcell
|
||||||
|
|
@ -26,21 +23,9 @@ class wordline_driver(design.design):
|
||||||
debug.info(1, "Creating {0}".format(self.name))
|
debug.info(1, "Creating {0}".format(self.name))
|
||||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||||
|
|
||||||
self.bitcell_rows = rows
|
self.rows = rows
|
||||||
self.bitcell_cols = cols
|
self.cols = cols
|
||||||
|
|
||||||
b = factory.create(module_type="bitcell")
|
|
||||||
try:
|
|
||||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
|
||||||
except AttributeError:
|
|
||||||
self.cell_multiple = 1
|
|
||||||
self.cell_height = self.cell_multiple * b.height
|
|
||||||
|
|
||||||
# We may have more than one bitcell per decoder row
|
|
||||||
self.num_rows = math.ceil(self.bitcell_rows / self.cell_multiple)
|
|
||||||
# We will place this many final decoders per row
|
|
||||||
self.decoders_per_row = math.ceil(self.bitcell_rows / self.num_rows)
|
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
|
|
@ -51,7 +36,10 @@ class wordline_driver(design.design):
|
||||||
self.create_drivers()
|
self.create_drivers()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.setup_layout_constants()
|
if "li" in layer:
|
||||||
|
self.route_layer = "li"
|
||||||
|
else:
|
||||||
|
self.route_layer = "m1"
|
||||||
self.place_drivers()
|
self.place_drivers()
|
||||||
self.route_layout()
|
self.route_layout()
|
||||||
self.route_vdd_gnd()
|
self.route_vdd_gnd()
|
||||||
|
|
@ -61,104 +49,99 @@ class wordline_driver(design.design):
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
# inputs to wordline_driver.
|
# inputs to wordline_driver.
|
||||||
for i in range(self.bitcell_rows):
|
for i in range(self.rows):
|
||||||
self.add_pin("in_{0}".format(i), "INPUT")
|
self.add_pin("in_{0}".format(i), "INPUT")
|
||||||
# Outputs from wordline_driver.
|
# Outputs from wordline_driver.
|
||||||
for i in range(self.bitcell_rows):
|
for i in range(self.rows):
|
||||||
self.add_pin("wl_{0}".format(i), "OUTPUT")
|
self.add_pin("wl_{0}".format(i), "OUTPUT")
|
||||||
self.add_pin("en", "INPUT")
|
self.add_pin("en", "INPUT")
|
||||||
self.add_pin("vdd", "POWER")
|
self.add_pin("vdd", "POWER")
|
||||||
self.add_pin("gnd", "GROUND")
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.and2 = factory.create(module_type="pand2",
|
self.wl_driver = factory.create(module_type="wordline_driver",
|
||||||
height=self.cell_height,
|
size=self.cols)
|
||||||
size=self.bitcell_cols)
|
self.add_mod(self.wl_driver)
|
||||||
self.add_mod(self.and2)
|
|
||||||
|
|
||||||
def route_vdd_gnd(self):
|
def route_vdd_gnd(self):
|
||||||
"""
|
"""
|
||||||
Add a pin for each row of vdd/gnd which
|
Add a pin for each row of vdd/gnd which
|
||||||
are must-connects next level up.
|
are must-connects next level up.
|
||||||
"""
|
"""
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
# Find the x offsets for where the vias/pins should be placed
|
|
||||||
xoffset_list = [self.and_inst[0].lx()]
|
|
||||||
for num in range(self.bitcell_rows):
|
|
||||||
# this will result in duplicate polygons for rails, but who cares
|
|
||||||
|
|
||||||
# use the inverter offset even though it will be the and's too
|
|
||||||
(gate_offset, y_dir) = self.get_gate_offset(0,
|
|
||||||
self.and2.height,
|
|
||||||
num)
|
|
||||||
# Route both supplies
|
|
||||||
for name in ["vdd", "gnd"]:
|
for name in ["vdd", "gnd"]:
|
||||||
supply_pin = self.and_inst[num].get_pin(name)
|
supply_pins = self.wld_inst[0].get_pins(name)
|
||||||
|
for pin in supply_pins:
|
||||||
# Add pins in two locations
|
self.add_layout_pin_segment_center(text=name,
|
||||||
for xoffset in xoffset_list:
|
layer=pin.layer,
|
||||||
pin_pos = vector(xoffset, supply_pin.cy())
|
start=pin.bc(),
|
||||||
self.add_power_pin(name, pin_pos)
|
end=vector(pin.cx(), self.height))
|
||||||
|
else:
|
||||||
|
# Find the x offsets for where the vias/pins should be placed
|
||||||
|
xoffset_list = [self.wld_inst[0].rx()]
|
||||||
|
for num in range(self.rows):
|
||||||
|
# this will result in duplicate polygons for rails, but who cares
|
||||||
|
|
||||||
|
# use the inverter offset even though it will be the and's too
|
||||||
|
(gate_offset, y_dir) = self.get_gate_offset(0,
|
||||||
|
self.wl_driver.height,
|
||||||
|
num)
|
||||||
|
# Route both supplies
|
||||||
|
for name in ["vdd", "gnd"]:
|
||||||
|
supply_pin = self.wld_inst[num].get_pin(name)
|
||||||
|
|
||||||
|
# Add pins in two locations
|
||||||
|
for xoffset in xoffset_list:
|
||||||
|
pin_pos = vector(xoffset, supply_pin.cy())
|
||||||
|
self.add_power_pin(name, pin_pos)
|
||||||
|
|
||||||
def create_drivers(self):
|
def create_drivers(self):
|
||||||
self.and_inst = []
|
self.wld_inst = []
|
||||||
for row in range(self.bitcell_rows):
|
for row in range(self.rows):
|
||||||
name_and = "wl_driver_and{}".format(row)
|
name_and = "wl_driver_and{}".format(row)
|
||||||
|
|
||||||
# add and2
|
# add and2
|
||||||
self.and_inst.append(self.add_inst(name=name_and,
|
self.wld_inst.append(self.add_inst(name=name_and,
|
||||||
mod=self.and2))
|
mod=self.wl_driver))
|
||||||
self.connect_inst(["in_{0}".format(row),
|
self.connect_inst(["in_{0}".format(row),
|
||||||
"en",
|
"en",
|
||||||
"wl_{0}".format(row),
|
"wl_{0}".format(row),
|
||||||
"vdd", "gnd"])
|
"vdd", "gnd"])
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
|
||||||
# We may have more than one bitcell per decoder row
|
|
||||||
self.driver_rows = math.ceil(self.bitcell_rows / self.cell_multiple)
|
|
||||||
# We will place this many final decoders per row
|
|
||||||
self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows)
|
|
||||||
|
|
||||||
def place_drivers(self):
|
def place_drivers(self):
|
||||||
|
|
||||||
|
for row in range(self.rows):
|
||||||
|
if (row % 2):
|
||||||
|
y_offset = self.wl_driver.height * (row + 1)
|
||||||
|
inst_mirror = "MX"
|
||||||
|
else:
|
||||||
|
y_offset = self.wl_driver.height * row
|
||||||
|
inst_mirror = "R0"
|
||||||
|
|
||||||
|
and2_offset = [self.wl_driver.width, y_offset]
|
||||||
|
|
||||||
|
# add and2
|
||||||
|
self.wld_inst[row].place(offset=and2_offset,
|
||||||
|
mirror=inst_mirror)
|
||||||
|
|
||||||
# Leave a well gap to separate the bitcell array well from this well
|
# Leave a well gap to separate the bitcell array well from this well
|
||||||
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
|
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
|
||||||
|
self.width = self.wl_driver.width + well_gap
|
||||||
self.width = self.decoders_per_row * self.and2.width + well_gap
|
self.height = self.wl_driver.height * self.rows
|
||||||
self.height = self.and2.height * self.driver_rows
|
|
||||||
|
|
||||||
for inst_index in range(self.bitcell_rows):
|
|
||||||
row = math.floor(inst_index / self.decoders_per_row)
|
|
||||||
dec = inst_index % self.decoders_per_row
|
|
||||||
|
|
||||||
if (row % 2):
|
|
||||||
y_offset = self.and2.height * (row + 1)
|
|
||||||
inst_mirror = "MX"
|
|
||||||
else:
|
|
||||||
y_offset = self.and2.height * row
|
|
||||||
inst_mirror = "R0"
|
|
||||||
|
|
||||||
x_offset = dec * self.and2.width
|
|
||||||
and2_offset = [x_offset, y_offset]
|
|
||||||
|
|
||||||
# add and2
|
|
||||||
self.and_inst[inst_index].place(offset=and2_offset,
|
|
||||||
mirror=inst_mirror)
|
|
||||||
|
|
||||||
def route_layout(self):
|
def route_layout(self):
|
||||||
""" Route all of the signals """
|
""" Route all of the signals """
|
||||||
|
|
||||||
# Wordline enable connection
|
# Wordline enable connection
|
||||||
en_pin = self.and_inst[0].get_pin("B")
|
en_pin = self.wld_inst[0].get_pin("B")
|
||||||
en_bottom_pos = vector(en_pin.lx(), 0)
|
en_bottom_pos = vector(en_pin.lx(), 0)
|
||||||
en_pin = self.add_layout_pin(text="en",
|
en_pin = self.add_layout_pin(text="en",
|
||||||
layer="m2",
|
layer="m2",
|
||||||
offset=en_bottom_pos,
|
offset=en_bottom_pos,
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
for inst_index in range(self.bitcell_rows):
|
for row in range(self.rows):
|
||||||
and_inst = self.and_inst[inst_index]
|
and_inst = self.wld_inst[row]
|
||||||
row = math.floor(inst_index / self.decoders_per_row)
|
|
||||||
|
|
||||||
# Drop a via
|
# Drop a via
|
||||||
b_pin = and_inst.get_pin("B")
|
b_pin = and_inst.get_pin("B")
|
||||||
|
|
@ -167,18 +150,12 @@ class wordline_driver(design.design):
|
||||||
offset=b_pin.center())
|
offset=b_pin.center())
|
||||||
|
|
||||||
# connect the decoder input pin to and2 A
|
# connect the decoder input pin to and2 A
|
||||||
a_pin = and_inst.get_pin("A")
|
self.copy_layout_pin(and_inst, "A", "in_{0}".format(row))
|
||||||
a_pos = a_pin.center()
|
|
||||||
# must under the clk line in M1
|
|
||||||
self.add_layout_pin_segment_center(text="in_{0}".format(row),
|
|
||||||
layer="m1",
|
|
||||||
start=vector(0, a_pos.y),
|
|
||||||
end=a_pos)
|
|
||||||
|
|
||||||
# output each WL on the right
|
# output each WL on the right
|
||||||
wl_offset = and_inst.get_pin("Z").rc()
|
wl_offset = and_inst.get_pin("Z").rc()
|
||||||
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
|
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
|
||||||
layer="m1",
|
layer=self.route_layer,
|
||||||
start=wl_offset,
|
start=wl_offset,
|
||||||
end=wl_offset - vector(self.m1_width, 0))
|
end=wl_offset - vector(self.m1_width, 0))
|
||||||
|
|
||||||
|
|
@ -189,7 +166,7 @@ class wordline_driver(design.design):
|
||||||
"""
|
"""
|
||||||
stage_effort_list = []
|
stage_effort_list = []
|
||||||
|
|
||||||
stage1 = self.and2.get_stage_effort(external_cout, inp_is_rise)
|
stage1 = self.wl_driver.get_stage_effort(external_cout, inp_is_rise)
|
||||||
stage_effort_list.append(stage1)
|
stage_effort_list.append(stage1)
|
||||||
|
|
||||||
return stage_effort_list
|
return stage_effort_list
|
||||||
|
|
@ -200,5 +177,5 @@ class wordline_driver(design.design):
|
||||||
the enable connections in the bank
|
the enable connections in the bank
|
||||||
"""
|
"""
|
||||||
# The enable is connected to a and2 for every row.
|
# The enable is connected to a and2 for every row.
|
||||||
total_cin = self.and2.get_cin() * self.rows
|
total_cin = self.wl_driver.get_cin() * self.rows
|
||||||
return total_cin
|
return total_cin
|
||||||
|
|
@ -18,7 +18,8 @@ class write_driver_array(design.design):
|
||||||
Dynamically generated write driver array of all bitlines.
|
Dynamically generated write driver array of all bitlines.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, columns, word_size, num_spare_cols=None, write_size=None):
|
def __init__(self, name, columns, word_size, num_spare_cols=None, write_size=None, column_offset=0):
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(1, "Creating {0}".format(self.name))
|
debug.info(1, "Creating {0}".format(self.name))
|
||||||
self.add_comment("columns: {0}".format(columns))
|
self.add_comment("columns: {0}".format(columns))
|
||||||
|
|
@ -27,6 +28,7 @@ class write_driver_array(design.design):
|
||||||
self.columns = columns
|
self.columns = columns
|
||||||
self.word_size = word_size
|
self.word_size = word_size
|
||||||
self.write_size = write_size
|
self.write_size = write_size
|
||||||
|
self.column_offset = column_offset
|
||||||
self.words_per_row = int(columns / word_size)
|
self.words_per_row = int(columns / word_size)
|
||||||
if not num_spare_cols:
|
if not num_spare_cols:
|
||||||
self.num_spare_cols = 0
|
self.num_spare_cols = 0
|
||||||
|
|
@ -162,7 +164,7 @@ class write_driver_array(design.design):
|
||||||
index = int(i / self.words_per_row)
|
index = int(i / self.words_per_row)
|
||||||
xoffset = i * self.driver_spacing
|
xoffset = i * self.driver_spacing
|
||||||
|
|
||||||
if cell_properties.bitcell.mirror.y and i % 2:
|
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
|
||||||
mirror = "MY"
|
mirror = "MY"
|
||||||
xoffset = xoffset + self.driver.width
|
xoffset = xoffset + self.driver.width
|
||||||
else:
|
else:
|
||||||
|
|
@ -176,7 +178,7 @@ class write_driver_array(design.design):
|
||||||
index = self.word_size + i
|
index = self.word_size + i
|
||||||
xoffset = (self.columns + i) * self.driver_spacing
|
xoffset = (self.columns + i) * self.driver_spacing
|
||||||
|
|
||||||
if cell_properties.bitcell.mirror.y and i % 2:
|
if cell_properties.bitcell.mirror.y and (i + self.column_offset) % 2:
|
||||||
mirror = "MY"
|
mirror = "MY"
|
||||||
xoffset = xoffset + self.driver.width
|
xoffset = xoffset + self.driver.width
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class write_mask_and_array(design.design):
|
||||||
The write mask AND array goes between the write driver array and the sense amp array.
|
The write mask AND array goes between the write driver array and the sense amp array.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, columns, word_size, write_size, port=0):
|
def __init__(self, name, columns, word_size, write_size, column_offset=0):
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
debug.info(1, "Creating {0}".format(self.name))
|
debug.info(1, "Creating {0}".format(self.name))
|
||||||
self.add_comment("columns: {0}".format(columns))
|
self.add_comment("columns: {0}".format(columns))
|
||||||
|
|
@ -28,7 +28,7 @@ class write_mask_and_array(design.design):
|
||||||
self.columns = columns
|
self.columns = columns
|
||||||
self.word_size = word_size
|
self.word_size = word_size
|
||||||
self.write_size = write_size
|
self.write_size = write_size
|
||||||
self.port = port
|
self.column_offset = column_offset
|
||||||
self.words_per_row = int(columns / word_size)
|
self.words_per_row = int(columns / word_size)
|
||||||
self.num_wmasks = int(word_size / write_size)
|
self.num_wmasks = int(word_size / write_size)
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ class write_mask_and_array(design.design):
|
||||||
# Size the AND gate for the number of write drivers it drives, which is equal to the write size.
|
# Size the AND gate for the number of write drivers it drives, which is equal to the write size.
|
||||||
# Assume stage effort of 3 to compute the size
|
# Assume stage effort of 3 to compute the size
|
||||||
self.and2 = factory.create(module_type="pand2",
|
self.and2 = factory.create(module_type="pand2",
|
||||||
size=self.write_size / 4.0)
|
size=max(self.write_size / 4.0, 1))
|
||||||
self.add_mod(self.and2)
|
self.add_mod(self.and2)
|
||||||
|
|
||||||
def create_and2_array(self):
|
def create_and2_array(self):
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ class options(optparse.Values):
|
||||||
###################
|
###################
|
||||||
# This is the technology directory.
|
# This is the technology directory.
|
||||||
openram_tech = ""
|
openram_tech = ""
|
||||||
|
|
||||||
# This is the name of the technology.
|
# This is the name of the technology.
|
||||||
tech_name = ""
|
tech_name = ""
|
||||||
|
|
||||||
# Port configuration (1-2 ports allowed)
|
# Port configuration (1-2 ports allowed)
|
||||||
num_rw_ports = 1
|
num_rw_ports = 1
|
||||||
num_r_ports = 0
|
num_r_ports = 0
|
||||||
|
|
@ -32,7 +32,7 @@ class options(optparse.Values):
|
||||||
|
|
||||||
# Write mask size, default will be overwritten with word_size if not user specified
|
# Write mask size, default will be overwritten with word_size if not user specified
|
||||||
write_size = None
|
write_size = None
|
||||||
|
|
||||||
# These will get initialized by the user or the tech file
|
# These will get initialized by the user or the tech file
|
||||||
nominal_corner_only = False
|
nominal_corner_only = False
|
||||||
supply_voltages = ""
|
supply_voltages = ""
|
||||||
|
|
@ -52,17 +52,17 @@ class options(optparse.Values):
|
||||||
###################
|
###################
|
||||||
# Approximate percentage of delay compared to bitlines
|
# Approximate percentage of delay compared to bitlines
|
||||||
rbl_delay_percentage = 0.5
|
rbl_delay_percentage = 0.5
|
||||||
|
|
||||||
# Allow manual adjustment of the delay chain over automatic
|
# Allow manual adjustment of the delay chain over automatic
|
||||||
use_tech_delay_chain_size = False
|
use_tech_delay_chain_size = False
|
||||||
delay_chain_stages = 9
|
delay_chain_stages = 9
|
||||||
delay_chain_fanout_per_stage = 4
|
delay_chain_fanout_per_stage = 4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# Debug options.
|
# Debug options.
|
||||||
###################
|
###################
|
||||||
# This is the temp directory where all intermediate results are stored.
|
# This is the temp directory where all intermediate results are stored.
|
||||||
try:
|
try:
|
||||||
# If user defined the temporary location in their environment, use it
|
# If user defined the temporary location in their environment, use it
|
||||||
|
|
@ -93,7 +93,7 @@ class options(optparse.Values):
|
||||||
# Run with extracted parasitics
|
# Run with extracted parasitics
|
||||||
use_pex = False
|
use_pex = False
|
||||||
|
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# Tool options
|
# Tool options
|
||||||
###################
|
###################
|
||||||
|
|
@ -110,7 +110,7 @@ class options(optparse.Values):
|
||||||
drc_exe = None
|
drc_exe = None
|
||||||
lvs_exe = None
|
lvs_exe = None
|
||||||
pex_exe = None
|
pex_exe = None
|
||||||
|
|
||||||
# Should we print out the banner at startup
|
# Should we print out the banner at startup
|
||||||
print_banner = True
|
print_banner = True
|
||||||
|
|
||||||
|
|
@ -126,6 +126,7 @@ class options(optparse.Values):
|
||||||
purge_temp = True
|
purge_temp = True
|
||||||
|
|
||||||
# These are the default modules that can be over-riden
|
# These are the default modules that can be over-riden
|
||||||
|
bitcell_suffix = ""
|
||||||
bank_select = "bank_select"
|
bank_select = "bank_select"
|
||||||
bitcell_array = "bitcell_array"
|
bitcell_array = "bitcell_array"
|
||||||
bitcell = "bitcell"
|
bitcell = "bitcell"
|
||||||
|
|
@ -135,10 +136,12 @@ class options(optparse.Values):
|
||||||
delay_chain = "delay_chain"
|
delay_chain = "delay_chain"
|
||||||
dff_array = "dff_array"
|
dff_array = "dff_array"
|
||||||
dff = "dff"
|
dff = "dff"
|
||||||
dummy_bitcell = "dummy_bitcell"
|
inv_dec = "pinv"
|
||||||
|
nand2_dec = "pnand2"
|
||||||
|
nand3_dec = "pnand3"
|
||||||
|
nand4_dec = "pnand4" # Not available right now
|
||||||
precharge_array = "precharge_array"
|
precharge_array = "precharge_array"
|
||||||
ptx = "ptx"
|
ptx = "ptx"
|
||||||
replica_bitcell = "replica_bitcell"
|
|
||||||
replica_bitline = "replica_bitline"
|
replica_bitline = "replica_bitline"
|
||||||
sense_amp_array = "sense_amp_array"
|
sense_amp_array = "sense_amp_array"
|
||||||
sense_amp = "sense_amp"
|
sense_amp = "sense_amp"
|
||||||
|
|
@ -148,4 +151,3 @@ class options(optparse.Values):
|
||||||
write_driver_array = "write_driver_array"
|
write_driver_array = "write_driver_array"
|
||||||
write_driver = "write_driver"
|
write_driver = "write_driver"
|
||||||
write_mask_and_array = "write_mask_and_array"
|
write_mask_and_array = "write_mask_and_array"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,16 @@ from sram_factory import factory
|
||||||
|
|
||||||
class pand2(pgate.pgate):
|
class pand2(pgate.pgate):
|
||||||
"""
|
"""
|
||||||
This is a simple buffer used for driving loads.
|
This is an AND (or NAND) with configurable drive strength.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None):
|
def __init__(self, name, size=1, height=None, vertical=False, add_wells=True):
|
||||||
debug.info(1, "Creating pnand2 {}".format(name))
|
debug.info(1, "Creating pand2 {}".format(name))
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
|
|
||||||
|
self.vertical = vertical
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
||||||
# Creates the netlist and layout
|
pgate.pgate.__init__(self, name, height, add_wells)
|
||||||
pgate.pgate.__init__(self, name, height)
|
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
|
|
@ -30,17 +30,25 @@ class pand2(pgate.pgate):
|
||||||
self.create_insts()
|
self.create_insts()
|
||||||
|
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
self.nand = factory.create(module_type="pnand2", height=self.height)
|
self.nand = factory.create(module_type="pnand2",
|
||||||
self.add_mod(self.nand)
|
height=self.height,
|
||||||
|
add_wells=self.vertical)
|
||||||
|
|
||||||
self.inv = factory.create(module_type="pdriver",
|
self.inv = factory.create(module_type="pdriver",
|
||||||
neg_polarity=True,
|
size_list=[self.size],
|
||||||
fanout=self.size,
|
height=self.height,
|
||||||
height=self.height)
|
add_wells=self.add_wells)
|
||||||
|
|
||||||
|
self.add_mod(self.nand)
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.width = self.nand.width + self.inv.width
|
if self.vertical:
|
||||||
|
self.height = 2 * self.nand.height
|
||||||
|
self.width = max(self.nand.width, self.inv.width)
|
||||||
|
else:
|
||||||
|
self.width = self.nand.width + self.inv.width
|
||||||
|
|
||||||
self.place_insts()
|
self.place_insts()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
@ -68,17 +76,60 @@ class pand2(pgate.pgate):
|
||||||
# Add NAND to the right
|
# Add NAND to the right
|
||||||
self.nand_inst.place(offset=vector(0, 0))
|
self.nand_inst.place(offset=vector(0, 0))
|
||||||
|
|
||||||
# Add INV to the right
|
if self.vertical:
|
||||||
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
# Add INV above
|
||||||
|
self.inv_inst.place(offset=vector(self.inv.width,
|
||||||
|
2 * self.nand.height),
|
||||||
|
mirror="XY")
|
||||||
|
else:
|
||||||
|
# Add INV to the right
|
||||||
|
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||||
|
|
||||||
|
def route_supply_rails(self):
|
||||||
|
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, 0),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
# Second gnd of the inverter gate
|
||||||
|
if self.vertical:
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, self.height),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
if self.vertical:
|
||||||
|
# Shared between two gates
|
||||||
|
y_offset = 0.5 * self.height
|
||||||
|
else:
|
||||||
|
y_offset = self.height
|
||||||
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, y_offset),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
def add_wires(self):
|
def add_wires(self):
|
||||||
# nand Z to inv A
|
# nand Z to inv A
|
||||||
z1_pin = self.nand_inst.get_pin("Z")
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
a2_pin = self.inv_inst.get_pin("A")
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
|
if self.vertical:
|
||||||
mid2_point = vector(mid1_point, a2_pin.cy())
|
route_layer = "m2"
|
||||||
self.add_path(self.route_layer,
|
self.add_via_stack_center(offset=z1_pin.center(),
|
||||||
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
|
from_layer=z1_pin.layer,
|
||||||
|
to_layer=route_layer)
|
||||||
|
self.add_zjog(route_layer,
|
||||||
|
z1_pin.uc(),
|
||||||
|
a2_pin.bc(),
|
||||||
|
"V")
|
||||||
|
self.add_via_stack_center(offset=a2_pin.center(),
|
||||||
|
from_layer=a2_pin.layer,
|
||||||
|
to_layer=route_layer)
|
||||||
|
else:
|
||||||
|
route_layer = self.route_layer
|
||||||
|
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||||
|
self.add_path(route_layer,
|
||||||
|
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
pin = self.inv_inst.get_pin("Z")
|
pin = self.inv_inst.get_pin("Z")
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,15 @@ class pand3(pgate.pgate):
|
||||||
"""
|
"""
|
||||||
This is a simple buffer used for driving loads.
|
This is a simple buffer used for driving loads.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None):
|
def __init__(self, name, size=1, height=None, vertical=False, add_wells=True):
|
||||||
debug.info(1, "Creating pand3 {}".format(name))
|
debug.info(1, "Creating pand3 {}".format(name))
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
|
|
||||||
|
self.vertical = vertical
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
||||||
# Creates the netlist and layout
|
# Creates the netlist and layout
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height, add_wells)
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
|
|
@ -31,16 +32,27 @@ class pand3(pgate.pgate):
|
||||||
|
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
# Shield the cap, but have at least a stage effort of 4
|
# Shield the cap, but have at least a stage effort of 4
|
||||||
self.nand = factory.create(module_type="pnand3", height=self.height)
|
self.nand = factory.create(module_type="pnand3",
|
||||||
self.add_mod(self.nand)
|
height=self.height,
|
||||||
|
add_wells=self.vertical)
|
||||||
|
|
||||||
self.inv = factory.create(module_type="pinv",
|
# Add the well tap to the inverter because when stacked
|
||||||
size=self.size,
|
# vertically it is sometimes narrower
|
||||||
height=self.height)
|
self.inv = factory.create(module_type="pdriver",
|
||||||
|
size_list=[self.size],
|
||||||
|
height=self.height,
|
||||||
|
add_wells=self.add_wells)
|
||||||
|
|
||||||
|
self.add_mod(self.nand)
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.width = self.nand.width + self.inv.width
|
if self.vertical:
|
||||||
|
self.height = 2 * self.nand.height
|
||||||
|
self.width = max(self.nand.width, self.inv.width)
|
||||||
|
else:
|
||||||
|
self.width = self.nand.width + self.inv.width
|
||||||
|
|
||||||
self.place_insts()
|
self.place_insts()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
@ -69,18 +81,61 @@ class pand3(pgate.pgate):
|
||||||
# Add NAND to the right
|
# Add NAND to the right
|
||||||
self.nand_inst.place(offset=vector(0, 0))
|
self.nand_inst.place(offset=vector(0, 0))
|
||||||
|
|
||||||
# Add INV to the right
|
if self.vertical:
|
||||||
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
# Add INV above
|
||||||
|
self.inv_inst.place(offset=vector(self.inv.width,
|
||||||
|
2 * self.nand.height),
|
||||||
|
mirror="XY")
|
||||||
|
else:
|
||||||
|
# Add INV to the right
|
||||||
|
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||||
|
|
||||||
|
def route_supply_rails(self):
|
||||||
|
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, 0),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
# Second gnd of the inverter gate
|
||||||
|
if self.vertical:
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, self.height),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
if self.vertical:
|
||||||
|
# Shared between two gates
|
||||||
|
y_offset = 0.5 * self.height
|
||||||
|
else:
|
||||||
|
y_offset = self.height
|
||||||
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, y_offset),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
def add_wires(self):
|
def add_wires(self):
|
||||||
# nand Z to inv A
|
# nand Z to inv A
|
||||||
z1_pin = self.nand_inst.get_pin("Z")
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
a2_pin = self.inv_inst.get_pin("A")
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
|
if self.vertical:
|
||||||
mid2_point = vector(mid1_point, a2_pin.cy())
|
route_layer = "m2"
|
||||||
self.add_path(z1_pin.layer,
|
self.add_via_stack_center(offset=z1_pin.center(),
|
||||||
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
|
from_layer=z1_pin.layer,
|
||||||
|
to_layer=route_layer)
|
||||||
|
self.add_zjog(route_layer,
|
||||||
|
z1_pin.uc(),
|
||||||
|
a2_pin.bc(),
|
||||||
|
"V")
|
||||||
|
self.add_via_stack_center(offset=a2_pin.center(),
|
||||||
|
from_layer=a2_pin.layer,
|
||||||
|
to_layer=route_layer)
|
||||||
|
else:
|
||||||
|
route_layer = self.route_layer
|
||||||
|
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||||
|
self.add_path(route_layer,
|
||||||
|
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
pin = self.inv_inst.get_pin("Z")
|
pin = self.inv_inst.get_pin("Z")
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,8 @@ class pbuf(pgate.pgate):
|
||||||
|
|
||||||
self.inv2 = factory.create(module_type="pinv",
|
self.inv2 = factory.create(module_type="pinv",
|
||||||
size=self.size,
|
size=self.size,
|
||||||
height=self.height)
|
height=self.height,
|
||||||
|
add_wells=False)
|
||||||
self.add_mod(self.inv2)
|
self.add_mod(self.inv2)
|
||||||
|
|
||||||
def create_insts(self):
|
def create_insts(self):
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@ class pdriver(pgate.pgate):
|
||||||
sized for driving a load.
|
sized for driving a load.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None):
|
def __init__(self, name, inverting=False, fanout=0, size_list=None, height=None, add_wells=True):
|
||||||
|
|
||||||
debug.info(1, "creating pdriver {}".format(name))
|
debug.info(1, "creating pdriver {}".format(name))
|
||||||
|
|
||||||
self.stage_effort = 3
|
self.stage_effort = 3
|
||||||
self.height = height
|
self.height = height
|
||||||
self.neg_polarity = neg_polarity
|
self.inverting = inverting
|
||||||
self.size_list = size_list
|
self.size_list = size_list
|
||||||
self.fanout = fanout
|
self.fanout = fanout
|
||||||
|
|
||||||
|
|
@ -31,11 +31,11 @@ class pdriver(pgate.pgate):
|
||||||
debug.error("Either fanout or size list must be specified.", -1)
|
debug.error("Either fanout or size list must be specified.", -1)
|
||||||
if self.size_list and self.fanout != 0:
|
if self.size_list and self.fanout != 0:
|
||||||
debug.error("Cannot specify both size_list and fanout.", -1)
|
debug.error("Cannot specify both size_list and fanout.", -1)
|
||||||
if self.size_list and self.neg_polarity:
|
if self.size_list and self.inverting:
|
||||||
debug.error("Cannot specify both size_list and neg_polarity.", -1)
|
debug.error("Cannot specify both size_list and inverting.", -1)
|
||||||
|
|
||||||
# Creates the netlist and layout
|
# Creates the netlist and layout
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height, add_wells)
|
||||||
|
|
||||||
def compute_sizes(self):
|
def compute_sizes(self):
|
||||||
# size_list specified
|
# size_list specified
|
||||||
|
|
@ -47,9 +47,9 @@ class pdriver(pgate.pgate):
|
||||||
int(round(self.fanout ** (1 / self.stage_effort))))
|
int(round(self.fanout ** (1 / self.stage_effort))))
|
||||||
|
|
||||||
# Increase the number of stages if we need to fix polarity
|
# Increase the number of stages if we need to fix polarity
|
||||||
if self.neg_polarity and (self.num_stages % 2 == 0):
|
if self.inverting and (self.num_stages % 2 == 0):
|
||||||
self.num_stages += 1
|
self.num_stages += 1
|
||||||
elif not self.neg_polarity and (self.num_stages % 2):
|
elif not self.inverting and (self.num_stages % 2):
|
||||||
self.num_stages += 1
|
self.num_stages += 1
|
||||||
|
|
||||||
self.size_list = []
|
self.size_list = []
|
||||||
|
|
@ -73,9 +73,9 @@ class pdriver(pgate.pgate):
|
||||||
self.place_modules()
|
self.place_modules()
|
||||||
self.route_wires()
|
self.route_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
self.width = self.inv_inst_list[-1].rx()
|
self.width = self.inv_inst_list[-1].rx()
|
||||||
self.height = self.inv_inst_list[0].height
|
self.height = self.inv_inst_list[0].height
|
||||||
|
self.extend_wells()
|
||||||
self.route_supply_rails()
|
self.route_supply_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
|
|
@ -87,10 +87,13 @@ class pdriver(pgate.pgate):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.inv_list = []
|
self.inv_list = []
|
||||||
|
add_well = self.add_wells
|
||||||
for size in self.size_list:
|
for size in self.size_list:
|
||||||
temp_inv = factory.create(module_type="pinv",
|
temp_inv = factory.create(module_type="pinv",
|
||||||
size=size,
|
size=size,
|
||||||
height=self.height)
|
height=self.height,
|
||||||
|
add_wells=add_well)
|
||||||
|
add_well=False
|
||||||
self.inv_list.append(temp_inv)
|
self.inv_list.append(temp_inv)
|
||||||
self.add_mod(temp_inv)
|
self.add_mod(temp_inv)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ class pgate(design.design):
|
||||||
functions for parameterized gates.
|
functions for parameterized gates.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, height=None):
|
def __init__(self, name, height=None, add_wells=True):
|
||||||
""" Creates a generic cell """
|
""" Creates a generic cell """
|
||||||
design.design.__init__(self, name)
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
|
@ -33,7 +33,8 @@ class pgate(design.design):
|
||||||
elif not height:
|
elif not height:
|
||||||
# By default, something simple
|
# By default, something simple
|
||||||
self.height = 14 * self.m1_pitch
|
self.height = 14 * self.m1_pitch
|
||||||
|
self.add_wells = add_wells
|
||||||
|
|
||||||
if "li" in layer:
|
if "li" in layer:
|
||||||
self.route_layer = "li"
|
self.route_layer = "li"
|
||||||
else:
|
else:
|
||||||
|
|
@ -43,7 +44,6 @@ class pgate(design.design):
|
||||||
self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer))
|
self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer))
|
||||||
|
|
||||||
# This is the space from a S/D contact to the supply rail
|
# This is the space from a S/D contact to the supply rail
|
||||||
# Assume the contact starts at the active edge
|
|
||||||
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space
|
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space
|
||||||
# This is a poly-to-poly of a flipped cell
|
# This is a poly-to-poly of a flipped cell
|
||||||
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
|
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
|
||||||
|
|
@ -150,7 +150,7 @@ class pgate(design.design):
|
||||||
|
|
||||||
# This should match the cells in the cell library
|
# This should match the cells in the cell library
|
||||||
self.nwell_y_offset = 0.48 * self.height
|
self.nwell_y_offset = 0.48 * self.height
|
||||||
full_height = self.height + 0.5* self.m1_width
|
full_height = self.height + 0.5 * self.m1_width
|
||||||
|
|
||||||
# FIXME: float rounding problem
|
# FIXME: float rounding problem
|
||||||
if "nwell" in layer:
|
if "nwell" in layer:
|
||||||
|
|
@ -161,12 +161,12 @@ class pgate(design.design):
|
||||||
nwell_height = nwell_max_offset - self.nwell_y_offset
|
nwell_height = nwell_max_offset - self.nwell_y_offset
|
||||||
self.add_rect(layer="nwell",
|
self.add_rect(layer="nwell",
|
||||||
offset=nwell_position,
|
offset=nwell_position,
|
||||||
width=self.well_width,
|
width=self.width + 2 * self.well_extend_active,
|
||||||
height=nwell_height)
|
height=nwell_height)
|
||||||
if "vtg" in layer:
|
if "vtg" in layer:
|
||||||
self.add_rect(layer="vtg",
|
self.add_rect(layer="vtg",
|
||||||
offset=nwell_position,
|
offset=nwell_position,
|
||||||
width=self.well_width,
|
width=self.width + 2 * self.well_extend_active,
|
||||||
height=nwell_height)
|
height=nwell_height)
|
||||||
|
|
||||||
# Start this half a rail width below the cell
|
# Start this half a rail width below the cell
|
||||||
|
|
@ -177,12 +177,12 @@ class pgate(design.design):
|
||||||
pwell_height = self.nwell_y_offset - pwell_position.y
|
pwell_height = self.nwell_y_offset - pwell_position.y
|
||||||
self.add_rect(layer="pwell",
|
self.add_rect(layer="pwell",
|
||||||
offset=pwell_position,
|
offset=pwell_position,
|
||||||
width=self.well_width,
|
width=self.width + 2 * self.well_extend_active,
|
||||||
height=pwell_height)
|
height=pwell_height)
|
||||||
if "vtg" in layer:
|
if "vtg" in layer:
|
||||||
self.add_rect(layer="vtg",
|
self.add_rect(layer="vtg",
|
||||||
offset=pwell_position,
|
offset=pwell_position,
|
||||||
width=self.well_width,
|
width=self.width + 2 * self.well_extend_active,
|
||||||
height=pwell_height)
|
height=pwell_height)
|
||||||
|
|
||||||
def add_nwell_contact(self, pmos, pmos_pos):
|
def add_nwell_contact(self, pmos, pmos_pos):
|
||||||
|
|
@ -302,10 +302,18 @@ class pgate(design.design):
|
||||||
|
|
||||||
def determine_width(self):
|
def determine_width(self):
|
||||||
""" Determine the width based on the well contacts (assumed to be on the right side) """
|
""" Determine the width based on the well contacts (assumed to be on the right side) """
|
||||||
|
|
||||||
|
# It was already set or is left as default (minimum)
|
||||||
# Width is determined by well contact and spacing and allowing a supply via between each cell
|
# Width is determined by well contact and spacing and allowing a supply via between each cell
|
||||||
self.width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width
|
if self.add_wells:
|
||||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width
|
||||||
# Height is an input parameter, so it is not recomputed.
|
# Height is an input parameter, so it is not recomputed.
|
||||||
|
else:
|
||||||
|
max_active_xoffset = self.find_highest_layer_coords("active").x
|
||||||
|
max_route_xoffset = self.find_highest_layer_coords(self.route_layer).x + 0.5 * self.m1_space
|
||||||
|
width = max(max_active_xoffset, max_route_xoffset)
|
||||||
|
|
||||||
|
self.width = width
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def bin_width(tx_type, target_width):
|
def bin_width(tx_type, target_width):
|
||||||
|
|
@ -327,16 +335,20 @@ class pgate(design.design):
|
||||||
base_bins = []
|
base_bins = []
|
||||||
scaled_bins = []
|
scaled_bins = []
|
||||||
scaling_factors = []
|
scaling_factors = []
|
||||||
scaled_bins.append(bins[-1])
|
|
||||||
base_bins.append(bins[-1])
|
for width in bins:
|
||||||
scaling_factors.append(1)
|
|
||||||
for width in bins[0:-1]:
|
|
||||||
m = math.ceil(target_width / width)
|
m = math.ceil(target_width / width)
|
||||||
base_bins.append(width)
|
base_bins.append(width)
|
||||||
scaling_factors.append(m)
|
scaling_factors.append(m)
|
||||||
scaled_bins.append(m * width)
|
scaled_bins.append(m * width)
|
||||||
|
|
||||||
select = bisect_left(scaled_bins, target_width)
|
select = -1
|
||||||
|
for i in reversed(range(0, len(scaled_bins))):
|
||||||
|
if abs(target_width - scaled_bins[i])/target_width <= 1-accuracy_requirement:
|
||||||
|
select = i
|
||||||
|
break
|
||||||
|
if select == -1:
|
||||||
|
debug.error("failed to bin tx size {}, try reducing accuracy requirement".format(target_width), 1)
|
||||||
scaling_factor = scaling_factors[select]
|
scaling_factor = scaling_factors[select]
|
||||||
scaled_bin = scaled_bins[select]
|
scaled_bin = scaled_bins[select]
|
||||||
selected_bin = base_bins[select]
|
selected_bin = base_bins[select]
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ class pinv(pgate.pgate):
|
||||||
from center of rail to rail.
|
from center of rail to rail.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, size=1, beta=parameter["beta"], height=None):
|
def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True):
|
||||||
|
|
||||||
debug.info(2,
|
debug.info(2,
|
||||||
"creating pinv structure {0} with size of {1}".format(name,
|
"creating pinv structure {0} with size of {1}".format(name,
|
||||||
|
|
@ -40,11 +40,12 @@ class pinv(pgate.pgate):
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
|
|
||||||
self.size = size
|
self.size = size
|
||||||
|
debug.check(self.size >= 1, "Must have a size greater than or equal to 1.")
|
||||||
self.nmos_size = size
|
self.nmos_size = size
|
||||||
self.pmos_size = beta * size
|
self.pmos_size = beta * size
|
||||||
self.beta = beta
|
self.beta = beta
|
||||||
|
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height, add_wells)
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
""" Calls all functions related to the generation of the netlist """
|
""" Calls all functions related to the generation of the netlist """
|
||||||
|
|
@ -56,17 +57,18 @@ class pinv(pgate.pgate):
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
""" Calls all functions related to the generation of the layout """
|
""" Calls all functions related to the generation of the layout """
|
||||||
self.place_ptx()
|
self.place_ptx()
|
||||||
self.add_well_contacts()
|
if self.add_wells:
|
||||||
|
self.add_well_contacts()
|
||||||
self.determine_width()
|
self.determine_width()
|
||||||
self.extend_wells()
|
self.extend_wells()
|
||||||
self.route_supply_rails()
|
|
||||||
self.connect_rails()
|
|
||||||
self.route_input_gate(self.pmos_inst,
|
self.route_input_gate(self.pmos_inst,
|
||||||
self.nmos_inst,
|
self.nmos_inst,
|
||||||
self.output_pos.y,
|
self.output_pos.y,
|
||||||
"A",
|
"A",
|
||||||
position="farleft")
|
position="farleft")
|
||||||
self.route_outputs()
|
self.route_outputs()
|
||||||
|
self.route_supply_rails()
|
||||||
|
self.connect_rails()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 contact
|
||||||
|
import pinv
|
||||||
|
import debug
|
||||||
|
from tech import drc, parameter
|
||||||
|
from vector import vector
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
|
||||||
|
if(OPTS.tech_name == "s8"):
|
||||||
|
from tech import nmos_bins, pmos_bins, accuracy_requirement
|
||||||
|
|
||||||
|
|
||||||
|
class pinv_dec(pinv.pinv):
|
||||||
|
"""
|
||||||
|
This is another version of pinv but with layout for the decoder.
|
||||||
|
Other stuff is the same (netlist, sizes, etc.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True):
|
||||||
|
|
||||||
|
debug.info(2,
|
||||||
|
"creating pinv_dec structure {0} with size of {1}".format(name,
|
||||||
|
size))
|
||||||
|
if not height:
|
||||||
|
b = factory.create(module_type="bitcell")
|
||||||
|
self.cell_height = b.height
|
||||||
|
else:
|
||||||
|
self.cell_height = height
|
||||||
|
|
||||||
|
# Inputs to cells are on input layer
|
||||||
|
# Outputs from cells are on output layer
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
self.supply_layer = "m1"
|
||||||
|
else:
|
||||||
|
self.supply_layer = "m2"
|
||||||
|
|
||||||
|
pinv.pinv.__init__(self, name, size, beta, self.cell_height, add_wells)
|
||||||
|
|
||||||
|
def determine_tx_mults(self):
|
||||||
|
"""
|
||||||
|
Determines the number of fingers needed to achieve the size within
|
||||||
|
the height constraint. This may fail if the user has a tight height.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# This is always 1 tx, because we have horizontal transistors.
|
||||||
|
self.tx_mults = 1
|
||||||
|
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||||
|
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
(self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width)
|
||||||
|
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Over-ride the route input gate to call the horizontal version.
|
||||||
|
# Other top-level netlist and layout functions are not changed.
|
||||||
|
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None):
|
||||||
|
"""
|
||||||
|
Route the input gate to the left side of the cell for access.
|
||||||
|
Position is actually ignored and is left to be compatible with the pinv.
|
||||||
|
"""
|
||||||
|
|
||||||
|
nmos_gate_pin = nmos_inst.get_pin("G")
|
||||||
|
pmos_gate_pin = pmos_inst.get_pin("G")
|
||||||
|
|
||||||
|
# Check if the gates are aligned and give an error if they aren't!
|
||||||
|
if nmos_gate_pin.ll().y != pmos_gate_pin.ll().y:
|
||||||
|
self.gds_write("unaliged_gates.gds")
|
||||||
|
debug.check(nmos_gate_pin.ll().y == pmos_gate_pin.ll().y,
|
||||||
|
"Connecting unaligned gates not supported. See unaligned_gates.gds.")
|
||||||
|
|
||||||
|
# Pick point on the left of NMOS and up to PMOS
|
||||||
|
nmos_gate_pos = nmos_gate_pin.rc()
|
||||||
|
pmos_gate_pos = pmos_gate_pin.lc()
|
||||||
|
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
|
||||||
|
|
||||||
|
# Center is completely symmetric.
|
||||||
|
contact_width = contact.poly_contact.width
|
||||||
|
contact_offset = nmos_gate_pin.lc() \
|
||||||
|
- vector(self.poly_extend_active + 0.5 * contact_width, 0)
|
||||||
|
via = self.add_via_stack_center(from_layer="poly",
|
||||||
|
to_layer=self.route_layer,
|
||||||
|
offset=contact_offset,
|
||||||
|
directions=directions)
|
||||||
|
self.add_path("poly", [contact_offset, nmos_gate_pin.lc()])
|
||||||
|
|
||||||
|
self.add_layout_pin_rect_center(text=name,
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=contact_offset,
|
||||||
|
width=via.mod.second_layer_width,
|
||||||
|
height=via.mod.second_layer_height)
|
||||||
|
|
||||||
|
def determine_width(self):
|
||||||
|
self.width = self.pmos_inst.rx() + self.well_extend_active
|
||||||
|
|
||||||
|
def extend_wells(self):
|
||||||
|
""" Extend bottom to top for each well. """
|
||||||
|
|
||||||
|
from tech import layer
|
||||||
|
if "pwell" in layer:
|
||||||
|
ll = self.nmos_inst.ll() - self.nmos_inst.mod.active_offset
|
||||||
|
ur = self.nmos_inst.ur() + self.nmos_inst.mod.active_offset
|
||||||
|
self.add_rect(layer="pwell",
|
||||||
|
offset=ll,
|
||||||
|
width=ur.x - ll.x,
|
||||||
|
height=self.height - ll.y)
|
||||||
|
|
||||||
|
if "nwell" in layer:
|
||||||
|
ll = self.pmos_inst.ll() - self.pmos_inst.mod.active_offset
|
||||||
|
ur = self.pmos_inst.ur() + self.pmos_inst.mod.active_offset
|
||||||
|
self.add_rect(layer="nwell",
|
||||||
|
offset=ll - vector(self.nwell_enclose_active, 0),
|
||||||
|
width=ur.x - ll.x + self.nwell_enclose_active,
|
||||||
|
height=self.height - ll.y + 2 * self.nwell_enclose_active)
|
||||||
|
|
||||||
|
def place_ptx(self):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
|
||||||
|
# offset so that the input contact is over from the left edge by poly spacing
|
||||||
|
x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space
|
||||||
|
# center the transistor in the y-dimension
|
||||||
|
y_offset = self.nmos.width + self.active_space
|
||||||
|
self.nmos_pos = vector(x_offset, y_offset)
|
||||||
|
self.nmos_inst.place(self.nmos_pos)
|
||||||
|
self.nmos_inst.place(self.nmos_pos,
|
||||||
|
rotate=270)
|
||||||
|
# place PMOS so it is half a poly spacing down from the top
|
||||||
|
xoffset = self.nmos_inst.height + 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell")
|
||||||
|
self.pmos_pos = self.nmos_pos + vector(xoffset, 0)
|
||||||
|
self.pmos_inst.place(self.pmos_pos,
|
||||||
|
rotate=270)
|
||||||
|
|
||||||
|
# Output position will be in between the PMOS and NMOS drains
|
||||||
|
pmos_drain_pos = self.pmos_inst.get_pin("D").center()
|
||||||
|
nmos_drain_pos = self.nmos_inst.get_pin("D").center()
|
||||||
|
self.output_pos = vector(0.5 * (pmos_drain_pos.x + nmos_drain_pos.x), nmos_drain_pos.y)
|
||||||
|
|
||||||
|
def route_outputs(self):
|
||||||
|
"""
|
||||||
|
Route the output (drains) together.
|
||||||
|
Optionally, routes output to edge.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get the drain pin
|
||||||
|
nmos_drain_pin = self.nmos_inst.get_pin("D")
|
||||||
|
|
||||||
|
# Pick point at right most of NMOS and connect over to PMOS
|
||||||
|
nmos_drain_pos = nmos_drain_pin.lc()
|
||||||
|
right_side = vector(self.width, nmos_drain_pos.y)
|
||||||
|
|
||||||
|
self.add_layout_pin_segment_center("Z",
|
||||||
|
self.route_layer,
|
||||||
|
nmos_drain_pos,
|
||||||
|
right_side)
|
||||||
|
|
||||||
|
def add_well_contacts(self):
|
||||||
|
""" Add n/p well taps to the layout and connect to supplies """
|
||||||
|
|
||||||
|
source_pos = self.pmos_inst.get_pin("S").center()
|
||||||
|
contact_pos = vector(source_pos.x, self.height)
|
||||||
|
self.nwell_contact = self.add_via_center(layers=self.active_stack,
|
||||||
|
offset=contact_pos,
|
||||||
|
implant_type="n",
|
||||||
|
well_type="n")
|
||||||
|
self.add_via_stack_center(offset=contact_pos,
|
||||||
|
from_layer=self.active_stack[2],
|
||||||
|
to_layer=self.supply_layer)
|
||||||
|
|
||||||
|
source_pos = self.nmos_inst.get_pin("S").center()
|
||||||
|
contact_pos = vector(source_pos.x, self.height)
|
||||||
|
self.pwell_contact= self.add_via_center(layers=self.active_stack,
|
||||||
|
offset=contact_pos,
|
||||||
|
implant_type="p",
|
||||||
|
well_type="p")
|
||||||
|
self.add_via_stack_center(offset=contact_pos,
|
||||||
|
from_layer=self.active_stack[2],
|
||||||
|
to_layer=self.supply_layer)
|
||||||
|
|
||||||
|
def route_supply_rails(self):
|
||||||
|
pin = self.nmos_inst.get_pin("S")
|
||||||
|
source_pos = pin.center()
|
||||||
|
bottom_pos = source_pos.scale(1, 0)
|
||||||
|
top_pos = bottom_pos + vector(0, self.height)
|
||||||
|
self.add_layout_pin_segment_center("gnd",
|
||||||
|
self.supply_layer,
|
||||||
|
start=bottom_pos,
|
||||||
|
end=top_pos)
|
||||||
|
|
||||||
|
pin = self.pmos_inst.get_pin("S")
|
||||||
|
source_pos = pin.center()
|
||||||
|
bottom_pos = source_pos.scale(1, 0)
|
||||||
|
top_pos = bottom_pos + vector(0, self.height)
|
||||||
|
self.add_layout_pin_segment_center("vdd",
|
||||||
|
self.supply_layer,
|
||||||
|
start=bottom_pos,
|
||||||
|
end=top_pos)
|
||||||
|
|
||||||
|
def connect_rails(self):
|
||||||
|
""" Connect the nmos and pmos to its respective power rails """
|
||||||
|
|
||||||
|
source_pos = self.nmos_inst.get_pin("S").center()
|
||||||
|
self.add_via_stack_center(offset=source_pos,
|
||||||
|
from_layer=self.route_layer,
|
||||||
|
to_layer=self.supply_layer)
|
||||||
|
|
||||||
|
source_pos = self.pmos_inst.get_pin("S").center()
|
||||||
|
self.add_via_stack_center(offset=source_pos,
|
||||||
|
from_layer=self.route_layer,
|
||||||
|
to_layer=self.supply_layer)
|
||||||
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import contact
|
|
||||||
import pgate
|
import pgate
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
|
|
@ -20,7 +19,7 @@ class pnand2(pgate.pgate):
|
||||||
This module generates gds of a parametrically sized 2-input nand.
|
This module generates gds of a parametrically sized 2-input nand.
|
||||||
This model use ptx to generate a 2-input nand within a cetrain height.
|
This model use ptx to generate a 2-input nand within a cetrain height.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None):
|
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||||
""" Creates a cell for a simple 2 input nand """
|
""" Creates a cell for a simple 2 input nand """
|
||||||
|
|
||||||
debug.info(2,
|
debug.info(2,
|
||||||
|
|
@ -43,7 +42,7 @@ class pnand2(pgate.pgate):
|
||||||
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
|
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
|
||||||
|
|
||||||
# Creates the netlist and layout
|
# Creates the netlist and layout
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height, add_wells)
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
|
|
@ -55,13 +54,14 @@ class pnand2(pgate.pgate):
|
||||||
|
|
||||||
self.setup_layout_constants()
|
self.setup_layout_constants()
|
||||||
self.place_ptx()
|
self.place_ptx()
|
||||||
self.add_well_contacts()
|
if self.add_wells:
|
||||||
|
self.add_well_contacts()
|
||||||
|
self.route_output()
|
||||||
self.determine_width()
|
self.determine_width()
|
||||||
self.route_supply_rails()
|
self.route_supply_rails()
|
||||||
self.connect_rails()
|
self.connect_rails()
|
||||||
self.extend_wells()
|
self.extend_wells()
|
||||||
self.route_inputs()
|
self.route_inputs()
|
||||||
self.route_output()
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class pnand3(pgate.pgate):
|
||||||
This module generates gds of a parametrically sized 2-input nand.
|
This module generates gds of a parametrically sized 2-input nand.
|
||||||
This model use ptx to generate a 2-input nand within a cetrain height.
|
This model use ptx to generate a 2-input nand within a cetrain height.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None):
|
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||||
""" Creates a cell for a simple 3 input nand """
|
""" Creates a cell for a simple 3 input nand """
|
||||||
|
|
||||||
debug.info(2,
|
debug.info(2,
|
||||||
|
|
@ -45,7 +45,7 @@ class pnand3(pgate.pgate):
|
||||||
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
|
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
|
||||||
|
|
||||||
# Creates the netlist and layout
|
# Creates the netlist and layout
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height, add_wells)
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" Adds pins for spice netlist """
|
""" Adds pins for spice netlist """
|
||||||
|
|
@ -63,13 +63,14 @@ class pnand3(pgate.pgate):
|
||||||
|
|
||||||
self.setup_layout_constants()
|
self.setup_layout_constants()
|
||||||
self.place_ptx()
|
self.place_ptx()
|
||||||
self.add_well_contacts()
|
if self.add_wells:
|
||||||
|
self.add_well_contacts()
|
||||||
|
self.route_inputs()
|
||||||
|
self.route_output()
|
||||||
self.determine_width()
|
self.determine_width()
|
||||||
self.route_supply_rails()
|
self.route_supply_rails()
|
||||||
self.connect_rails()
|
self.connect_rails()
|
||||||
self.extend_wells()
|
self.extend_wells()
|
||||||
self.route_inputs()
|
|
||||||
self.route_output()
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
def add_ptx(self):
|
def add_ptx(self):
|
||||||
|
|
@ -211,7 +212,10 @@ class pnand3(pgate.pgate):
|
||||||
pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
|
pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
|
||||||
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
|
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
|
||||||
|
|
||||||
|
# This is a more compact offset, but the bottom one works better in the decoders to "center" the pins
|
||||||
|
# in the height of the gates
|
||||||
self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space
|
self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space
|
||||||
|
# self.inputA_yoffset = self.output_yoffset - self.m1_pitch
|
||||||
self.route_input_gate(self.pmos1_inst,
|
self.route_input_gate(self.pmos1_inst,
|
||||||
self.nmos1_inst,
|
self.nmos1_inst,
|
||||||
self.inputA_yoffset,
|
self.inputA_yoffset,
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class pnor2(pgate.pgate):
|
||||||
This module generates gds of a parametrically sized 2-input nor.
|
This module generates gds of a parametrically sized 2-input nor.
|
||||||
This model use ptx to generate a 2-input nor within a cetrain height.
|
This model use ptx to generate a 2-input nor within a cetrain height.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None):
|
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||||
""" Creates a cell for a simple 2 input nor """
|
""" Creates a cell for a simple 2 input nor """
|
||||||
|
|
||||||
debug.info(2,
|
debug.info(2,
|
||||||
|
|
@ -42,7 +42,7 @@ class pnor2(pgate.pgate):
|
||||||
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
|
(self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width)
|
||||||
|
|
||||||
# Creates the netlist and layout
|
# Creates the netlist and layout
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.pgate.__init__(self, name, height, add_wells)
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
|
|
@ -54,13 +54,14 @@ class pnor2(pgate.pgate):
|
||||||
|
|
||||||
self.setup_layout_constants()
|
self.setup_layout_constants()
|
||||||
self.place_ptx()
|
self.place_ptx()
|
||||||
self.add_well_contacts()
|
if self.add_wells:
|
||||||
|
self.add_well_contacts()
|
||||||
|
self.route_inputs()
|
||||||
|
self.route_output()
|
||||||
self.determine_width()
|
self.determine_width()
|
||||||
self.route_supply_rails()
|
self.route_supply_rails()
|
||||||
self.connect_rails()
|
self.connect_rails()
|
||||||
self.extend_wells()
|
self.extend_wells()
|
||||||
self.route_inputs()
|
|
||||||
self.route_output()
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
|
||||||
|
|
@ -105,21 +105,16 @@ class precharge(design.design):
|
||||||
|
|
||||||
# center of vdd rail
|
# center of vdd rail
|
||||||
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
|
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
|
||||||
self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos])
|
self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos])
|
||||||
|
|
||||||
# if enable is not on M1, the supply can be
|
|
||||||
if self.en_layer != "m1":
|
|
||||||
self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=pmos_vdd_pos)
|
|
||||||
|
|
||||||
self.add_power_pin("vdd",
|
self.add_power_pin("vdd",
|
||||||
self.well_contact_pos,
|
self.well_contact_pos,
|
||||||
directions=("V", "V"))
|
directions=("V", "V"))
|
||||||
|
|
||||||
# Hack for li layers
|
self.add_via_stack_center(from_layer=pmos_pin.layer,
|
||||||
if hasattr(self, "li_stack"):
|
to_layer=self.en_layer,
|
||||||
self.add_via_center(layers=self.li_stack,
|
offset=pmos_pin.center(),
|
||||||
offset=self.well_contact_pos)
|
directions=("V", "V"))
|
||||||
|
|
||||||
def create_ptx(self):
|
def create_ptx(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -196,19 +191,15 @@ class precharge(design.design):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# adds the en contact to connect the gates to the en rail
|
# adds the en contact to connect the gates to the en rail
|
||||||
# midway in the 4 M2 tracks
|
pin_offset = self.lower_pmos_inst.get_pin("G").lr()
|
||||||
offset = self.lower_pmos_inst.get_pin("G").ul() \
|
# This is an extra space down for some techs with contact to active spacing
|
||||||
+ vector(0, 0.5 * self.m2_pitch)
|
offset = pin_offset - vector(0, self.poly_space)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_stack_center(from_layer="poly",
|
||||||
offset=offset)
|
to_layer=self.en_layer,
|
||||||
if self.en_layer == "m2":
|
offset=offset)
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_path("poly",
|
||||||
offset=offset)
|
[self.lower_pmos_inst.get_pin("G").bc(), offset])
|
||||||
if hasattr(self, "li_stack"):
|
# adds the en rail
|
||||||
self.add_via_center(layers=self.li_stack,
|
|
||||||
offset=offset)
|
|
||||||
|
|
||||||
# adds the en rail on metal1
|
|
||||||
self.add_layout_pin_segment_center(text="en_bar",
|
self.add_layout_pin_segment_center(text="en_bar",
|
||||||
layer=self.en_layer,
|
layer=self.en_layer,
|
||||||
start=offset.scale(0, 1),
|
start=offset.scale(0, 1),
|
||||||
|
|
@ -225,13 +216,13 @@ class precharge(design.design):
|
||||||
self.nwell_extend_active
|
self.nwell_extend_active
|
||||||
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \
|
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \
|
||||||
vector(0, offset_height)
|
vector(0, offset_height)
|
||||||
self.add_via_center(layers=self.active_stack,
|
self.well_contact = self.add_via_center(layers=self.active_stack,
|
||||||
offset=self.well_contact_pos,
|
offset=self.well_contact_pos,
|
||||||
implant_type="n",
|
implant_type="n",
|
||||||
well_type="n")
|
well_type="n")
|
||||||
if hasattr(self, "li_stack"):
|
self.add_via_stack_center(from_layer=self.active_stack[2],
|
||||||
self.add_via_center(layers=self.li_stack,
|
to_layer=self.bitline_layer,
|
||||||
offset=self.well_contact_pos)
|
offset=self.well_contact_pos)
|
||||||
|
|
||||||
self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space
|
self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space
|
||||||
|
|
||||||
|
|
@ -245,11 +236,10 @@ class precharge(design.design):
|
||||||
"""
|
"""
|
||||||
Adds both bit-line and bit-line-bar to the module
|
Adds both bit-line and bit-line-bar to the module
|
||||||
"""
|
"""
|
||||||
layer_width = drc("minwidth_" + self.bitline_layer)
|
layer_pitch = getattr(self, "{}_pitch".format(self.bitline_layer))
|
||||||
layer_space = drc("{0}_to_{0}".format(self.bitline_layer))
|
|
||||||
|
|
||||||
# adds the BL
|
# adds the BL
|
||||||
self.bl_xoffset = layer_space + 0.5 * layer_width
|
self.bl_xoffset = layer_pitch
|
||||||
top_pos = vector(self.bl_xoffset, self.height)
|
top_pos = vector(self.bl_xoffset, self.height)
|
||||||
pin_pos = vector(self.bl_xoffset, 0)
|
pin_pos = vector(self.bl_xoffset, 0)
|
||||||
self.add_path(self.bitline_layer, [top_pos, pin_pos])
|
self.add_path(self.bitline_layer, [top_pos, pin_pos])
|
||||||
|
|
@ -259,7 +249,7 @@ class precharge(design.design):
|
||||||
end=top_pos)
|
end=top_pos)
|
||||||
|
|
||||||
# adds the BR
|
# adds the BR
|
||||||
self.br_xoffset = self.width - layer_space - 0.5 * layer_width
|
self.br_xoffset = self.width - layer_pitch
|
||||||
top_pos = vector(self.br_xoffset, self.height)
|
top_pos = vector(self.br_xoffset, self.height)
|
||||||
pin_pos = vector(self.br_xoffset, 0)
|
pin_pos = vector(self.br_xoffset, 0)
|
||||||
self.add_path(self.bitline_layer, [top_pos, pin_pos])
|
self.add_path(self.bitline_layer, [top_pos, pin_pos])
|
||||||
|
|
@ -288,31 +278,19 @@ class precharge(design.design):
|
||||||
Adds contacts/via from metal1 to metal2 for bit-lines
|
Adds contacts/via from metal1 to metal2 for bit-lines
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# No contacts needed if M1
|
|
||||||
if self.bitline_layer == "m1":
|
|
||||||
return
|
|
||||||
|
|
||||||
# BL
|
# BL
|
||||||
lower_pin = self.lower_pmos_inst.get_pin("S")
|
for lower_pin in [self.lower_pmos_inst.get_pin("S"), self.lower_pmos_inst.get_pin("D")]:
|
||||||
self.lower_via = self.add_via_center(layers=self.m1_stack,
|
self.add_via_stack_center(from_layer=lower_pin.layer,
|
||||||
offset=lower_pin.center(),
|
to_layer=self.bitline_layer,
|
||||||
directions=("V", "V"))
|
offset=lower_pin.center(),
|
||||||
|
directions=("V", "V"))
|
||||||
|
|
||||||
lower_pin = self.lower_pmos_inst.get_pin("D")
|
|
||||||
self.lower_via = self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=lower_pin.center(),
|
|
||||||
directions=("V", "V"))
|
|
||||||
|
|
||||||
# BR
|
# BR
|
||||||
upper_pin = self.upper_pmos1_inst.get_pin("S")
|
for upper_pin in [self.upper_pmos1_inst.get_pin("S"), self.upper_pmos2_inst.get_pin("D")]:
|
||||||
self.upper_via2 = self.add_via_center(layers=self.m1_stack,
|
self.add_via_stack_center(from_layer=upper_pin.layer,
|
||||||
offset=upper_pin.center(),
|
to_layer=self.bitline_layer,
|
||||||
directions=("V", "V"))
|
offset=upper_pin.center(),
|
||||||
|
directions=("V", "V"))
|
||||||
upper_pin = self.upper_pmos2_inst.get_pin("D")
|
|
||||||
self.upper_via2 = self.add_via_center(layers=self.m1_stack,
|
|
||||||
offset=upper_pin.center(),
|
|
||||||
directions=("V", "V"))
|
|
||||||
|
|
||||||
def connect_pmos(self, pmos_pin, bit_xoffset):
|
def connect_pmos(self, pmos_pin, bit_xoffset):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -386,10 +386,12 @@ class ptx(design.design):
|
||||||
well_ll = center_pos - vector(0.5 * self.well_width,
|
well_ll = center_pos - vector(0.5 * self.well_width,
|
||||||
0.5 * self.well_height)
|
0.5 * self.well_height)
|
||||||
if well_name in layer:
|
if well_name in layer:
|
||||||
self.add_rect(layer=well_name,
|
well = self.add_rect(layer=well_name,
|
||||||
offset=well_ll,
|
offset=well_ll,
|
||||||
width=self.well_width,
|
width=self.well_width,
|
||||||
height=self.well_height)
|
height=self.well_height)
|
||||||
|
setattr(self, well_name, well)
|
||||||
|
|
||||||
if "vtg" in layer:
|
if "vtg" in layer:
|
||||||
self.add_rect(layer="vtg",
|
self.add_rect(layer="vtg",
|
||||||
offset=well_ll,
|
offset=well_ll,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from tech import drc, layer
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import logical_effort
|
import logical_effort
|
||||||
|
from utils import round_to_grid
|
||||||
|
|
||||||
|
|
||||||
class single_level_column_mux(pgate.pgate):
|
class single_level_column_mux(pgate.pgate):
|
||||||
|
|
@ -44,13 +45,22 @@ class single_level_column_mux(pgate.pgate):
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
|
|
||||||
self.pin_height = 2 * self.m2_width
|
# If li exists, use li and m1 for the mux, otherwise use m1 and m2
|
||||||
|
if "li" in layer:
|
||||||
|
self.col_mux_stack = self.li_stack
|
||||||
|
else:
|
||||||
|
self.col_mux_stack = self.m1_stack
|
||||||
|
self.pin_layer = self.bitcell.get_pin(self.bitcell_bl).layer
|
||||||
|
self.pin_pitch = getattr(self, "{}_pitch".format(self.pin_layer))
|
||||||
|
self.pin_width = getattr(self, "{}_width".format(self.pin_layer))
|
||||||
|
self.pin_height = 2 * self.pin_width
|
||||||
self.width = self.bitcell.width
|
self.width = self.bitcell.width
|
||||||
self.height = self.nmos_upper.uy() + self.pin_height
|
self.height = self.nmos_upper.uy() + self.pin_height
|
||||||
|
|
||||||
self.connect_poly()
|
self.connect_poly()
|
||||||
self.add_bitline_pins()
|
self.add_bitline_pins()
|
||||||
self.connect_bitlines()
|
self.connect_bitlines()
|
||||||
self.add_wells()
|
self.add_pn_wells()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.bitcell = factory.create(module_type="bitcell")
|
self.bitcell = factory.create(module_type="bitcell")
|
||||||
|
|
@ -58,9 +68,7 @@ class single_level_column_mux(pgate.pgate):
|
||||||
# Adds nmos_lower,nmos_upper to the module
|
# Adds nmos_lower,nmos_upper to the module
|
||||||
self.ptx_width = self.tx_size * drc("minwidth_tx")
|
self.ptx_width = self.tx_size * drc("minwidth_tx")
|
||||||
self.nmos = factory.create(module_type="ptx",
|
self.nmos = factory.create(module_type="ptx",
|
||||||
width=self.ptx_width,
|
width=self.ptx_width)
|
||||||
add_source_contact=False,
|
|
||||||
add_drain_contact=False)
|
|
||||||
self.add_mod(self.nmos)
|
self.add_mod(self.nmos)
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
|
|
@ -69,29 +77,26 @@ class single_level_column_mux(pgate.pgate):
|
||||||
def add_bitline_pins(self):
|
def add_bitline_pins(self):
|
||||||
""" Add the top and bottom pins to this cell """
|
""" Add the top and bottom pins to this cell """
|
||||||
|
|
||||||
bl_pin=self.bitcell.get_pin(self.bitcell_bl)
|
bl_pos = vector(self.pin_pitch, 0)
|
||||||
br_pin=self.bitcell.get_pin(self.bitcell_br)
|
br_pos = vector(self.width - self.pin_pitch, 0)
|
||||||
|
|
||||||
bl_pos = vector(bl_pin.lx(), 0)
|
|
||||||
br_pos = vector(br_pin.lx(), 0)
|
|
||||||
|
|
||||||
# bl and br
|
# bl and br
|
||||||
self.add_layout_pin(text="bl",
|
self.add_layout_pin(text="bl",
|
||||||
layer=bl_pin.layer,
|
layer=self.pin_layer,
|
||||||
offset=bl_pos + vector(0, self.height - self.pin_height),
|
offset=bl_pos + vector(0, self.height - self.pin_height),
|
||||||
height=self.pin_height)
|
height=self.pin_height)
|
||||||
self.add_layout_pin(text="br",
|
self.add_layout_pin(text="br",
|
||||||
layer=br_pin.layer,
|
layer=self.pin_layer,
|
||||||
offset=br_pos + vector(0, self.height - self.pin_height),
|
offset=br_pos + vector(0, self.height - self.pin_height),
|
||||||
height=self.pin_height)
|
height=self.pin_height)
|
||||||
|
|
||||||
# bl_out and br_out
|
# bl_out and br_out
|
||||||
self.add_layout_pin(text="bl_out",
|
self.add_layout_pin(text="bl_out",
|
||||||
layer=bl_pin.layer,
|
layer=self.pin_layer,
|
||||||
offset=bl_pos,
|
offset=bl_pos,
|
||||||
height=self.pin_height)
|
height=self.pin_height)
|
||||||
self.add_layout_pin(text="br_out",
|
self.add_layout_pin(text="br_out",
|
||||||
layer=br_pin.layer,
|
layer=self.pin_layer,
|
||||||
offset=br_pos,
|
offset=br_pos,
|
||||||
height=self.pin_height)
|
height=self.pin_height)
|
||||||
|
|
||||||
|
|
@ -99,7 +104,7 @@ class single_level_column_mux(pgate.pgate):
|
||||||
""" Create the two pass gate NMOS transistors to switch the bitlines"""
|
""" Create the two pass gate NMOS transistors to switch the bitlines"""
|
||||||
|
|
||||||
# Space it in the center
|
# Space it in the center
|
||||||
nmos_lower_position = self.nmos.active_offset.scale(0,1) \
|
nmos_lower_position = self.nmos.active_offset.scale(0, 1) \
|
||||||
+ vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
|
+ vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
|
||||||
self.nmos_lower = self.add_inst(name="mux_tx1",
|
self.nmos_lower = self.add_inst(name="mux_tx1",
|
||||||
mod=self.nmos,
|
mod=self.nmos,
|
||||||
|
|
@ -133,63 +138,30 @@ class single_level_column_mux(pgate.pgate):
|
||||||
|
|
||||||
def connect_bitlines(self):
|
def connect_bitlines(self):
|
||||||
""" Connect the bitlines to the mux transistors """
|
""" Connect the bitlines to the mux transistors """
|
||||||
|
|
||||||
# If li exists, use li and m1 for the mux, otherwise use m1 and m2
|
|
||||||
if "li" in layer:
|
|
||||||
self.col_mux_stack = self.li_stack
|
|
||||||
else:
|
|
||||||
self.col_mux_stack = self.m1_stack
|
|
||||||
|
|
||||||
# These are on metal2
|
|
||||||
bl_pin = self.get_pin("bl")
|
bl_pin = self.get_pin("bl")
|
||||||
br_pin = self.get_pin("br")
|
br_pin = self.get_pin("br")
|
||||||
bl_out_pin = self.get_pin("bl_out")
|
bl_out_pin = self.get_pin("bl_out")
|
||||||
br_out_pin = self.get_pin("br_out")
|
br_out_pin = self.get_pin("br_out")
|
||||||
|
|
||||||
# These are on metal1
|
|
||||||
nmos_lower_s_pin = self.nmos_lower.get_pin("S")
|
nmos_lower_s_pin = self.nmos_lower.get_pin("S")
|
||||||
nmos_lower_d_pin = self.nmos_lower.get_pin("D")
|
nmos_lower_d_pin = self.nmos_lower.get_pin("D")
|
||||||
nmos_upper_s_pin = self.nmos_upper.get_pin("S")
|
nmos_upper_s_pin = self.nmos_upper.get_pin("S")
|
||||||
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
|
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
|
||||||
|
|
||||||
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
|
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
|
||||||
self.add_via_center(layers=self.col_mux_stack,
|
self.add_via_stack_center(from_layer=bl_pin.layer,
|
||||||
offset=bl_pin.bc(),
|
to_layer=self.col_mux_stack[0],
|
||||||
directions=("V", "V"))
|
offset=bl_pin.bc())
|
||||||
self.add_via_center(layers=self.col_mux_stack,
|
self.add_via_stack_center(from_layer=br_out_pin.layer,
|
||||||
offset=br_out_pin.uc(),
|
to_layer=self.col_mux_stack[0],
|
||||||
directions=("V", "V"))
|
offset=br_out_pin.uc())
|
||||||
self.add_via_center(layers=self.col_mux_stack,
|
self.add_via_stack_center(from_layer=nmos_upper_s_pin.layer,
|
||||||
offset=nmos_upper_s_pin.center(),
|
to_layer=self.col_mux_stack[2],
|
||||||
directions=("V", "V"))
|
offset=nmos_upper_s_pin.center())
|
||||||
self.add_via_center(layers=self.col_mux_stack,
|
self.add_via_stack_center(from_layer=nmos_lower_d_pin.layer,
|
||||||
offset=nmos_lower_d_pin.center(),
|
to_layer=self.col_mux_stack[2],
|
||||||
directions=("V", "V"))
|
offset=nmos_lower_d_pin.center())
|
||||||
|
|
||||||
# Add diffusion contacts
|
|
||||||
# These were previously omitted with the options: add_source_contact=False, add_drain_contact=False
|
|
||||||
# They are added now and not previously so that they do not include m1 (which is usually included by default)
|
|
||||||
# This is only a concern when the local interconnect (li) layer is being used
|
|
||||||
self.add_via_center(layers=self.active_stack,
|
|
||||||
offset=nmos_upper_d_pin.center(),
|
|
||||||
directions=("V", "V"),
|
|
||||||
implant_type="n",
|
|
||||||
well_type="nwell")
|
|
||||||
self.add_via_center(layers=self.active_stack,
|
|
||||||
offset=nmos_lower_s_pin.center(),
|
|
||||||
directions=("V", "V"),
|
|
||||||
implant_type="n",
|
|
||||||
well_type="nwell")
|
|
||||||
self.add_via_center(layers=self.active_stack,
|
|
||||||
offset=nmos_upper_s_pin.center(),
|
|
||||||
directions=("V", "V"),
|
|
||||||
implant_type="n",
|
|
||||||
well_type="nwell")
|
|
||||||
self.add_via_center(layers=self.active_stack,
|
|
||||||
offset=nmos_lower_d_pin.center(),
|
|
||||||
directions=("V", "V"),
|
|
||||||
implant_type="n",
|
|
||||||
well_type="nwell")
|
|
||||||
|
|
||||||
# bl -> nmos_upper/D on metal1
|
# bl -> nmos_upper/D on metal1
|
||||||
# bl_out -> nmos_upper/S on metal2
|
# bl_out -> nmos_upper/S on metal2
|
||||||
|
|
@ -218,7 +190,7 @@ class single_level_column_mux(pgate.pgate):
|
||||||
self.add_path(self.col_mux_stack[2],
|
self.add_path(self.col_mux_stack[2],
|
||||||
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()])
|
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.center()])
|
||||||
|
|
||||||
def add_wells(self):
|
def add_pn_wells(self):
|
||||||
"""
|
"""
|
||||||
Add a well and implant over the whole cell. Also, add the
|
Add a well and implant over the whole cell. Also, add the
|
||||||
pwell contact (if it exists)
|
pwell contact (if it exists)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 debug
|
||||||
|
from vector import vector
|
||||||
|
import design
|
||||||
|
from sram_factory import factory
|
||||||
|
from globals import OPTS
|
||||||
|
from tech import layer
|
||||||
|
|
||||||
|
|
||||||
|
class wordline_driver(design.design):
|
||||||
|
"""
|
||||||
|
This is an AND (or NAND) with configurable drive strength to drive the wordlines.
|
||||||
|
It is matched to the bitcell height.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, size=1, height=None):
|
||||||
|
debug.info(1, "Creating wordline_driver {}".format(name))
|
||||||
|
self.add_comment("size: {}".format(size))
|
||||||
|
design.design.__init__(self, name)
|
||||||
|
|
||||||
|
if height is None:
|
||||||
|
b = factory.create(module_type="bitcell")
|
||||||
|
self.height = b.height
|
||||||
|
else:
|
||||||
|
self.height = height
|
||||||
|
self.size = size
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
self.create_layout()
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
self.add_pins()
|
||||||
|
self.create_modules()
|
||||||
|
self.create_insts()
|
||||||
|
|
||||||
|
def create_modules(self):
|
||||||
|
self.nand = factory.create(module_type="nand2_dec",
|
||||||
|
height=self.height)
|
||||||
|
|
||||||
|
self.driver = factory.create(module_type="inv_dec",
|
||||||
|
size=self.size,
|
||||||
|
height=self.nand.height)
|
||||||
|
|
||||||
|
self.add_mod(self.nand)
|
||||||
|
self.add_mod(self.driver)
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
self.width = self.nand.width + self.driver.width
|
||||||
|
if "li" in layer:
|
||||||
|
self.route_layer = "li"
|
||||||
|
else:
|
||||||
|
self.route_layer = "m1"
|
||||||
|
|
||||||
|
self.place_insts()
|
||||||
|
self.route_wires()
|
||||||
|
self.add_layout_pins()
|
||||||
|
self.route_supply_rails()
|
||||||
|
self.add_boundary()
|
||||||
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
def add_pins(self):
|
||||||
|
self.add_pin("A", "INPUT")
|
||||||
|
self.add_pin("B", "INPUT")
|
||||||
|
self.add_pin("Z", "OUTPUT")
|
||||||
|
self.add_pin("vdd", "POWER")
|
||||||
|
self.add_pin("gnd", "GROUND")
|
||||||
|
|
||||||
|
def create_insts(self):
|
||||||
|
self.nand_inst = self.add_inst(name="wld_nand",
|
||||||
|
mod=self.nand)
|
||||||
|
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
|
self.driver_inst = self.add_inst(name="wl_driver",
|
||||||
|
mod=self.driver)
|
||||||
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_insts(self):
|
||||||
|
# Add NAND to the right
|
||||||
|
self.nand_inst.place(offset=vector(0, 0))
|
||||||
|
|
||||||
|
# Add INV to the right
|
||||||
|
self.driver_inst.place(offset=vector(self.nand_inst.rx(), 0))
|
||||||
|
|
||||||
|
def route_supply_rails(self):
|
||||||
|
""" Add vdd/gnd rails to the top, (middle), and bottom. """
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
for name in ["vdd", "gnd"]:
|
||||||
|
for inst in [self.nand_inst, self.driver_inst]:
|
||||||
|
self.copy_layout_pin(inst, name)
|
||||||
|
else:
|
||||||
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, 0),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
y_offset = self.height
|
||||||
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
|
layer=self.route_layer,
|
||||||
|
offset=vector(0.5 * self.width, y_offset),
|
||||||
|
width=self.width)
|
||||||
|
|
||||||
|
def route_wires(self):
|
||||||
|
|
||||||
|
# nand Z to inv A
|
||||||
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
a2_pin = self.driver_inst.get_pin("A")
|
||||||
|
if OPTS.tech_name == "s8":
|
||||||
|
mid1_point = vector(a2_pin.cx(), z1_pin.cy())
|
||||||
|
else:
|
||||||
|
mid1_point = vector(z1_pin.cx(), a2_pin.cy())
|
||||||
|
self.add_path(self.route_layer,
|
||||||
|
[z1_pin.center(), mid1_point, a2_pin.center()])
|
||||||
|
|
||||||
|
def add_layout_pins(self):
|
||||||
|
pin = self.driver_inst.get_pin("Z")
|
||||||
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
for pin_name in ["A", "B"]:
|
||||||
|
pin = self.nand_inst.get_pin(pin_name)
|
||||||
|
self.add_layout_pin_rect_center(text=pin_name,
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
def get_stage_efforts(self, external_cout, inp_is_rise=False):
|
||||||
|
"""Get the stage efforts of the A or B -> Z path"""
|
||||||
|
stage_effort_list = []
|
||||||
|
stage1_cout = self.driver.get_cin()
|
||||||
|
stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise)
|
||||||
|
stage_effort_list.append(stage1)
|
||||||
|
|
||||||
|
stage2 = self.driver.get_stage_effort(external_cout, stage1.is_rise)
|
||||||
|
stage_effort_list.append(stage2)
|
||||||
|
|
||||||
|
return stage_effort_list
|
||||||
|
|
||||||
|
def get_cin(self):
|
||||||
|
"""Return the relative input capacitance of a single input"""
|
||||||
|
return self.nand.get_cin()
|
||||||
|
|
||||||
|
|
@ -189,7 +189,7 @@ class sram_1bank(sram_base):
|
||||||
if self.num_spare_cols:
|
if self.num_spare_cols:
|
||||||
spare_wen_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width
|
spare_wen_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width
|
||||||
- self.spare_wen_dff_insts[port].width - self.bank.m2_gap,
|
- self.spare_wen_dff_insts[port].width - self.bank.m2_gap,
|
||||||
self.bank.height + bus_size + self.dff.height))
|
self.bank.height + bus_size + self.dff.height)
|
||||||
self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX")
|
self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX")
|
||||||
|
|
||||||
# Place dffs when spare cols is enabled
|
# Place dffs when spare cols is enabled
|
||||||
|
|
@ -400,7 +400,9 @@ class sram_1bank(sram_base):
|
||||||
# Only input (besides pins) is the replica bitline
|
# Only input (besides pins) is the replica bitline
|
||||||
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
|
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
|
||||||
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
||||||
self.connect_hbus(src_pin, dest_pin)
|
self.add_wire(self.m2_stack[::-1],
|
||||||
|
[src_pin.center(), vector(src_pin.cx(), dest_pin.cy()), dest_pin.rc()])
|
||||||
|
# self.connect_hbus(src_pin, dest_pin)
|
||||||
|
|
||||||
def route_row_addr_dff(self):
|
def route_row_addr_dff(self):
|
||||||
""" Connect the output of the row flops to the bank pins """
|
""" Connect the output of the row flops to the bank pins """
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,9 @@ class sram_factory:
|
||||||
"""
|
"""
|
||||||
tech_module_type, tm_overridden = self.get_techmodule_type(module_type)
|
tech_module_type, tm_overridden = self.get_techmodule_type(module_type)
|
||||||
user_module_type, um_overridden = self.get_usermodule_type(module_type)
|
user_module_type, um_overridden = self.get_usermodule_type(module_type)
|
||||||
|
# print(module_type, tech_module_type, tm_overridden)
|
||||||
|
# print(module_type, user_module_type, um_overridden)
|
||||||
|
|
||||||
# overridden user modules have priority
|
# overridden user modules have priority
|
||||||
if um_overridden:
|
if um_overridden:
|
||||||
real_module_type = user_module_type
|
real_module_type = user_module_type
|
||||||
|
|
@ -109,11 +111,12 @@ class sram_factory:
|
||||||
return obj_item
|
return obj_item
|
||||||
|
|
||||||
# If no prefered module name is provided, we generate one.
|
# If no prefered module name is provided, we generate one.
|
||||||
if module_name is None:
|
if not module_name:
|
||||||
# Use the default name if there are default arguments
|
# Use the default name for the first cell.
|
||||||
# This is especially for library cells so that the
|
# This is especially for library cells so that the
|
||||||
# spice and gds files can be found.
|
# spice and gds files can be found.
|
||||||
if len(kwargs) > 0:
|
# Subsequent objects will get unique names to help with GDS limitation.
|
||||||
|
if len(self.objects[real_module_type]) > 0:
|
||||||
# Create a unique name and increment the index
|
# Create a unique name and increment the index
|
||||||
module_name = "{0}_{1}".format(real_module_type,
|
module_name = "{0}_{1}".format(real_module_type,
|
||||||
self.module_indices[real_module_type])
|
self.module_indices[real_module_type])
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class library_lvs_test(openram_test):
|
||||||
debug.error("Missing GDS file {}".format(gds_name))
|
debug.error("Missing GDS file {}".format(gds_name))
|
||||||
if not os.path.isfile(sp_name):
|
if not os.path.isfile(sp_name):
|
||||||
lvs_errors += 1
|
lvs_errors += 1
|
||||||
debug.error("Missing SPICE file {}".format(gds_name))
|
debug.error("Missing SPICE file {}".format(sp_name))
|
||||||
drc_errors += verify.run_drc(name, gds_name)
|
drc_errors += verify.run_drc(name, gds_name)
|
||||||
lvs_errors += verify.run_lvs(f, gds_name, sp_name)
|
lvs_errors += verify.run_lvs(f, gds_name, sp_name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class and2_dec_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
global verify
|
||||||
|
import verify
|
||||||
|
|
||||||
|
import and2_dec
|
||||||
|
|
||||||
|
debug.info(2, "Testing and2 gate 4x")
|
||||||
|
a = and2_dec.and2_dec(name="and2x4", size=4)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# instantiate a copdsay of the class to actually run the test
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main(testRunner=debugTestRunner())
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class and3_dec_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
global verify
|
||||||
|
import verify
|
||||||
|
|
||||||
|
import and3_dec
|
||||||
|
|
||||||
|
debug.info(2, "Testing and3 gate 4x")
|
||||||
|
a = and3_dec.and3_dec(name="and3x4", size=4)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# instantiate a copdsay of the class to actually run the test
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main(testRunner=debugTestRunner())
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class and3_dec_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
global verify
|
||||||
|
import verify
|
||||||
|
|
||||||
|
import and3_dec
|
||||||
|
|
||||||
|
debug.info(2, "Testing and3 gate 4x")
|
||||||
|
a = and3_dec.and3_dec(name="and3x4", size=4)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# instantiate a copdsay of the class to actually run the test
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main(testRunner=debugTestRunner())
|
||||||
|
|
@ -29,6 +29,10 @@ class pand2_test(openram_test):
|
||||||
a = pand2.pand2(name="pand2x4", size=4)
|
a = pand2.pand2(name="pand2x4", size=4)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
|
debug.info(2, "Testing vertical pand2 gate 4x")
|
||||||
|
a = pand2.pand2(name="pand2x4", size=4, vertical=True)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# instantiate a copdsay of the class to actually run the test
|
# instantiate a copdsay of the class to actually run the test
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ class pand3_test(openram_test):
|
||||||
a = pand3.pand3(name="pand3x4", size=4)
|
a = pand3.pand3(name="pand3x4", size=4)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
|
debug.info(2, "Testing vertical pand3 gate 4x")
|
||||||
|
a = pand3.pand3(name="pand3x4", size=4, vertical=True)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# instantiate a copdsay of the class to actually run the test
|
# instantiate a copdsay of the class to actually run the test
|
||||||
|
|
|
||||||
|
|
@ -32,13 +32,13 @@ class pdriver_test(openram_test):
|
||||||
c = factory.create(module_type="pdriver", fanout = 50)
|
c = factory.create(module_type="pdriver", fanout = 50)
|
||||||
self.local_check(c)
|
self.local_check(c)
|
||||||
|
|
||||||
d = factory.create(module_type="pdriver", fanout = 50, neg_polarity = True)
|
d = factory.create(module_type="pdriver", fanout = 50, inverting = True)
|
||||||
self.local_check(d)
|
self.local_check(d)
|
||||||
|
|
||||||
e = factory.create(module_type="pdriver", fanout = 64)
|
e = factory.create(module_type="pdriver", fanout = 64)
|
||||||
self.local_check(e)
|
self.local_check(e)
|
||||||
|
|
||||||
f = factory.create(module_type="pdriver", fanout = 64, neg_polarity = True)
|
f = factory.create(module_type="pdriver", fanout = 64, inverting = True)
|
||||||
self.local_check(f)
|
self.local_check(f)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class pinv_dec_1x_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
debug.info(2, "Checking 1x size decoder inverter")
|
||||||
|
tx = factory.create(module_type="pinv_dec", 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(testRunner=debugTestRunner())
|
||||||
|
|
@ -25,6 +25,11 @@ class pnand2_test(openram_test):
|
||||||
tx = factory.create(module_type="pnand2", size=1)
|
tx = factory.create(module_type="pnand2", size=1)
|
||||||
self.local_check(tx)
|
self.local_check(tx)
|
||||||
|
|
||||||
|
debug.info(2, "Checking 2-input nand gate")
|
||||||
|
tx = factory.create(module_type="pnand2", size=1, add_wells=False)
|
||||||
|
# Only DRC because well contacts will fail LVS
|
||||||
|
self.local_drc_check(tx)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ class pnand3_test(openram_test):
|
||||||
tx = factory.create(module_type="pnand3", size=1)
|
tx = factory.create(module_type="pnand3", size=1)
|
||||||
self.local_check(tx)
|
self.local_check(tx)
|
||||||
|
|
||||||
|
debug.info(2, "Checking 3-input nand gate")
|
||||||
|
tx = factory.create(module_type="pnand3", size=1, add_wells=False)
|
||||||
|
# Only DRC because well contacts will fail LVS
|
||||||
|
self.local_drc_check(tx)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class single_level_column_mux_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
|
debug.info(2, "Checking column mux port 0")
|
||||||
|
tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0")
|
||||||
|
self.local_check(tx)
|
||||||
|
|
||||||
|
debug.info(2, "Checking column mux port 1")
|
||||||
|
tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl1", bitcell_br="br1")
|
||||||
|
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(testRunner=debugTestRunner())
|
||||||
|
|
@ -15,7 +15,6 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
#@unittest.skip("SKIPPING 04_driver_test")
|
|
||||||
|
|
||||||
class single_level_column_mux_test(openram_test):
|
class single_level_column_mux_test(openram_test):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class wordline_driver_test(openram_test):
|
||||||
|
|
||||||
# check wordline driver for single port
|
# check wordline driver for single port
|
||||||
debug.info(2, "Checking driver")
|
debug.info(2, "Checking driver")
|
||||||
tx = factory.create(module_type="wordline_driver", rows=8, cols=32)
|
tx = factory.create(module_type="wordline_driver")
|
||||||
self.local_check(tx)
|
self.local_check(tx)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
@ -23,12 +23,10 @@ class bitcell_1rw_1r_array_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
OPTS.bitcell = "bitcell_1rw_1r"
|
|
||||||
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
|
|
||||||
OPTS.dummy_bitcell="dummy_bitcell_1rw_1r"
|
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_r_ports = 1
|
OPTS.num_r_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
|
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
|
||||||
a = factory.create(module_type="bitcell_array", cols=4, rows=4)
|
a = factory.create(module_type="bitcell_array", cols=4, rows=4)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class hierarchical_decoder_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
# Use the 2 port cell since it is usually bigger/easier
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
|
# Checks 2x4 and 2-input NAND decoder
|
||||||
|
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
||||||
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
# Checks 2x4 and 2-input NAND decoder with non-power-of-two
|
||||||
|
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
|
||||||
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
# Checks 2x4 with 3x8 and 2-input NAND decoder
|
||||||
|
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
|
||||||
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
# Checks 3 x 2x4 and 3-input NAND decoder
|
||||||
|
debug.info(1, "Testing 64 row sample for hierarchical_decoder")
|
||||||
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=64)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
# Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two
|
||||||
|
debug.info(1, "Testing 132 row sample for hierarchical_decoder")
|
||||||
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=132)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
# Checks 3 x 3x8 and 3-input NAND decoder
|
||||||
|
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
|
||||||
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
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(testRunner=debugTestRunner())
|
||||||
|
|
@ -21,11 +21,11 @@ class hierarchical_decoder_pbitcell_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
# check hierarchical decoder for multi-port
|
# check hierarchical decoder for multi-port
|
||||||
OPTS.bitcell = "pbitcell"
|
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
OPTS.num_r_ports = 0
|
OPTS.num_r_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
factory.reset()
|
factory.reset()
|
||||||
debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)")
|
debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)")
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class hierarchical_decoder_test(openram_test):
|
||||||
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
# Checks 2x4 and 2-input NAND decoder with non-power-of-two
|
# Checks 2x4 and 2-input NAND decoder with non-power-of-two
|
||||||
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
|
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
|
||||||
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
|
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
|
||||||
|
|
|
||||||
19
compiler/tests/05_replica_bitcell_array_test.py → compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py
Normal file → Executable file
19
compiler/tests/05_replica_bitcell_array_test.py → compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py
Normal file → Executable file
|
|
@ -1,7 +1,9 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# See LICENSE for licensing information.
|
# See LICENSE for licensing information.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California
|
# Copyright (c) 2016-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.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import unittest
|
import unittest
|
||||||
|
|
@ -13,21 +15,22 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
class replica_bitcell_array_test(openram_test):
|
|
||||||
|
class hierarchical_predecode2x4_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_r_ports = 0
|
OPTS.num_r_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
factory.reset()
|
|
||||||
debug.info(2, "Testing 4x4 array for bitcell")
|
|
||||||
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0])
|
|
||||||
self.local_check(a)
|
|
||||||
|
|
||||||
|
debug.info(1, "Testing sample for hierarchy_predecode2x4")
|
||||||
|
a = factory.create(module_type="hierarchical_predecode2x4")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# run the test from the command line
|
# run the test from the command line
|
||||||
|
|
@ -22,11 +22,11 @@ class hierarchical_predecode2x4_pbitcell_test(openram_test):
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
# checking hierarchical precode 2x4 for multi-port
|
# checking hierarchical precode 2x4 for multi-port
|
||||||
OPTS.bitcell = "pbitcell"
|
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
OPTS.num_r_ports = 0
|
OPTS.num_r_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)")
|
debug.info(1, "Testing sample for hierarchy_predecode2x4 (multi-port case)")
|
||||||
a = factory.create(module_type="hierarchical_predecode2x4")
|
a = factory.create(module_type="hierarchical_predecode2x4")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ class hierarchical_predecode2x4_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
# checking hierarchical precode 2x4 for single port
|
|
||||||
debug.info(1, "Testing sample for hierarchy_predecode2x4")
|
debug.info(1, "Testing sample for hierarchy_predecode2x4")
|
||||||
a = factory.create(module_type="hierarchical_predecode2x4")
|
a = factory.create(module_type="hierarchical_predecode2x4")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class hierarchical_predecode3x8_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
# Use the 2 port cell since it is usually bigger/easier
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
|
debug.info(1, "Testing sample for hierarchy_predecode3x8")
|
||||||
|
a = factory.create(module_type="hierarchical_predecode3x8")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
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(testRunner=debugTestRunner())
|
||||||
|
|
@ -22,11 +22,11 @@ class hierarchical_predecode3x8_pbitcell_test(openram_test):
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
# checking hierarchical precode 3x8 for multi-port
|
# checking hierarchical precode 3x8 for multi-port
|
||||||
OPTS.bitcell = "pbitcell"
|
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
OPTS.num_r_ports = 0
|
OPTS.num_r_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)")
|
debug.info(1, "Testing sample for hierarchy_predecode3x8 (multi-port case)")
|
||||||
a = factory.create(module_type="hierarchical_predecode3x8")
|
a = factory.create(module_type="hierarchical_predecode3x8")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ class hierarchical_predecode3x8_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
# checking hierarchical precode 3x8 for single port
|
|
||||||
debug.info(1, "Testing sample for hierarchy_predecode3x8")
|
debug.info(1, "Testing sample for hierarchy_predecode3x8")
|
||||||
a = factory.create(module_type="hierarchical_predecode3x8")
|
a = factory.create(module_type="hierarchical_predecode3x8")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
@unittest.skip("SKIPPING hierarchical_predecode4x16_test")
|
||||||
|
class hierarchical_predecode4x16_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
# Use the 2 port cell since it is usually bigger/easier
|
||||||
|
OPTS.bitcell = "bitcell_1rw_1r"
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
|
||||||
|
debug.info(1, "Testing sample for hierarchy_predecode4x16")
|
||||||
|
a = factory.create(module_type="hierarchical_predecode4x16")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
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(testRunner=debugTestRunner())
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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.
|
||||||
|
#
|
||||||
|
from testutils import *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class single_level_column_mux_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
|
debug.info(1, "Testing sample for 4-way column_mux_array port 0")
|
||||||
|
a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
debug.info(1, "Testing sample for 4-way column_mux_array port 1")
|
||||||
|
a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
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(testRunner=debugTestRunner())
|
||||||
|
|
@ -41,7 +41,7 @@ class single_level_column_mux_pbitcell_test(openram_test):
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)")
|
debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)")
|
||||||
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2")
|
a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2", column_offset=3)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,7 @@ class single_level_column_mux_test(openram_test):
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
import single_level_column_mux_array
|
|
||||||
|
|
||||||
# check single level column mux array in single port
|
|
||||||
debug.info(1, "Testing sample for 2-way column_mux_array")
|
debug.info(1, "Testing sample for 2-way column_mux_array")
|
||||||
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8)
|
a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
|
||||||
|
|
@ -15,31 +15,27 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
class precharge_test(openram_test):
|
class precharge_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
# check precharge array in multi-port
|
# check precharge array in multi-port
|
||||||
OPTS.bitcell = "bitcell_1rw_1r"
|
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_r_ports = 1
|
OPTS.num_r_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
factory.reset()
|
factory.reset()
|
||||||
debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell")
|
debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 0)")
|
||||||
pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0")
|
pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0")
|
||||||
self.local_check(pc)
|
self.local_check(pc)
|
||||||
|
|
||||||
# debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)")
|
|
||||||
# pc = precharge_array.precharge_array(name="pre3", columns=3, bitcell_bl="bl0", bitcell_br="br0")
|
|
||||||
# self.local_check(pc)
|
|
||||||
|
|
||||||
# debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)")
|
|
||||||
# pc = precharge_array.precharge_array(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2")
|
|
||||||
# self.local_check(pc)
|
|
||||||
|
|
||||||
|
debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell (port 1)")
|
||||||
|
pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0", column_offset=1)
|
||||||
|
self.local_check(pc)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# run the test from the command line
|
# run the test from the command line
|
||||||
|
|
@ -21,7 +21,6 @@ class precharge_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
# check precharge array in single port
|
|
||||||
debug.info(2, "Checking 3 column precharge")
|
debug.info(2, "Checking 3 column precharge")
|
||||||
pc = factory.create(module_type="precharge_array", columns=3)
|
pc = factory.create(module_type="precharge_array", columns=3)
|
||||||
self.local_check(pc)
|
self.local_check(pc)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class wordline_driver_array_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
# Use the 2 port cell since it is usually bigger/easier
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
|
# check wordline driver for single port
|
||||||
|
debug.info(2, "Checking driver")
|
||||||
|
tx = factory.create(module_type="wordline_driver_array", rows=8, cols=32)
|
||||||
|
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(testRunner=debugTestRunner())
|
||||||
5
compiler/tests/08_wordline_driver_pbitcell_test.py → compiler/tests/08_wordline_driver_array_pbitcell_test.py
Normal file → Executable file
5
compiler/tests/08_wordline_driver_pbitcell_test.py → compiler/tests/08_wordline_driver_array_pbitcell_test.py
Normal file → Executable file
|
|
@ -15,9 +15,8 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
#@unittest.skip("SKIPPING 04_driver_test")
|
|
||||||
|
|
||||||
class wordline_driver_pbitcell_test(openram_test):
|
class wordline_driver_array_pbitcell_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
|
@ -31,7 +30,7 @@ class wordline_driver_pbitcell_test(openram_test):
|
||||||
|
|
||||||
factory.reset()
|
factory.reset()
|
||||||
debug.info(2, "Checking driver (multi-port case)")
|
debug.info(2, "Checking driver (multi-port case)")
|
||||||
tx = factory.create(module_type="wordline_driver", rows=8, cols=64)
|
tx = factory.create(module_type="wordline_driver_array", rows=8, cols=64)
|
||||||
self.local_check(tx)
|
self.local_check(tx)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-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 *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class wordline_driver_array_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
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")
|
||||||
|
tx = factory.create(module_type="wordline_driver_array", rows=8, cols=32)
|
||||||
|
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(testRunner=debugTestRunner())
|
||||||
|
|
@ -21,7 +21,6 @@ class sense_amp_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
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=1")
|
debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=1")
|
||||||
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1)
|
a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# See LICENSE for licensing information.
|
# See LICENSE for licensing information.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016-2019 Regents of the University of California
|
# Copyright (c) 2016-2019 Regents of the University of California
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import unittest
|
import unittest
|
||||||
|
|
@ -19,19 +19,17 @@ class replica_bitcell_array_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
OPTS.bitcell = "bitcell_1rw_1r"
|
|
||||||
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
|
|
||||||
OPTS.dummy_bitcell="dummy_bitcell_1rw_1r"
|
|
||||||
OPTS.num_rw_ports = 1
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_r_ports = 1
|
OPTS.num_r_ports = 1
|
||||||
OPTS.num_w_ports = 0
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
|
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
|
||||||
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0,1])
|
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0, 1])
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
|
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
|
||||||
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0,1])
|
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0, 1])
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,15 @@ class replica_bitcell_array_test(openram_test):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
debug.info(2, "Testing 4x4 array for 6t_cell")
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 0
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
|
||||||
|
factory.reset()
|
||||||
|
debug.info(2, "Testing 4x4 array for bitcell")
|
||||||
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0])
|
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=0, bitcell_ports=[0])
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# run the test from the command line
|
# run the test from the command line
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# See LICENSE for licensing information.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016-2019 Regents of the University of California
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
import unittest
|
||||||
|
from testutils import *
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
from sram_factory import factory
|
||||||
|
import debug
|
||||||
|
|
||||||
|
|
||||||
|
class port_address_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
|
globals.init_openram(config_file)
|
||||||
|
|
||||||
|
# Use the 2 port cell since it is usually bigger/easier
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
|
debug.info(1, "Port address 16 rows")
|
||||||
|
a = factory.create("port_address", cols=16, rows=16)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
debug.info(1, "Port address 512 rows")
|
||||||
|
a = factory.create("port_address", cols=256, rows=512)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
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(testRunner=debugTestRunner())
|
||||||
|
|
@ -13,6 +13,7 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
|
||||||
class port_address_test(openram_test):
|
class port_address_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
@ -23,6 +24,10 @@ class port_address_test(openram_test):
|
||||||
a = factory.create("port_address", cols=16, rows=16)
|
a = factory.create("port_address", cols=16, rows=16)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
|
debug.info(1, "Port address 512 rows")
|
||||||
|
a = factory.create("port_address", cols=256, rows=512)
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# run the test from the command line
|
# run the test from the command line
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
|
||||||
class port_data_1rw_1r_test(openram_test):
|
class port_data_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
@ -20,11 +21,11 @@ class port_data_1rw_1r_test(openram_test):
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
from sram_config import sram_config
|
from sram_config import sram_config
|
||||||
|
|
||||||
OPTS.bitcell = "bitcell_1w_1r"
|
OPTS.num_rw_ports = 1
|
||||||
OPTS.num_rw_ports = 0
|
|
||||||
OPTS.num_r_ports = 1
|
OPTS.num_r_ports = 1
|
||||||
OPTS.num_w_ports = 1
|
OPTS.num_w_ports = 0
|
||||||
|
globals.setup_bitcell()
|
||||||
|
|
||||||
c = sram_config(word_size=4,
|
c = sram_config(word_size=4,
|
||||||
num_words=16)
|
num_words=16)
|
||||||
|
|
||||||
|
|
@ -36,7 +37,7 @@ class port_data_1rw_1r_test(openram_test):
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
a = factory.create("port_data", sram_config=c, port=1)
|
a = factory.create("port_data", sram_config=c, port=1)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
c.num_words=32
|
c.num_words=32
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
factory.reset()
|
factory.reset()
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue