mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into bisr
This commit is contained in:
commit
b75eeb7688
|
|
@ -1,8 +1,10 @@
|
|||
.DS_Store
|
||||
*~
|
||||
*.orig
|
||||
*.rej
|
||||
*.pyc
|
||||
*.aux
|
||||
*.out
|
||||
*.toc
|
||||
*.synctex.gz
|
||||
**/model_data
|
||||
**/model_data
|
||||
|
|
|
|||
16
README.md
16
README.md
|
|
@ -18,7 +18,7 @@ An open-source static random access memory (SRAM) compiler.
|
|||
# What is OpenRAM?
|
||||
<img align="right" width="25%" src="images/SCMOS_16kb_sram.jpg">
|
||||
|
||||
OpenRAM is an open-source Python framework to create the layout,
|
||||
OpenRAM is an award winning open-source Python framework to create the layout,
|
||||
netlists, timing and power models, placement and routing models, and
|
||||
other views necessary to use SRAMs in ASIC design. OpenRAM supports
|
||||
integration in both commercial and open-source flows with both
|
||||
|
|
@ -195,7 +195,7 @@ specific technology (e.g., [FreePDK45]) should be a subdirectory
|
|||
+ Report bugs by submitting [Github issues].
|
||||
+ Develop new features (see [how to contribute](./CONTRIBUTING.md))
|
||||
+ Submit code/fixes using a [Github pull request]
|
||||
+ Follow our [project][Github projects].
|
||||
+ Follow our [project][Github project].
|
||||
+ Read and cite our [ICCAD paper][OpenRAMpaper]
|
||||
|
||||
# Further Help
|
||||
|
|
@ -214,15 +214,7 @@ OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
|
|||
|
||||
- [Matthew Guthaus] from [VLSIDA] created the OpenRAM project and is the lead architect.
|
||||
- [James Stine] from [VLSIARCH] co-founded the project.
|
||||
- Hunter Nichols maintains and updates the timing characterization.
|
||||
- Michael Grimes created and maintains the multiport netlist code.
|
||||
- Jennifer Sowash is creating the OpenRAM IP library.
|
||||
- Jesse Cirimelli-Low created the datasheet generation.
|
||||
- Samira Ataei created early multi-bank layouts and control logic.
|
||||
- Bin Wu created early parameterized cells.
|
||||
- Yusu Wang is porting parameterized cells to new technologies.
|
||||
- Brian Chen created early prototypes of the timing characterizer.
|
||||
- Jeff Butera created early prototypes of the bank layout.
|
||||
- Many students: Hunter Nichols, Michael Grimes, Jennifer Sowash, Yusu Wang, Joey Kunzler, Jesse Cirimelli-Low, Samira Ataei, Bin Wu, Brian Chen, Jeff Butera
|
||||
|
||||
If I forgot to add you, please let me know!
|
||||
|
||||
|
|
@ -236,7 +228,7 @@ If I forgot to add you, please let me know!
|
|||
|
||||
[Github issues]: https://github.com/VLSIDA/OpenRAM/issues
|
||||
[Github pull request]: https://github.com/VLSIDA/OpenRAM/pulls
|
||||
[Github projects]: https://github.com/VLSIDA/OpenRAM/projects
|
||||
[Github project]: https://github.com/VLSIDA/OpenRAM
|
||||
|
||||
[documentation]: https://docs.google.com/presentation/d/10InGB33N51I6oBHnqpU7_w9DXlx-qe9zdrlco2Yc5co/edit?usp=sharing
|
||||
[dev-group]: mailto:openram-dev-group@ucsc.edu
|
||||
|
|
|
|||
|
|
@ -116,6 +116,15 @@ class cell_properties():
|
|||
self._dff_buff_array = _dff_buff_array(use_custom_ports = False,
|
||||
add_body_contacts = False)
|
||||
|
||||
self._write_driver = _cell({'din': 'din',
|
||||
'bl' : 'bl',
|
||||
'br' : 'br',
|
||||
'en' : 'en'})
|
||||
self._sense_amp = _cell({'bl' : 'bl',
|
||||
'br' : 'br',
|
||||
'dout' : 'dout',
|
||||
'en' : 'en'})
|
||||
|
||||
@property
|
||||
def bitcell(self):
|
||||
return self._bitcell
|
||||
|
|
@ -132,3 +141,10 @@ class cell_properties():
|
|||
def dff_buff_array(self):
|
||||
return self._dff_buff_array
|
||||
|
||||
@property
|
||||
def write_driver(self):
|
||||
return self._write_driver
|
||||
|
||||
@property
|
||||
def sense_amp(self):
|
||||
return self._sense_amp
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class geometry:
|
|||
y = item[0] * math.sin(angle) + item[1] * mirr * math.cos(angle) + offset[1]
|
||||
coordinate += [[x, y]]
|
||||
return coordinate
|
||||
|
||||
|
||||
def normalize(self):
|
||||
""" Re-find the LL and UR points after a transform """
|
||||
(first, second) = self.boundary
|
||||
|
|
@ -64,14 +64,14 @@ class geometry:
|
|||
def update_boundary(self):
|
||||
""" Update the boundary with a new placement. """
|
||||
self.compute_boundary(self.offset, self.mirror, self.rotate)
|
||||
|
||||
|
||||
def compute_boundary(self, offset=vector(0, 0), mirror="", rotate=0):
|
||||
""" Transform with offset, mirror and rotation to get the absolute pin location.
|
||||
We must then re-find the ll and ur. The master is the cell instance. """
|
||||
if OPTS.netlist_only:
|
||||
self.boundary = [vector(0,0), vector(0,0)]
|
||||
return
|
||||
|
||||
|
||||
(ll, ur) = [vector(0, 0), vector(self.width, self.height)]
|
||||
|
||||
if mirror == "MX":
|
||||
|
|
@ -83,7 +83,7 @@ class geometry:
|
|||
elif mirror == "XY":
|
||||
ll = ll.scale(-1, -1)
|
||||
ur = ur.scale(-1, -1)
|
||||
|
||||
|
||||
if rotate == 90:
|
||||
ll = ll.rotate_scale(-1, 1)
|
||||
ur = ur.rotate_scale(-1, 1)
|
||||
|
|
@ -96,19 +96,19 @@ class geometry:
|
|||
|
||||
self.boundary = [offset + ll, offset + ur]
|
||||
self.normalize()
|
||||
|
||||
|
||||
def ll(self):
|
||||
""" Return the lower left corner """
|
||||
return self.boundary[0]
|
||||
|
||||
|
||||
def ur(self):
|
||||
""" Return the upper right corner """
|
||||
return self.boundary[1]
|
||||
|
||||
|
||||
def lr(self):
|
||||
""" Return the lower right corner """
|
||||
return vector(self.boundary[1].x, self.boundary[0].y)
|
||||
|
||||
|
||||
def ul(self):
|
||||
""" Return the upper left corner """
|
||||
return vector(self.boundary[0].x, self.boundary[1].y)
|
||||
|
|
@ -132,12 +132,12 @@ class geometry:
|
|||
def cx(self):
|
||||
""" Return the center x """
|
||||
return 0.5 * (self.boundary[0].x + self.boundary[1].x)
|
||||
|
||||
|
||||
def cy(self):
|
||||
""" Return the center y """
|
||||
return 0.5 * (self.boundary[0].y + self.boundary[1].y)
|
||||
|
||||
|
||||
|
||||
class instance(geometry):
|
||||
"""
|
||||
An instance of an instance/module with a specified location and
|
||||
|
|
@ -148,7 +148,7 @@ class instance(geometry):
|
|||
geometry.__init__(self)
|
||||
debug.check(mirror not in ["R90", "R180", "R270"],
|
||||
"Please use rotation and not mirroring during instantiation.")
|
||||
|
||||
|
||||
self.name = name
|
||||
self.mod = mod
|
||||
self.gds = mod.gds
|
||||
|
|
@ -166,7 +166,7 @@ class instance(geometry):
|
|||
self.width = round_to_grid(mod.width)
|
||||
self.height = round_to_grid(mod.height)
|
||||
self.compute_boundary(offset, mirror, rotate)
|
||||
|
||||
|
||||
debug.info(4, "creating instance: " + self.name)
|
||||
|
||||
def get_blockages(self, lpp, top=False):
|
||||
|
|
@ -202,11 +202,11 @@ class instance(geometry):
|
|||
new_blockages.append(self.transform_coords(b,self.offset, mirr, angle))
|
||||
return new_blockages
|
||||
|
||||
|
||||
|
||||
def gds_write_file(self, new_layout):
|
||||
"""Recursively writes all the sub-modules in this instance"""
|
||||
debug.info(4, "writing instance: " + self.name)
|
||||
# make sure to write out my module/structure
|
||||
# make sure to write out my module/structure
|
||||
# (it will only be written the first time though)
|
||||
self.mod.gds_write_file(self.gds)
|
||||
# now write an instance of my module/structure
|
||||
|
|
@ -215,7 +215,7 @@ class instance(geometry):
|
|||
offsetInMicrons=self.offset,
|
||||
mirror=self.mirror,
|
||||
rotate=self.rotate)
|
||||
|
||||
|
||||
def place(self, offset, mirror="R0", rotate=0):
|
||||
""" This updates the placement of an instance. """
|
||||
# Update the placement of an already added instance
|
||||
|
|
@ -224,8 +224,8 @@ class instance(geometry):
|
|||
self.rotate = rotate
|
||||
self.update_boundary()
|
||||
debug.info(3, "placing instance {}".format(self))
|
||||
|
||||
|
||||
|
||||
|
||||
def get_pin(self,name,index=-1):
|
||||
""" Return an absolute pin that is offset and transformed based on
|
||||
this instance location. Index will return one of several pins."""
|
||||
|
|
@ -243,20 +243,20 @@ class instance(geometry):
|
|||
def get_num_pins(self, name):
|
||||
""" Return the number of pins of a given name """
|
||||
return len(self.mod.get_pins(name))
|
||||
|
||||
|
||||
def get_pins(self,name):
|
||||
""" Return an absolute pin that is offset and transformed based on
|
||||
this instance location. """
|
||||
|
||||
|
||||
import copy
|
||||
pin = copy.deepcopy(self.mod.get_pins(name))
|
||||
|
||||
|
||||
new_pins = []
|
||||
for p in pin:
|
||||
p.transform(self.offset,self.mirror,self.rotate)
|
||||
p.transform(self.offset,self.mirror,self.rotate)
|
||||
new_pins.append(p)
|
||||
return new_pins
|
||||
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")"
|
||||
|
|
@ -293,7 +293,7 @@ class path(geometry):
|
|||
def get_blockages(self, layer):
|
||||
""" Fail since we don't support paths yet. """
|
||||
assert(0)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "path: layer=" + self.layerNumber + " w=" + self.width
|
||||
|
|
@ -329,6 +329,7 @@ class label(geometry):
|
|||
debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text)
|
||||
new_layout.addText(text=self.text,
|
||||
layerNumber=self.layerNumber,
|
||||
purposeNumber=self.layerPurpose,
|
||||
offsetInMicrons=self.offset,
|
||||
magnification=self.zoom,
|
||||
rotate=None)
|
||||
|
|
@ -336,7 +337,7 @@ class label(geometry):
|
|||
def get_blockages(self, layer):
|
||||
""" Returns an empty list since text cannot be blockages. """
|
||||
return []
|
||||
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "label: " + self.text + " layer=" + str(self.layerNumber)
|
||||
|
|
@ -345,7 +346,7 @@ class label(geometry):
|
|||
""" override print function output """
|
||||
return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )"
|
||||
|
||||
|
||||
|
||||
class rectangle(geometry):
|
||||
"""Represents a rectangular shape"""
|
||||
|
||||
|
|
@ -363,7 +364,7 @@ class rectangle(geometry):
|
|||
|
||||
debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): "
|
||||
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
|
||||
|
||||
|
||||
def get_blockages(self, layer):
|
||||
""" Returns a list of one rectangle if it is on this layer"""
|
||||
if self.layerNumber == layer:
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class layout():
|
|||
self.objs = [] # Holds all other objects (labels, geometries, etc)
|
||||
self.pin_map = {} # Holds name->pin_layout map for all pins
|
||||
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()
|
||||
try:
|
||||
from tech import power_grid
|
||||
|
|
@ -71,7 +71,7 @@ class layout():
|
|||
(inv_num + 1) * height - \
|
||||
(inv_num % 2) * drc["minwidth_m1"])
|
||||
y_dir = -1
|
||||
|
||||
|
||||
return (base_offset, y_dir)
|
||||
|
||||
def find_lowest_coords(self):
|
||||
|
|
@ -90,7 +90,7 @@ class layout():
|
|||
lowesty2 = min(inst.by() for inst in self.insts)
|
||||
else:
|
||||
lowestx2 = lowesty2 = None
|
||||
|
||||
|
||||
if lowestx1 == None and lowestx2 == None:
|
||||
return None
|
||||
elif lowestx1 == None:
|
||||
|
|
@ -146,7 +146,7 @@ class layout():
|
|||
subcoord = inst.mod.find_highest_layer_coords(layer) + inst.offset
|
||||
highestx = max(highestx, subcoord.x)
|
||||
highesty = max(highesty, subcoord.y)
|
||||
|
||||
|
||||
return vector(highestx, highesty)
|
||||
|
||||
def find_lowest_layer_coords(self, layer):
|
||||
|
|
@ -172,7 +172,7 @@ class layout():
|
|||
lowesty = min(lowesty, subcoord.y)
|
||||
|
||||
return vector(lowestx, lowesty)
|
||||
|
||||
|
||||
def translate_all(self, offset):
|
||||
"""
|
||||
Translates all objects, instances, and pins by the given (x,y) offset
|
||||
|
|
@ -189,7 +189,7 @@ class layout():
|
|||
pin_list = self.pin_map[pin_name]
|
||||
for pin in pin_list:
|
||||
pin.rect = [pin.ll() - offset, pin.ur() - offset]
|
||||
|
||||
|
||||
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
|
||||
""" Adds an instance of a mod to this module """
|
||||
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
|
||||
|
|
@ -204,7 +204,7 @@ class layout():
|
|||
if inst.name == name:
|
||||
return inst
|
||||
return None
|
||||
|
||||
|
||||
def add_rect(self, layer, offset, width=None, height=None):
|
||||
"""
|
||||
Adds a rectangle on a given layer,offset with width and height
|
||||
|
|
@ -260,7 +260,7 @@ class layout():
|
|||
start-offset,
|
||||
minwidth_layer,
|
||||
end.y-start.y)
|
||||
|
||||
|
||||
def get_pin(self, text):
|
||||
"""
|
||||
Return the pin or list of pins
|
||||
|
|
@ -275,7 +275,7 @@ class layout():
|
|||
except Exception:
|
||||
self.gds_write("missing_pin.gds")
|
||||
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text,self.name),-1)
|
||||
|
||||
|
||||
def get_pins(self, text):
|
||||
"""
|
||||
Return a pin list (instead of a single pin)
|
||||
|
|
@ -290,17 +290,17 @@ class layout():
|
|||
Return a pin list of all pins
|
||||
"""
|
||||
return self.pin_map.keys()
|
||||
|
||||
|
||||
def copy_layout_pin(self, instance, pin_name, new_name=""):
|
||||
"""
|
||||
Create a copied version of the layout pin at the current level.
|
||||
You can optionally rename the pin to a new name.
|
||||
"""
|
||||
pins = instance.get_pins(pin_name)
|
||||
|
||||
|
||||
debug.check(len(pins) > 0,
|
||||
"Could not find pin {}".format(pin_name))
|
||||
|
||||
|
||||
for pin in pins:
|
||||
if new_name == "":
|
||||
new_name = pin.name
|
||||
|
|
@ -316,18 +316,19 @@ class layout():
|
|||
You can optionally rename the pin to a new name.
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Creates a path like pin with center-line convention
|
||||
"""
|
||||
|
||||
debug.check(start.x == end.x or start.y == end.y,
|
||||
"Cannot have a non-manhatten layout pin.")
|
||||
|
||||
if start.x != end.x and start.y != end.y:
|
||||
file_name = "non_rectilinear.gds"
|
||||
self.gds_write(file_name)
|
||||
debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1)
|
||||
|
||||
minwidth_layer = drc["minwidth_{}".format(layer)]
|
||||
|
||||
|
||||
# one of these will be zero
|
||||
width = max(start.x, end.x) - min(start.x, end.x)
|
||||
height = max(start.y, end.y) - min(start.y, end.y)
|
||||
|
|
@ -341,7 +342,7 @@ class layout():
|
|||
# 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,
|
||||
layer,
|
||||
ll_offset,
|
||||
|
|
@ -358,13 +359,13 @@ class layout():
|
|||
ll_offset = offset - vector(0.5 * width, 0.5 * height)
|
||||
|
||||
return self.add_layout_pin(text, layer, ll_offset, width, height)
|
||||
|
||||
|
||||
def remove_layout_pin(self, text):
|
||||
"""
|
||||
Delete a labeled pin (or all pins of the same name)
|
||||
"""
|
||||
self.pin_map[text] = set()
|
||||
|
||||
|
||||
def add_layout_pin(self, text, layer, offset, width=None, height=None):
|
||||
"""
|
||||
Create a labeled pin
|
||||
|
|
@ -408,7 +409,7 @@ class layout():
|
|||
layer=layer,
|
||||
offset=offset + vector(0.5 * width,
|
||||
0.5 * height))
|
||||
|
||||
|
||||
def add_label(self, text, layer, offset=[0, 0], zoom=-1):
|
||||
"""Adds a text label on the given layer,offset, and zoom level"""
|
||||
# negative layers indicate "unused" layers in a given technology
|
||||
|
|
@ -447,7 +448,7 @@ class layout():
|
|||
layer_stack=layers,
|
||||
path=coordinates,
|
||||
layer_widths=layer_widths)
|
||||
|
||||
|
||||
def add_wire(self, layers, coordinates):
|
||||
"""Connects a routing path on given layer,coordinates,width.
|
||||
The layers are the (horizontal, via, vertical). """
|
||||
|
|
@ -462,14 +463,14 @@ class layout():
|
|||
""" Return the preferred routing directions """
|
||||
from tech import preferred_directions
|
||||
return preferred_directions[layer]
|
||||
|
||||
|
||||
def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None):
|
||||
""" Add a three layer via structure. """
|
||||
|
||||
if not directions:
|
||||
directions = (self.get_preferred_direction(layers[0]),
|
||||
self.get_preferred_direction(layers[2]))
|
||||
|
||||
|
||||
from sram_factory import factory
|
||||
via = factory.create(module_type="contact",
|
||||
layer_stack=layers,
|
||||
|
|
@ -494,7 +495,7 @@ class layout():
|
|||
if not directions:
|
||||
directions = (self.get_preferred_direction(layers[0]),
|
||||
self.get_preferred_direction(layers[2]))
|
||||
|
||||
|
||||
from sram_factory import factory
|
||||
via = factory.create(module_type="contact",
|
||||
layer_stack=layers,
|
||||
|
|
@ -504,7 +505,7 @@ class layout():
|
|||
well_type=well_type)
|
||||
height = via.height
|
||||
width = via.width
|
||||
|
||||
|
||||
corrected_offset = offset + vector(-0.5 * width,
|
||||
-0.5 * height)
|
||||
|
||||
|
|
@ -516,12 +517,12 @@ class layout():
|
|||
self.connect_inst([])
|
||||
return inst
|
||||
|
||||
def add_via_stack(self, offset, direction, from_layer, to_layer,
|
||||
size=[1,1]):
|
||||
def add_via_stack(self, offset, from_layer, to_layer,
|
||||
direction=None,
|
||||
size=[1, 1]):
|
||||
"""
|
||||
Punch a stack of vias from a start layer to a target layer.
|
||||
"""
|
||||
|
||||
return self.__add_via_stack_internal(offset=offset,
|
||||
direction=direction,
|
||||
from_layer=from_layer,
|
||||
|
|
@ -530,8 +531,9 @@ class layout():
|
|||
last_via=None,
|
||||
size=size)
|
||||
|
||||
def add_via_stack_center(self, offset, direction, from_layer, to_layer,
|
||||
size=[1,1]):
|
||||
def add_via_stack_center(self, offset, from_layer, to_layer,
|
||||
direction=None,
|
||||
size=[1, 1]):
|
||||
"""
|
||||
Punch a stack of vias from a start layer to a target layer by the center
|
||||
coordinate accounting for mirroring and rotation.
|
||||
|
|
@ -544,7 +546,6 @@ class layout():
|
|||
last_via=None,
|
||||
size=size)
|
||||
|
||||
|
||||
def __add_via_stack_internal(self, offset, direction, from_layer, to_layer,
|
||||
via_func, last_via, size):
|
||||
"""
|
||||
|
|
@ -580,7 +581,6 @@ class layout():
|
|||
last_via=via,
|
||||
size=size)
|
||||
|
||||
|
||||
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
|
||||
"""Adds a ptx module to the design."""
|
||||
import ptx
|
||||
|
|
@ -602,11 +602,11 @@ class layout():
|
|||
# This must be done for netlist only mode too
|
||||
if os.path.isfile(self.gds_file):
|
||||
self.is_library_cell = True
|
||||
|
||||
|
||||
if OPTS.netlist_only:
|
||||
self.gds = None
|
||||
return
|
||||
|
||||
|
||||
# open the gds file if it exists or else create a blank layout
|
||||
if os.path.isfile(self.gds_file):
|
||||
debug.info(3, "opening {}".format(self.gds_file))
|
||||
|
|
@ -662,7 +662,7 @@ class layout():
|
|||
height=height,
|
||||
center=False)
|
||||
debug.info(2, "Adding {0} boundary {1}".format(self.name, boundary))
|
||||
|
||||
|
||||
self.visited.append(self.name)
|
||||
|
||||
def gds_write(self, gds_name):
|
||||
|
|
@ -681,10 +681,10 @@ class layout():
|
|||
# which may have been previously processed!
|
||||
# MRG: 10/4/18 We need to clear if we make changes and write a second GDS!
|
||||
self.clear_visited()
|
||||
|
||||
|
||||
# recursively create all the remaining objects
|
||||
self.gds_write_file(self.gds)
|
||||
|
||||
|
||||
# populates the xyTree data structure for gds
|
||||
# self.gds.prepareForWrite()
|
||||
writer.writeToFile(gds_name)
|
||||
|
|
@ -706,7 +706,7 @@ class layout():
|
|||
lpp = techlayer[layer]
|
||||
else:
|
||||
lpp = layer
|
||||
|
||||
|
||||
blockages = []
|
||||
for i in self.objs:
|
||||
blockages += i.get_blockages(lpp)
|
||||
|
|
@ -724,7 +724,7 @@ class layout():
|
|||
pin_names = copy.deepcopy(self.pins)
|
||||
if self.name.startswith("pmos") or self.name.startswith("nmos"):
|
||||
pin_names.remove("B")
|
||||
|
||||
|
||||
blockages = []
|
||||
for pin_name in pin_names:
|
||||
pin_list = self.get_pins(pin_name)
|
||||
|
|
@ -784,7 +784,7 @@ class layout():
|
|||
|
||||
# half minwidth so we can return the center line offsets
|
||||
half_minwidth = 0.5 * drc["minwidth_{}".format(layer)]
|
||||
|
||||
|
||||
line_positions = {}
|
||||
if vertical:
|
||||
for i in range(len(names)):
|
||||
|
|
@ -829,7 +829,7 @@ class layout():
|
|||
layer_stack=("m1", "via1", "m2")):
|
||||
""" Vertical version of connect_bus. """
|
||||
self.connect_bus(mapping, inst, bus_offsets, layer_stack, False)
|
||||
|
||||
|
||||
def connect_bus(self, mapping, inst, bus_offsets, layer_stack, horizontal):
|
||||
"""
|
||||
Connect a mapping of pin -> name for a bus. This could be
|
||||
|
|
@ -842,7 +842,7 @@ class layout():
|
|||
route_layer = vertical_layer
|
||||
else:
|
||||
route_layer = horizontal_layer
|
||||
|
||||
|
||||
for (pin_name, bus_name) in mapping:
|
||||
pin = inst.get_pin(pin_name)
|
||||
pin_pos = pin.center()
|
||||
|
|
@ -854,7 +854,7 @@ class layout():
|
|||
else:
|
||||
# left/right then up/down
|
||||
mid_pos = vector(bus_pos.x, pin_pos.y)
|
||||
|
||||
|
||||
self.add_wire(layer_stack,
|
||||
[bus_pos, mid_pos, pin_pos])
|
||||
|
||||
|
|
@ -865,7 +865,7 @@ class layout():
|
|||
offset=pin_pos)
|
||||
# FIXME: output pins tend to not be rotate,
|
||||
# but supply pins are. Make consistent?
|
||||
|
||||
|
||||
# We only need a via if they happened to align perfectly
|
||||
# so the add_wire didn't add a via
|
||||
if (horizontal and bus_pos.y == pin_pos.y) or (not horizontal and bus_pos.x == pin_pos.x):
|
||||
|
|
@ -899,7 +899,7 @@ class layout():
|
|||
self.m3_space)
|
||||
else:
|
||||
debug.error("Cannot find layer pitch.")
|
||||
|
||||
|
||||
def add_horizontal_trunk_route(self,
|
||||
pins,
|
||||
trunk_offset,
|
||||
|
|
@ -915,7 +915,7 @@ class layout():
|
|||
# if we are less than a pitch, just create a non-preferred layer jog
|
||||
if max_x-min_x <= pitch:
|
||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
|
||||
|
||||
|
||||
# Add the horizontal trunk on the vertical layer!
|
||||
self.add_path(self.vertical_layer,
|
||||
[vector(min_x - half_layer_width, trunk_offset.y),
|
||||
|
|
@ -955,7 +955,7 @@ class layout():
|
|||
if max_y-min_y <= pitch:
|
||||
|
||||
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
|
||||
|
||||
|
||||
# Add the vertical trunk on the horizontal layer!
|
||||
self.add_path(self.horizontal_layer,
|
||||
[vector(trunk_offset.x, min_y - half_layer_width),
|
||||
|
|
@ -978,7 +978,7 @@ class layout():
|
|||
self.add_path(self.horizontal_layer, [pin.center(), mid])
|
||||
self.add_via_center(layers=layer_stack,
|
||||
offset=mid)
|
||||
|
||||
|
||||
def create_channel_route(self, netlist,
|
||||
offset,
|
||||
layer_stack,
|
||||
|
|
@ -996,7 +996,7 @@ class layout():
|
|||
Remove the pin from the graph and all conflicts
|
||||
"""
|
||||
g.pop(pin, None)
|
||||
|
||||
|
||||
# Remove the pin from all conflicts
|
||||
# FIXME: This is O(n^2), so maybe optimize it.
|
||||
for other_pin,conflicts in g.items():
|
||||
|
|
@ -1010,21 +1010,21 @@ class layout():
|
|||
Check all the pin pairs on two nets and return a pin
|
||||
overlap if any pin overlaps.
|
||||
"""
|
||||
|
||||
|
||||
for pin1 in net1:
|
||||
for pin2 in net2:
|
||||
if vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
||||
""" Check for vertical or horizontal overlap of the two pins """
|
||||
# FIXME: If the pins are not in a row, this may break.
|
||||
# However, a top pin shouldn't overlap another top pin,
|
||||
# for example, so the
|
||||
# extra comparison *shouldn't* matter.
|
||||
|
||||
|
||||
# Pin 1 must be in the "BOTTOM" set
|
||||
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch
|
||||
|
||||
|
|
@ -1049,7 +1049,7 @@ class layout():
|
|||
# too if we want to minimize the
|
||||
# number of tracks!
|
||||
# hcg = {}
|
||||
|
||||
|
||||
# Initialize the vertical conflict graph (vcg)
|
||||
# and make a list of all pins
|
||||
vcg = collections.OrderedDict()
|
||||
|
|
@ -1084,7 +1084,7 @@ class layout():
|
|||
vertical,
|
||||
self.horizontal_pitch):
|
||||
vcg[net_name2].append(net_name1)
|
||||
|
||||
|
||||
# list of routes to do
|
||||
while vcg:
|
||||
# from pprint import pformat
|
||||
|
|
@ -1105,7 +1105,7 @@ class layout():
|
|||
|
||||
# Remove the net from other constriants in the VCG
|
||||
vcg = remove_net_from_graph(net_name, vcg)
|
||||
|
||||
|
||||
# Add the trunk routes from the bottom up for
|
||||
# horizontal or the left to right for vertical
|
||||
if vertical:
|
||||
|
|
@ -1137,7 +1137,7 @@ class layout():
|
|||
""" Add boundary for debugging dimensions """
|
||||
if OPTS.netlist_only:
|
||||
return
|
||||
|
||||
|
||||
if "stdc" in techlayer.keys():
|
||||
boundary_layer = "stdc"
|
||||
else:
|
||||
|
|
@ -1152,7 +1152,7 @@ class layout():
|
|||
offset=ll,
|
||||
height=ur.y-ll.y,
|
||||
width=ur.x-ll.x)
|
||||
|
||||
|
||||
def add_enclosure(self, insts, layer="nwell"):
|
||||
""" Add a layer that surrounds the given instances. Useful
|
||||
for creating wells, for example. Doesn't check for minimum widths or
|
||||
|
|
@ -1193,19 +1193,25 @@ class layout():
|
|||
"supply router."
|
||||
.format(name,inst.name,self.pwr_grid_layer))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"):
|
||||
"""
|
||||
Add a single power pin from the lowest power_grid layer down to M1 at
|
||||
the given center location. The starting layer is specified to determine
|
||||
which vias are needed.
|
||||
"""
|
||||
|
||||
# Force vdd/gnd via stack to be vertically or horizontally oriented
|
||||
# Default: None, uses prefered metal directions
|
||||
if vertical:
|
||||
direction = ("V", "V")
|
||||
else:
|
||||
elif not vertical and vertical is not None:
|
||||
direction = ("H", "H")
|
||||
|
||||
else:
|
||||
direction = None
|
||||
|
||||
|
||||
via = self.add_via_stack_center(from_layer=start_layer,
|
||||
to_layer=self.pwr_grid_layer,
|
||||
size=size,
|
||||
|
|
@ -1222,7 +1228,7 @@ class layout():
|
|||
offset=loc,
|
||||
width=via.width,
|
||||
height=via.height)
|
||||
|
||||
|
||||
def add_power_ring(self, bbox):
|
||||
"""
|
||||
Create vdd and gnd power rings around an area of the bounding box
|
||||
|
|
@ -1287,7 +1293,7 @@ class layout():
|
|||
offset=offset,
|
||||
width=width,
|
||||
height=self.supply_rail_width)
|
||||
|
||||
|
||||
# TOP horizontal rails
|
||||
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
|
||||
0)
|
||||
|
|
@ -1328,7 +1334,7 @@ class layout():
|
|||
else:
|
||||
self.supply_vias -= 1
|
||||
break
|
||||
|
||||
|
||||
via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center),
|
||||
vector(self.left_gnd_x_center, self.top_gnd_y_center),
|
||||
vector(self.right_gnd_x_center, self.bottom_gnd_y_center),
|
||||
|
|
@ -1337,7 +1343,7 @@ class layout():
|
|||
vector(self.left_vdd_x_center, self.top_vdd_y_center),
|
||||
vector(self.right_vdd_x_center, self.bottom_vdd_y_center),
|
||||
vector(self.right_vdd_x_center, self.top_vdd_y_center)]
|
||||
|
||||
|
||||
for pt in via_points:
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pt,
|
||||
|
|
@ -1390,4 +1396,3 @@ class layout():
|
|||
debug.info(0, "name={0} : mod={1} : offset={2}".format(inst.name,
|
||||
inst.mod.name,
|
||||
inst.offset))
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class pin_layout:
|
|||
|
||||
debug.check(self.width() > 0, "Zero width pin.")
|
||||
debug.check(self.height() > 0, "Zero height pin.")
|
||||
|
||||
|
||||
# if it's a string, use the name
|
||||
if type(layer_name_pp) == str:
|
||||
self._layer = layer_name_pp
|
||||
|
|
@ -93,17 +93,17 @@ class pin_layout:
|
|||
is a major speedup, if pin_layout is used as a key for dicts.
|
||||
"""
|
||||
return self._hash
|
||||
|
||||
|
||||
def __lt__(self, other):
|
||||
""" Provide a function for ordering items by the ll point """
|
||||
(ll, ur) = self.rect
|
||||
(oll, our) = other.rect
|
||||
|
||||
|
||||
if ll.x < oll.x and ll.y < oll.y:
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
""" Check if these are the same pins for duplicate checks """
|
||||
if isinstance(other, self.__class__):
|
||||
|
|
@ -128,14 +128,14 @@ class pin_layout:
|
|||
max_y = max(max_y, pin.ur().y)
|
||||
|
||||
self.rect = [vector(min_x, min_y), vector(max_x, max_y)]
|
||||
|
||||
|
||||
def fix_minarea(self):
|
||||
"""
|
||||
Try to fix minimum area rule.
|
||||
"""
|
||||
min_area = drc("{}_minarea".format(self.layer))
|
||||
pass
|
||||
|
||||
|
||||
def inflate(self, spacing=None):
|
||||
"""
|
||||
Inflate the rectangle by the spacing (or other rule)
|
||||
|
|
@ -143,12 +143,12 @@ class pin_layout:
|
|||
"""
|
||||
if not spacing:
|
||||
spacing = 0.5*drc("{0}_to_{0}".format(self.layer))
|
||||
|
||||
|
||||
(ll, ur) = self.rect
|
||||
spacing = vector(spacing, spacing)
|
||||
newll = ll - spacing
|
||||
newur = ur + spacing
|
||||
|
||||
|
||||
return (newll, newur)
|
||||
|
||||
def intersection(self, other):
|
||||
|
|
@ -191,7 +191,7 @@ class pin_layout:
|
|||
y_overlaps = True
|
||||
|
||||
return y_overlaps
|
||||
|
||||
|
||||
def xcontains(self, other):
|
||||
""" Check if shape contains the x overlap """
|
||||
(ll, ur) = self.rect
|
||||
|
|
@ -205,13 +205,13 @@ class pin_layout:
|
|||
(oll, our) = other.rect
|
||||
|
||||
return (oll.y >= ll.y and our.y <= ur.y)
|
||||
|
||||
|
||||
def contains(self, other):
|
||||
""" Check if a shape contains another rectangle """
|
||||
# If it is the same shape entirely, it is contained!
|
||||
if self == other:
|
||||
return True
|
||||
|
||||
|
||||
# Can only overlap on the same layer
|
||||
if not self.same_lpp(self.lpp, other.lpp):
|
||||
return False
|
||||
|
|
@ -230,13 +230,13 @@ class pin_layout:
|
|||
if shape.contains(self):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def overlaps(self, other):
|
||||
""" Check if a shape overlaps with a rectangle """
|
||||
# Can only overlap on the same layer
|
||||
if not self.same_lpp(self.lpp, other.lpp):
|
||||
return False
|
||||
|
||||
|
||||
x_overlaps = self.xoverlaps(other)
|
||||
y_overlaps = self.yoverlaps(other)
|
||||
|
||||
|
|
@ -245,11 +245,11 @@ class pin_layout:
|
|||
def area(self):
|
||||
""" Return the area. """
|
||||
return self.height()*self.width()
|
||||
|
||||
|
||||
def height(self):
|
||||
""" Return height. Abs is for pre-normalized value."""
|
||||
return abs(self.rect[1].y-self.rect[0].y)
|
||||
|
||||
|
||||
def width(self):
|
||||
""" Return width. Abs is for pre-normalized value."""
|
||||
return abs(self.rect[1].x-self.rect[0].x)
|
||||
|
|
@ -260,7 +260,7 @@ class pin_layout:
|
|||
ll = vector(min(first[0], second[0]), min(first[1], second[1]))
|
||||
ur = vector(max(first[0], second[0]), max(first[1], second[1]))
|
||||
self.rect=[ll, ur]
|
||||
|
||||
|
||||
def transform(self, offset, mirror, rotate):
|
||||
"""
|
||||
Transform with offset, mirror and rotation
|
||||
|
|
@ -278,7 +278,7 @@ class pin_layout:
|
|||
elif mirror == "XY":
|
||||
ll = ll.scale(-1, -1)
|
||||
ur = ur.scale(-1, -1)
|
||||
|
||||
|
||||
if rotate == 90:
|
||||
ll = ll.rotate_scale(-1, 1)
|
||||
ur = ur.rotate_scale(-1, 1)
|
||||
|
|
@ -303,7 +303,7 @@ class pin_layout:
|
|||
def cy(self):
|
||||
""" Center y """
|
||||
return 0.5*(self.rect[0].y+self.rect[1].y)
|
||||
|
||||
|
||||
# The four possible corners
|
||||
def ll(self):
|
||||
""" Lower left point """
|
||||
|
|
@ -320,7 +320,7 @@ class pin_layout:
|
|||
def ur(self):
|
||||
""" Upper right point """
|
||||
return self.rect[1]
|
||||
|
||||
|
||||
# The possible y edge values
|
||||
def uy(self):
|
||||
""" Upper y value """
|
||||
|
|
@ -331,15 +331,15 @@ class pin_layout:
|
|||
return self.rect[0].y
|
||||
|
||||
# The possible x edge values
|
||||
|
||||
|
||||
def lx(self):
|
||||
""" Left x value """
|
||||
return self.rect[0].x
|
||||
|
||||
|
||||
def rx(self):
|
||||
""" Right x value """
|
||||
return self.rect[1].x
|
||||
|
||||
|
||||
# The edge centers
|
||||
def rc(self):
|
||||
""" Right center point """
|
||||
|
|
@ -350,7 +350,7 @@ class pin_layout:
|
|||
""" Left center point """
|
||||
return vector(self.rect[0].x,
|
||||
0.5*(self.rect[0].y+self.rect[1].y))
|
||||
|
||||
|
||||
def uc(self):
|
||||
""" Upper center point """
|
||||
return vector(0.5*(self.rect[0].x+self.rect[1].x),
|
||||
|
|
@ -378,6 +378,7 @@ class pin_layout:
|
|||
# imported into Magic.
|
||||
newLayout.addText(text=self.name,
|
||||
layerNumber=layer_num,
|
||||
purposeNumber=purpose,
|
||||
offsetInMicrons=self.center(),
|
||||
magnification=GDS["zoom"],
|
||||
rotate=None)
|
||||
|
|
@ -392,7 +393,7 @@ class pin_layout:
|
|||
|
||||
dy = min(r1_ur.y, r2_ur.y) - max(r1_ll.y, r2_ll.y)
|
||||
dx = min(r1_ur.x, r2_ur.x) - max(r1_ll.x, r2_ll.x)
|
||||
|
||||
|
||||
if dx >= 0 and dy >= 0:
|
||||
return [dx, dy]
|
||||
else:
|
||||
|
|
@ -407,7 +408,7 @@ class pin_layout:
|
|||
|
||||
def dist(x1, y1, x2, y2):
|
||||
return math.sqrt((x2-x1)**2 + (y2-y1)**2)
|
||||
|
||||
|
||||
left = r2_ur.x < r1_ll.x
|
||||
right = r1_ur.x < r2_ll.x
|
||||
bottom = r2_ur.y < r1_ll.y
|
||||
|
|
@ -432,7 +433,7 @@ class pin_layout:
|
|||
else:
|
||||
# rectangles intersect
|
||||
return 0
|
||||
|
||||
|
||||
def overlap_length(self, other):
|
||||
"""
|
||||
Calculate the intersection segment and determine its length
|
||||
|
|
@ -453,7 +454,7 @@ class pin_layout:
|
|||
# This is where we had a corner intersection or none
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
def compute_overlap_segment(self, other):
|
||||
"""
|
||||
Calculate the intersection segment of two rectangles
|
||||
|
|
@ -469,19 +470,19 @@ class pin_layout:
|
|||
r2_lr = vector(r2_ur.x, r2_ll.y)
|
||||
|
||||
from itertools import tee
|
||||
|
||||
|
||||
def pairwise(iterable):
|
||||
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
||||
a, b = tee(iterable)
|
||||
next(b, None)
|
||||
return zip(a, b)
|
||||
|
||||
|
||||
# R1 edges CW
|
||||
r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll]
|
||||
r1_edges = []
|
||||
for (p, q) in pairwise(r1_cw_points):
|
||||
r1_edges.append([p, q])
|
||||
|
||||
|
||||
# R2 edges CW
|
||||
r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll]
|
||||
r2_edges = []
|
||||
|
|
@ -509,9 +510,9 @@ class pin_layout:
|
|||
q.y <= max(p.y, r.y) and \
|
||||
q.y >= min(p.y, r.y):
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def segment_intersection(self, s1, s2):
|
||||
"""
|
||||
Determine the intersection point of two segments
|
||||
|
|
@ -524,22 +525,22 @@ class pin_layout:
|
|||
a1 = b.y - a.y
|
||||
b1 = a.x - b.x
|
||||
c1 = a1*a.x + b1*a.y
|
||||
|
||||
|
||||
# Line CD represented as a2x + b2y = c2
|
||||
a2 = d.y - c.y
|
||||
b2 = c.x - d.x
|
||||
c2 = a2*c.x + b2*c.y
|
||||
|
||||
|
||||
determinant = a1*b2 - a2*b1
|
||||
|
||||
if determinant != 0:
|
||||
x = (b2*c1 - b1*c2)/determinant
|
||||
y = (a1*c2 - a2*c1)/determinant
|
||||
|
||||
|
||||
r = vector(x, y).snap_to_grid()
|
||||
if self.on_segment(a, r, b) and self.on_segment(c, r, d):
|
||||
return r
|
||||
|
||||
|
||||
return None
|
||||
|
||||
def same_lpp(self, lpp1, lpp2):
|
||||
|
|
@ -549,5 +550,5 @@ class pin_layout:
|
|||
"""
|
||||
if lpp1[1] == None or lpp2[1] == None:
|
||||
return lpp1[0] == lpp2[0]
|
||||
|
||||
|
||||
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
import math
|
||||
|
||||
from globals import OPTS
|
||||
# default purpose layer is used for addText() in vlsiLayout.py
|
||||
if OPTS.tech_name == "s8":
|
||||
purposeLayer=20
|
||||
else:
|
||||
purposeLayer=0
|
||||
|
||||
class GdsStructure:
|
||||
"""Class represent a GDS Structure Object"""
|
||||
def __init__(self):
|
||||
|
|
@ -147,7 +140,7 @@ class GdsText:
|
|||
self.elementFlags=""
|
||||
self.plex=""
|
||||
self.drawingLayer=""
|
||||
self.purposeLayer=purposeLayer
|
||||
self.purposeLayer=0
|
||||
self.transFlags=[0,0,0]
|
||||
self.magFactor=""
|
||||
self.rotateAngle=""
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class VlsiLayout:
|
|||
modDate.hour,
|
||||
modDate.minute,
|
||||
modDate.second)
|
||||
|
||||
|
||||
self.info = dict() #information gathered from the GDSII header
|
||||
self.info['units']=self.units
|
||||
self.info['dates']=(modDate.year,
|
||||
|
|
@ -52,12 +52,12 @@ class VlsiLayout:
|
|||
modDate.second)
|
||||
self.info['libraryName']=libraryName
|
||||
self.info['gdsVersion']=gdsVersion
|
||||
|
||||
|
||||
self.xyTree = [] #This will contain a list of all structure names
|
||||
#expanded to include srefs / arefs separately.
|
||||
#each structure will have an X,Y,offset, and rotate associated
|
||||
#with it. Populate via traverseTheHierarchy method.
|
||||
|
||||
|
||||
#temp variables used in delegate functions
|
||||
self.tempCoordinates=None
|
||||
self.tempPassFail = True
|
||||
|
|
@ -73,14 +73,14 @@ class VlsiLayout:
|
|||
if(rotateAngle):
|
||||
angle = math.radians(float(rotateAngle))
|
||||
|
||||
coordinatesRotate = [] #this will hold the rotated values
|
||||
coordinatesRotate = [] #this will hold the rotated values
|
||||
for coordinate in coordinatesToRotate:
|
||||
# This is the CCW rotation matrix
|
||||
newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle)
|
||||
newY = coordinate[0]*math.sin(angle) + coordinate[1]*math.cos(angle)
|
||||
coordinatesRotate.extend((newX,newY))
|
||||
return coordinatesRotate
|
||||
|
||||
|
||||
def rename(self,newName):
|
||||
#take the root structure and copy it to a new structure with the new name
|
||||
self.structures[newName] = self.structures[self.rootStructureName]
|
||||
|
|
@ -129,8 +129,8 @@ class VlsiLayout:
|
|||
modDate.hour,
|
||||
modDate.minute,
|
||||
modDate.second)
|
||||
|
||||
|
||||
|
||||
|
||||
#repopulate the 2d map so drawing occurs correctly
|
||||
self.prepareForWrite()
|
||||
|
||||
|
|
@ -155,15 +155,15 @@ class VlsiLayout:
|
|||
debug.check(len(structureNames)==1,"Multiple possible root structures in the layout: {}".format(str(structureNames)))
|
||||
self.rootStructureName = structureNames[0]
|
||||
|
||||
|
||||
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None,
|
||||
|
||||
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None,
|
||||
transformPath = [], rotateAngle = 0, transFlags = [0,0,0], coordinates = (0,0)):
|
||||
#since this is a recursive function, must deal with the default
|
||||
#parameters explicitly
|
||||
#parameters explicitly
|
||||
if startingStructureName == None:
|
||||
startingStructureName = self.rootStructureName
|
||||
startingStructureName = self.rootStructureName
|
||||
|
||||
#set up the rotation matrix
|
||||
#set up the rotation matrix
|
||||
if(rotateAngle == None or rotateAngle == ""):
|
||||
angle = 0
|
||||
else:
|
||||
|
|
@ -193,10 +193,10 @@ class VlsiLayout:
|
|||
try:
|
||||
if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others?
|
||||
#if so, go through each and call this function again
|
||||
#if not, return back to the caller (caller can be this function)
|
||||
#if not, return back to the caller (caller can be this function)
|
||||
for sref in self.structures[startingStructureName].srefs:
|
||||
#here, we are going to modify the sref coordinates based on the parent objects rotation
|
||||
self.traverseTheHierarchy(startingStructureName = sref.sName,
|
||||
#here, we are going to modify the sref coordinates based on the parent objects rotation
|
||||
self.traverseTheHierarchy(startingStructureName = sref.sName,
|
||||
delegateFunction = delegateFunction,
|
||||
transformPath = transformPath,
|
||||
rotateAngle = sref.rotateAngle,
|
||||
|
|
@ -204,12 +204,12 @@ class VlsiLayout:
|
|||
coordinates = sref.coordinates)
|
||||
except KeyError:
|
||||
debug.error("Could not find structure {} in GDS file.".format(startingStructureName),-1)
|
||||
|
||||
|
||||
#MUST HANDLE AREFs HERE AS WELL
|
||||
#when we return, drop the last transform from the transformPath
|
||||
del transformPath[-1]
|
||||
return
|
||||
|
||||
|
||||
def initialize(self):
|
||||
self.deduceHierarchy()
|
||||
# self.traverseTheHierarchy()
|
||||
|
|
@ -217,17 +217,17 @@ class VlsiLayout:
|
|||
|
||||
for layerNumber in self.layerNumbersInUse:
|
||||
self.processLabelPins((layerNumber, None))
|
||||
|
||||
|
||||
|
||||
|
||||
def populateCoordinateMap(self):
|
||||
def addToXyTree(startingStructureName = None,transformPath = None):
|
||||
uVector = np.array([[1.0],[0.0],[0.0]]) #start with normal basis vectors
|
||||
vVector = np.array([[0.0],[1.0],[0.0]])
|
||||
origin = np.array([[0.0],[0.0],[1.0]]) #and an origin (Z component is 1.0 to indicate position instead of vector)
|
||||
#make a copy of all the transforms and reverse it
|
||||
#make a copy of all the transforms and reverse it
|
||||
reverseTransformPath = transformPath[:]
|
||||
if len(reverseTransformPath) > 1:
|
||||
reverseTransformPath.reverse()
|
||||
reverseTransformPath.reverse()
|
||||
#now go through each transform and apply them to our basis and origin in succession
|
||||
for transform in reverseTransformPath:
|
||||
origin = np.dot(transform[0], origin) #rotate
|
||||
|
|
@ -237,20 +237,20 @@ class VlsiLayout:
|
|||
uVector = np.dot(transform[1], uVector) #scale
|
||||
vVector = np.dot(transform[1], vVector) #scale
|
||||
origin = np.dot(transform[2], origin) #translate
|
||||
#we don't need to do a translation on the basis vectors
|
||||
#we don't need to do a translation on the basis vectors
|
||||
#uVector = transform[2] * uVector #translate
|
||||
#vVector = transform[2] * vVector #translate
|
||||
#populate the xyTree with each structureName and coordinate space
|
||||
self.xyTree.append((startingStructureName,origin,uVector,vVector))
|
||||
self.traverseTheHierarchy(delegateFunction = addToXyTree)
|
||||
|
||||
|
||||
def microns(self, userUnits):
|
||||
"""Utility function to convert user units to microns"""
|
||||
userUnit = self.units[1]/self.units[0]
|
||||
userUnitsPerMicron = userUnit / userunit
|
||||
layoutUnitsPerMicron = userUnitsPerMicron / self.units[0]
|
||||
return userUnits / layoutUnitsPerMicron
|
||||
|
||||
|
||||
def userUnits(self, microns):
|
||||
"""Utility function to convert microns to user units"""
|
||||
userUnit = self.units[1]/self.units[0]
|
||||
|
|
@ -270,7 +270,7 @@ class VlsiLayout:
|
|||
|
||||
if self.debug:
|
||||
debug.info(0,"DEBUG: GdsMill vlsiLayout: changeRoot: %s "%newRoot)
|
||||
|
||||
|
||||
# Determine if newRoot exists
|
||||
# layoutToAdd (default) or nameOfLayout
|
||||
if (newRoot == 0 | ((newRoot not in self.structures) & ~create)):
|
||||
|
|
@ -282,19 +282,19 @@ class VlsiLayout:
|
|||
self.rootStructureName = newRoot
|
||||
|
||||
|
||||
|
||||
|
||||
def addInstance(self,layoutToAdd,nameOfLayout=0,offsetInMicrons=(0,0),mirror=None,rotate=None):
|
||||
"""
|
||||
Method to insert one layout into another at a particular offset.
|
||||
"""
|
||||
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
|
||||
if self.debug:
|
||||
if self.debug:
|
||||
debug.info(0,"DEBUG: GdsMill vlsiLayout: addInstance: type {0}, nameOfLayout {1}".format(type(layoutToAdd),nameOfLayout))
|
||||
debug.info(0,"DEBUG: name={0} offset={1} mirror={2} rotate={3}".format(layoutToAdd.rootStructureName,offsetInMicrons, mirror, rotate))
|
||||
|
||||
|
||||
|
||||
# Determine if we are instantiating the root design of
|
||||
# Determine if we are instantiating the root design of
|
||||
# layoutToAdd (default) or nameOfLayout
|
||||
if nameOfLayout == 0:
|
||||
StructureFound = True
|
||||
|
|
@ -303,7 +303,7 @@ class VlsiLayout:
|
|||
StructureName = nameOfLayout #layoutToAdd
|
||||
StructureFound = False
|
||||
for structure in layoutToAdd.structures:
|
||||
if StructureName in structure:
|
||||
if StructureName in structure:
|
||||
if self.debug:
|
||||
debug.info(1,"DEBUG: Structure %s Found"%StructureName)
|
||||
StructureFound = True
|
||||
|
|
@ -311,7 +311,7 @@ class VlsiLayout:
|
|||
debug.check(StructureFound,"Could not find layout to instantiate {}".format(StructureName))
|
||||
|
||||
|
||||
# If layoutToAdd is a unique object (not this), then copy hierarchy,
|
||||
# If layoutToAdd is a unique object (not this), then copy hierarchy,
|
||||
# otherwise, if it is a text name of an internal structure, use it.
|
||||
|
||||
if layoutToAdd != self:
|
||||
|
|
@ -330,7 +330,7 @@ class VlsiLayout:
|
|||
layoutToAddSref.coordinates = offsetInLayoutUnits
|
||||
|
||||
if mirror or rotate:
|
||||
|
||||
|
||||
layoutToAddSref.transFlags = [0,0,0]
|
||||
# transFlags = (mirror around x-axis, magnification, rotation)
|
||||
# If magnification or rotation is true, it is the flags are then
|
||||
|
|
@ -356,7 +356,7 @@ class VlsiLayout:
|
|||
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].srefs.append(layoutToAddSref)
|
||||
|
||||
|
||||
def addBox(self,layerNumber=0, purposeNumber=0, offsetInMicrons=(0,0), width=1.0, height=1.0,center=False):
|
||||
"""
|
||||
Method to add a box to a layout
|
||||
|
|
@ -373,7 +373,7 @@ class VlsiLayout:
|
|||
(offsetInLayoutUnits[0],offsetInLayoutUnits[1]+heightInLayoutUnits),
|
||||
offsetInLayoutUnits]
|
||||
else:
|
||||
startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2.0, offsetInLayoutUnits[1]-heightInLayoutUnits/2.0)
|
||||
startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2.0, offsetInLayoutUnits[1]-heightInLayoutUnits/2.0)
|
||||
coordinates=[startPoint,
|
||||
(startPoint[0]+widthInLayoutUnits,startPoint[1]),
|
||||
(startPoint[0]+widthInLayoutUnits,startPoint[1]+heightInLayoutUnits),
|
||||
|
|
@ -386,7 +386,7 @@ class VlsiLayout:
|
|||
boundaryToAdd.purposeLayer = purposeNumber
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].boundaries.append(boundaryToAdd)
|
||||
|
||||
|
||||
def addPath(self, layerNumber=0, purposeNumber=0, coordinates=[(0,0)], width=1.0):
|
||||
"""
|
||||
Method to add a path to a layout
|
||||
|
|
@ -405,11 +405,12 @@ class VlsiLayout:
|
|||
pathToAdd.coordinates = layoutUnitCoordinates
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].paths.append(pathToAdd)
|
||||
|
||||
def addText(self, text, layerNumber=0, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
|
||||
|
||||
def addText(self, text, layerNumber=0, purposeNumber=0, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
|
||||
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
|
||||
textToAdd = GdsText()
|
||||
textToAdd.drawingLayer = layerNumber
|
||||
textToAdd.purposeLayer = purposeNumber
|
||||
textToAdd.coordinates = [offsetInLayoutUnits]
|
||||
textToAdd.transFlags = [0,0,0]
|
||||
textToAdd.textString = self.padText(text)
|
||||
|
|
@ -438,7 +439,7 @@ class VlsiLayout:
|
|||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def intersectionPoint(self,startPoint1,endPoint1,startPoint2,endPoint2):
|
||||
if((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])!=0):
|
||||
pSlope = (endPoint1[1]-startPoint1[1])/(endPoint1[0]-startPoint1[0])
|
||||
|
|
@ -458,7 +459,7 @@ class VlsiLayout:
|
|||
newY = None
|
||||
elif((endPoint1[0]-startPoint1[0])==0 and (endPoint2[0]-startPoint2[0])!=0):
|
||||
qSlope = (endPoint2[1]-startPoint2[1])/(endPoint2[0]-startPoint2[0])
|
||||
qIntercept = startPoint2[1]-qSlope*startPoint2[0]
|
||||
qIntercept = startPoint2[1]-qSlope*startPoint2[0]
|
||||
newX=endPoint1[0]
|
||||
newY=qSlope*newX+qIntercept
|
||||
elif((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])==0):
|
||||
|
|
@ -467,14 +468,14 @@ class VlsiLayout:
|
|||
newX=endPoint2[0]
|
||||
newY=pSlope*newX+pIntercept
|
||||
return (newX,newY)
|
||||
|
||||
|
||||
def isCollinear(self,testPoint,point1,point2):
|
||||
slope1 = (testPoint[1]-point1[1])/(testPoint[0]-point1[0])
|
||||
slope2 = (point2[1]-point1[1])/(point2[0]-point1[0])
|
||||
if slope1 == slope2:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def doShapesIntersect(self,shape1Coordinates, shape2Coordinates):
|
||||
"""
|
||||
Utility function to determine if 2 arbitrary shapes intersect.
|
||||
|
|
@ -491,7 +492,7 @@ class VlsiLayout:
|
|||
if(self.isBounded(intersect,startPoint1,endPoint1) and self.isBounded(intersect,startPoint2,endPoint2)):
|
||||
return True #these shapes overlap!
|
||||
return False #these shapes are ok
|
||||
|
||||
|
||||
def isPointInsideOfBox(self,pointCoordinates,boxCoordinates):
|
||||
"""
|
||||
Check if a point is contained in the shape
|
||||
|
|
@ -516,7 +517,7 @@ class VlsiLayout:
|
|||
pointCoordinates[1]<bottomBound):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def isShapeInsideOfBox(self,shapeCoordinates, boxCoordinates):
|
||||
"""
|
||||
Go through every point in the shape to test if they are all inside the box.
|
||||
|
|
@ -525,8 +526,8 @@ class VlsiLayout:
|
|||
if not self.isPointInsideOfBox(point,boxCoordinates):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
def fillAreaDensity(self, layerToFill = 0, offsetInMicrons = (0,0), coverageWidth = 100.0, coverageHeight = 100.0, minSpacing = 0.22, blockSize = 1.0):
|
||||
effectiveBlock = blockSize+minSpacing
|
||||
widthInBlocks = int(coverageWidth/effectiveBlock)
|
||||
|
|
@ -545,7 +546,7 @@ class VlsiLayout:
|
|||
shiftedBoundaryCoordinates.append((shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1]))
|
||||
joint = self.doShapesIntersect(self.tempCoordinates, shiftedBoundaryCoordinates)
|
||||
if joint:
|
||||
self.tempPassFail = False
|
||||
self.tempPassFail = False
|
||||
common = self.isShapeInsideOfBox(shiftedBoundaryCoordinates,self.tempCoordinates)
|
||||
if common:
|
||||
self.tempPassFail = False
|
||||
|
|
@ -558,11 +559,11 @@ class VlsiLayout:
|
|||
shiftedBoundaryCoordinates.append((shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1]))
|
||||
joint = self.doShapesIntersect(self.tempCoordinates, shiftedBoundaryCoordinates)
|
||||
if joint:
|
||||
self.tempPassFail = False
|
||||
self.tempPassFail = False
|
||||
common = self.isShapeInsideOfBox(shiftedBoundaryCoordinates,self.tempCoordinates)
|
||||
if common:
|
||||
self.tempPassFail = False
|
||||
|
||||
|
||||
for yIndex in range(0,heightInBlocks):
|
||||
for xIndex in range(0,widthInBlocks):
|
||||
percentDone = (float((yIndex*heightInBlocks)+xIndex) / (heightInBlocks*widthInBlocks))*100
|
||||
|
|
@ -581,7 +582,7 @@ class VlsiLayout:
|
|||
passFailRecord.append(self.tempPassFail)
|
||||
print("Percent Complete:"+str(percentDone))
|
||||
|
||||
|
||||
|
||||
passFailIndex=0
|
||||
for yIndex in range(0,heightInBlocks):
|
||||
for xIndex in range(0,widthInBlocks):
|
||||
|
|
@ -632,7 +633,7 @@ class VlsiLayout:
|
|||
self.units[0]*cellBoundary[1]],
|
||||
[self.units[0]*cellBoundary[2],
|
||||
self.units[0]*cellBoundary[3]]]
|
||||
|
||||
|
||||
def measureSizeInStructure(self, structure, cellBoundary):
|
||||
(structureName, structureOrigin,
|
||||
structureuVector, structurevVector) = structure
|
||||
|
|
@ -645,7 +646,7 @@ class VlsiLayout:
|
|||
thisBoundary[2]+structureOrigin[0],thisBoundary[3]+structureOrigin[1]]
|
||||
cellBoundary=self.updateBoundary(thisBoundary,cellBoundary)
|
||||
return cellBoundary
|
||||
|
||||
|
||||
def updateBoundary(self,thisBoundary,cellBoundary):
|
||||
[left_bott_X,left_bott_Y,right_top_X,right_top_Y]=thisBoundary
|
||||
# If any are None
|
||||
|
|
@ -672,7 +673,7 @@ class VlsiLayout:
|
|||
lpp):
|
||||
text_list.append(Text)
|
||||
return text_list
|
||||
|
||||
|
||||
def getPinShape(self, pin_name):
|
||||
"""
|
||||
Search for a pin label and return the largest enclosing rectangle
|
||||
|
|
@ -693,7 +694,7 @@ class VlsiLayout:
|
|||
max_pins.append(max_pin)
|
||||
|
||||
return max_pins
|
||||
|
||||
|
||||
|
||||
def getAllPinShapes(self, pin_name):
|
||||
"""
|
||||
|
|
@ -716,7 +717,7 @@ class VlsiLayout:
|
|||
"""
|
||||
# Get the labels on a layer in the root level
|
||||
labels = self.getTexts(lpp)
|
||||
|
||||
|
||||
# Get all of the shapes on the layer at all levels
|
||||
# and transform them to the current level
|
||||
shapes = self.getAllShapes(lpp)
|
||||
|
|
@ -730,7 +731,7 @@ class VlsiLayout:
|
|||
pin_shapes.append((lpp, boundary))
|
||||
|
||||
label_text = label.textString
|
||||
|
||||
|
||||
# Remove the padding if it exists
|
||||
if label_text[-1] == "\x00":
|
||||
label_text = label_text[0:-1]
|
||||
|
|
@ -740,7 +741,7 @@ class VlsiLayout:
|
|||
except KeyError:
|
||||
self.pins[label_text] = []
|
||||
self.pins[label_text].append(pin_shapes)
|
||||
|
||||
|
||||
def getBlockages(self, lpp):
|
||||
"""
|
||||
Return all blockages on a given layer in
|
||||
|
|
@ -755,7 +756,7 @@ class VlsiLayout:
|
|||
for i in range(0, len(boundary), 2):
|
||||
vectors.append((boundary[i], boundary[i+1]))
|
||||
blockages.append(vectors)
|
||||
|
||||
|
||||
return blockages
|
||||
|
||||
def getAllShapes(self, lpp):
|
||||
|
|
@ -870,7 +871,7 @@ class VlsiLayout:
|
|||
"""
|
||||
Rotate a coordinate in space.
|
||||
"""
|
||||
# MRG: 9/3/18 Incorrect matrix multiplication!
|
||||
# MRG: 9/3/18 Incorrect matrix multiplication!
|
||||
# This is fixed to be:
|
||||
# |u[0] v[0]| |x| |x'|
|
||||
# |u[1] v[1]|x|y|=|y'|
|
||||
|
|
@ -892,7 +893,7 @@ class VlsiLayout:
|
|||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def sameLPP(lpp1, lpp2):
|
||||
"""
|
||||
Check if the layers and purposes are the same.
|
||||
|
|
@ -900,14 +901,13 @@ def sameLPP(lpp1, lpp2):
|
|||
"""
|
||||
if lpp1[1] == None or lpp2[1] == None:
|
||||
return lpp1[0] == lpp2[0]
|
||||
|
||||
|
||||
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]
|
||||
|
||||
|
||||
|
||||
|
||||
def boundaryArea(A):
|
||||
"""
|
||||
Returns boundary area for sorting.
|
||||
"""
|
||||
area_A=(A[2]-A[0])*(A[3]-A[1])
|
||||
return area_A
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import re
|
|||
import copy
|
||||
import importlib
|
||||
|
||||
VERSION = "1.1.3"
|
||||
VERSION = "1.1.4"
|
||||
NAME = "OpenRAM v{}".format(VERSION)
|
||||
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@ from globals import OPTS
|
|||
class bank(design.design):
|
||||
"""
|
||||
Dynamically generated a single bank including bitcell array,
|
||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
||||
write driver and sense amplifiers.
|
||||
This can create up to two ports in any combination: rw, w, r.
|
||||
This can create up to two ports in any combination: rw, w, r.
|
||||
"""
|
||||
|
||||
def __init__(self, sram_config, name=""):
|
||||
|
|
@ -27,15 +27,15 @@ class bank(design.design):
|
|||
self.sram_config = sram_config
|
||||
sram_config.set_local_config(self)
|
||||
if self.write_size:
|
||||
self.num_wmasks = int(self.word_size/self.write_size)
|
||||
self.num_wmasks = int(self.word_size / self.write_size)
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
if name == "":
|
||||
name = "bank_{0}_{1}".format(self.word_size, self.num_words)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words))
|
||||
|
||||
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
|
||||
self.num_words))
|
||||
|
||||
# The local control signals are gated when we have bank select logic,
|
||||
# so this prefix will be added to all of the input signals to create
|
||||
|
|
@ -47,18 +47,17 @@ class bank(design.design):
|
|||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
|
||||
debug.check(len(self.all_ports)<=2,
|
||||
"Bank layout cannot handle more than two ports.")
|
||||
self.create_layout()
|
||||
self.add_boundary()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
|
||||
self.compute_sizes()
|
||||
self.add_modules()
|
||||
self.add_pins() # Must create the replica bitcell array first
|
||||
self.create_instances()
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
self.place_instances()
|
||||
|
|
@ -66,35 +65,34 @@ class bank(design.design):
|
|||
self.route_layout()
|
||||
|
||||
# Can remove the following, but it helps for debug!
|
||||
#self.add_lvs_correspondence_points()
|
||||
# self.add_lvs_correspondence_points()
|
||||
|
||||
# Remember the bank center for further placement
|
||||
self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1)
|
||||
self.bank_array_ll = self.offset_all_coordinates().scale(-1, -1)
|
||||
self.bank_array_ur = self.bitcell_array_inst.ur()
|
||||
self.bank_array_ul = self.bitcell_array_inst.ul()
|
||||
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
""" Adding pins for Bank module"""
|
||||
for port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("dout{0}_{1}".format(port,bit),"OUTPUT")
|
||||
self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT")
|
||||
for port in self.all_ports:
|
||||
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]),"OUTPUT")
|
||||
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("din{0}_{1}".format(port,bit),"INPUT")
|
||||
self.add_pin("din{0}_{1}".format(port,bit), "INPUT")
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.addr_size):
|
||||
self.add_pin("addr{0}_{1}".format(port,bit),"INPUT")
|
||||
self.add_pin("addr{0}_{1}".format(port,bit), "INPUT")
|
||||
|
||||
# For more than one bank, we have a bank select and name
|
||||
# the signals gated_*.
|
||||
if self.num_banks > 1:
|
||||
for port in self.all_ports:
|
||||
self.add_pin("bank_sel{}".format(port),"INPUT")
|
||||
self.add_pin("bank_sel{}".format(port), "INPUT")
|
||||
for port in self.read_ports:
|
||||
self.add_pin("s_en{0}".format(port), "INPUT")
|
||||
for port in self.all_ports:
|
||||
|
|
@ -102,12 +100,11 @@ class bank(design.design):
|
|||
for port in self.write_ports:
|
||||
self.add_pin("w_en{0}".format(port), "INPUT")
|
||||
for bit in range(self.num_wmasks):
|
||||
self.add_pin("bank_wmask{0}_{1}".format(port,bit),"INPUT")
|
||||
self.add_pin("bank_wmask{0}_{1}".format(port, bit), "INPUT")
|
||||
for port in self.all_ports:
|
||||
self.add_pin("wl_en{0}".format(port), "INPUT")
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def route_layout(self):
|
||||
""" Create routing amoung the modules """
|
||||
|
|
@ -124,18 +121,27 @@ class bank(design.design):
|
|||
|
||||
self.route_supplies()
|
||||
|
||||
def route_rbl(self,port):
|
||||
def route_rbl(self, port):
|
||||
""" Route the rbl_bl and rbl_wl """
|
||||
|
||||
bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
||||
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
|
||||
self.add_layout_pin(text="rbl_bl{0}".format(port),
|
||||
layer=bl_pin.layer,
|
||||
offset=bl_pin.ll(),
|
||||
height=bl_pin.height(),
|
||||
width=bl_pin.width())
|
||||
|
||||
|
||||
# This will ensure the pin is only on the top or bottom edge
|
||||
if port % 2:
|
||||
via_offset = bl_pin.uc() + vector(0, self.m2_pitch)
|
||||
left_right_offset = vector(self.max_x_offset, via_offset.y)
|
||||
else:
|
||||
via_offset = bl_pin.bc() - vector(0, self.m2_pitch)
|
||||
left_right_offset = vector(self.min_x_offset, via_offset.y)
|
||||
if bl_pin == "m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=via_offset)
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=via_offset)
|
||||
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
|
||||
layer="m3",
|
||||
start=left_right_offset,
|
||||
end=via_offset)
|
||||
|
||||
def route_bitlines(self, port):
|
||||
""" Route the bitlines depending on the port type rw, w, or r. """
|
||||
|
|
@ -146,7 +152,6 @@ class bank(design.design):
|
|||
self.route_port_data_out(port)
|
||||
self.route_port_data_to_bitcell_array(port)
|
||||
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the instances of the netlist. """
|
||||
|
||||
|
|
@ -161,19 +166,18 @@ class bank(design.design):
|
|||
Compute the empty instance offsets for port0 and port1 (if needed)
|
||||
"""
|
||||
|
||||
self.port_data_offsets = [None]*len(self.all_ports)
|
||||
self.port_address_offsets = [None]*len(self.all_ports)
|
||||
|
||||
self.column_decoder_offsets = [None]*len(self.all_ports)
|
||||
self.bank_select_offsets = [None]*len(self.all_ports)
|
||||
self.port_data_offsets = [None] * len(self.all_ports)
|
||||
self.port_address_offsets = [None] * len(self.all_ports)
|
||||
|
||||
self.column_decoder_offsets = [None] * len(self.all_ports)
|
||||
self.bank_select_offsets = [None] * len(self.all_ports)
|
||||
|
||||
# The center point for these cells are the upper-right corner of
|
||||
# the bitcell array.
|
||||
# The port address decoder/driver logic is placed on the right and mirrored on Y-axis.
|
||||
# The port data write/sense/precharge/mux is placed on the top and mirrored on the X-axis.
|
||||
self.bitcell_array_top = self.bitcell_array.height
|
||||
self.bitcell_array_right = self.bitcell_array.width
|
||||
self.bitcell_array_top = self.bitcell_array.height
|
||||
self.bitcell_array_right = self.bitcell_array.width
|
||||
|
||||
# These are the offsets of the main array (excluding dummy and replica rows/cols)
|
||||
self.main_bitcell_array_top = self.bitcell_array.bitcell_array_inst.uy()
|
||||
|
|
@ -185,7 +189,6 @@ class bank(design.design):
|
|||
self.compute_instance_port0_offsets()
|
||||
if len(self.all_ports)==2:
|
||||
self.compute_instance_port1_offsets()
|
||||
|
||||
|
||||
def compute_instance_port0_offsets(self):
|
||||
"""
|
||||
|
|
@ -196,29 +199,30 @@ class bank(design.design):
|
|||
|
||||
# UPPER RIGHT QUADRANT
|
||||
# Bitcell array is placed at (0,0)
|
||||
self.bitcell_array_offset = vector(0,0)
|
||||
self.bitcell_array_offset = vector(0, 0)
|
||||
|
||||
# LOWER RIGHT QUADRANT
|
||||
# Below the bitcell array
|
||||
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width,0)
|
||||
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0)
|
||||
|
||||
# UPPER LEFT QUADRANT
|
||||
# To the left of the bitcell array
|
||||
x_offset = self.m2_gap + self.port_address.width
|
||||
self.port_address_offsets[port] = vector(-x_offset,self.main_bitcell_array_bottom)
|
||||
x_offset = self.m2_gap + self.port_address.width
|
||||
self.port_address_offsets[port] = vector(-x_offset,
|
||||
self.main_bitcell_array_bottom)
|
||||
|
||||
# LOWER LEFT QUADRANT
|
||||
# Place the col decoder left aligned with wordline driver
|
||||
# Place the col decoder left aligned with wordline driver
|
||||
# This is also placed so that it's supply rails do not align with the SRAM-level
|
||||
# control logic to allow control signals to easily pass over in M3
|
||||
# by placing 1/2 a cell pitch down
|
||||
x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width
|
||||
if self.col_addr_size > 0:
|
||||
x_offset += self.column_decoder.width + self.col_addr_bus_width
|
||||
y_offset = 0.5*self.dff.height + self.column_decoder.height
|
||||
y_offset = 0.5 * self.dff.height + self.column_decoder.height
|
||||
else:
|
||||
y_offset = 0
|
||||
self.column_decoder_offsets[port] = vector(-x_offset,-y_offset)
|
||||
self.column_decoder_offsets[port] = vector(-x_offset, -y_offset)
|
||||
|
||||
# Bank select gets placed below the column decoder (x_offset doesn't change)
|
||||
if self.col_addr_size > 0:
|
||||
|
|
@ -227,7 +231,7 @@ class bank(design.design):
|
|||
y_offset = self.port_address_offsets[port].y
|
||||
if self.num_banks > 1:
|
||||
y_offset += self.bank_select.height + drc("well_to_well")
|
||||
self.bank_select_offsets[port] = vector(-x_offset,-y_offset)
|
||||
self.bank_select_offsets[port] = vector(-x_offset, -y_offset)
|
||||
|
||||
def compute_instance_port1_offsets(self):
|
||||
"""
|
||||
|
|
@ -246,18 +250,19 @@ class bank(design.design):
|
|||
# LOWER RIGHT QUADRANT
|
||||
# To the left of the bitcell array
|
||||
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap
|
||||
self.port_address_offsets[port] = vector(x_offset,self.main_bitcell_array_bottom)
|
||||
self.port_address_offsets[port] = vector(x_offset,
|
||||
self.main_bitcell_array_bottom)
|
||||
|
||||
# UPPER RIGHT QUADRANT
|
||||
# Place the col decoder right aligned with wordline driver
|
||||
# Place the col decoder right aligned with wordline driver
|
||||
# Above the bitcell array with a well spacing
|
||||
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width
|
||||
x_offset = self.bitcell_array_right + self.central_bus_width[port] + self.port_address.wordline_driver.width
|
||||
if self.col_addr_size > 0:
|
||||
x_offset += self.column_decoder.width + self.col_addr_bus_width
|
||||
y_offset = self.bitcell_array_top + 0.5*self.dff.height + self.column_decoder.height
|
||||
y_offset = self.bitcell_array_top + 0.5 * self.dff.height + self.column_decoder.height
|
||||
else:
|
||||
y_offset = self.bitcell_array_top
|
||||
self.column_decoder_offsets[port] = vector(x_offset,y_offset)
|
||||
self.column_decoder_offsets[port] = vector(x_offset, y_offset)
|
||||
|
||||
# Bank select gets placed above the column decoder (x_offset doesn't change)
|
||||
if self.col_addr_size > 0:
|
||||
|
|
@ -265,7 +270,7 @@ class bank(design.design):
|
|||
self.port_data[port].column_mux_offset.y + self.port_data[port].column_mux_array.height)
|
||||
else:
|
||||
y_offset = self.port_address_offsets[port].y
|
||||
self.bank_select_offsets[port] = vector(x_offset,y_offset)
|
||||
self.bank_select_offsets[port] = vector(x_offset, y_offset)
|
||||
|
||||
def place_instances(self):
|
||||
""" Place the instances. """
|
||||
|
|
@ -281,11 +286,10 @@ class bank(design.design):
|
|||
self.place_column_decoder(self.column_decoder_offsets)
|
||||
self.place_bank_select(self.bank_select_offsets)
|
||||
|
||||
|
||||
def compute_sizes(self):
|
||||
""" Computes the required sizes to create the bank """
|
||||
|
||||
self.num_cols = int(self.words_per_row*self.word_size)
|
||||
self.num_cols = int(self.words_per_row * self.word_size)
|
||||
self.num_rows_temp = int(self.num_words / self.words_per_row)
|
||||
self.num_rows = self.num_rows_temp + self.num_spare_rows
|
||||
|
||||
|
|
@ -293,8 +297,10 @@ class bank(design.design):
|
|||
self.col_addr_size = int(log(self.words_per_row, 2))
|
||||
self.addr_size = self.col_addr_size + self.row_addr_size
|
||||
|
||||
debug.check(self.num_rows_temp*self.num_cols==self.word_size*self.num_words,"Invalid bank sizes.")
|
||||
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.")
|
||||
debug.check(self.num_rows_temp * self.num_cols==self.word_size * self.num_words,
|
||||
"Invalid bank sizes.")
|
||||
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,
|
||||
"Invalid address break down.")
|
||||
|
||||
# The order of the control signals on the control bus:
|
||||
self.input_control_signals = []
|
||||
|
|
@ -313,13 +319,13 @@ class bank(design.design):
|
|||
self.num_control_lines = [len(x) for x in self.input_control_signals]
|
||||
|
||||
# The width of this bus is needed to place other modules (e.g. decoder) for each port
|
||||
self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines]
|
||||
self.central_bus_width = [self.m2_pitch * x + self.m2_width for x in self.num_control_lines]
|
||||
|
||||
# These will be outputs of the gaters if this is multibank, if not, normal signals.
|
||||
self.control_signals = []
|
||||
for port in self.all_ports:
|
||||
if self.num_banks > 1:
|
||||
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
|
||||
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
|
||||
else:
|
||||
self.control_signals.append(self.input_control_signals[port])
|
||||
|
||||
|
|
@ -327,19 +333,18 @@ class bank(design.design):
|
|||
if self.col_addr_size>0:
|
||||
self.num_col_addr_lines = 2**self.col_addr_size
|
||||
else:
|
||||
self.num_col_addr_lines = 0
|
||||
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
|
||||
self.num_col_addr_lines = 0
|
||||
self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
|
||||
|
||||
# A space for wells or jogging m2
|
||||
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3*self.m2_pitch)
|
||||
|
||||
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3 * self.m2_pitch)
|
||||
|
||||
def add_modules(self):
|
||||
""" Add all the modules using the class loader """
|
||||
|
||||
# create arrays of bitline and bitline_bar names for read, write, or all ports
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bl_names = self.bitcell.get_all_bl_names()
|
||||
self.br_names = self.bitcell.get_all_br_names()
|
||||
self.wl_names = self.bitcell.get_all_wl_names()
|
||||
|
|
@ -353,13 +358,11 @@ class bank(design.design):
|
|||
self.port_data.append(temp_pre)
|
||||
self.add_mod(self.port_data[port])
|
||||
|
||||
|
||||
self.port_address = factory.create(module_type="port_address",
|
||||
cols=self.num_cols,
|
||||
rows=self.num_rows)
|
||||
self.add_mod(self.port_address)
|
||||
|
||||
|
||||
self.port_rbl_map = self.all_ports
|
||||
self.num_rbl = len(self.all_ports)
|
||||
|
||||
|
|
@ -371,22 +374,20 @@ class bank(design.design):
|
|||
bitcell_ports=self.all_ports)
|
||||
self.add_mod(self.bitcell_array)
|
||||
|
||||
|
||||
if(self.num_banks > 1):
|
||||
self.bank_select = factory.create(module_type="bank_select")
|
||||
self.add_mod(self.bank_select)
|
||||
|
||||
|
||||
def create_bitcell_array(self):
|
||||
""" Creating Bitcell Array """
|
||||
|
||||
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
|
||||
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
|
||||
mod=self.bitcell_array)
|
||||
|
||||
temp = []
|
||||
for col in range(self.num_cols):
|
||||
for bitline in self.bitline_names:
|
||||
temp.append("{0}_{1}".format(bitline,col))
|
||||
temp.append("{0}_{1}".format(bitline, col))
|
||||
for rbl in range(self.num_rbl):
|
||||
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl)
|
||||
temp.append(rbl_bl_name)
|
||||
|
|
@ -394,23 +395,21 @@ class bank(design.design):
|
|||
temp.append(rbl_br_name)
|
||||
for row in range(self.num_rows):
|
||||
for wordline in self.wl_names:
|
||||
temp.append("{0}_{1}".format(wordline,row))
|
||||
temp.append("{0}_{1}".format(wordline, row))
|
||||
for port in self.all_ports:
|
||||
temp.append("wl_en{0}".format(port))
|
||||
temp.append("vdd")
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_bitcell_array(self, offset):
|
||||
""" Placing Bitcell Array """
|
||||
self.bitcell_array_inst.place(offset)
|
||||
|
||||
|
||||
def create_port_data(self):
|
||||
""" Creating Port Data """
|
||||
|
||||
self.port_data_inst = [None]*len(self.all_ports)
|
||||
self.port_data_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port),
|
||||
mod=self.port_data[port])
|
||||
|
|
@ -420,17 +419,17 @@ class bank(design.design):
|
|||
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port])
|
||||
temp.append(rbl_bl_name)
|
||||
temp.append(rbl_br_name)
|
||||
for col in range(self.num_cols):
|
||||
temp.append("{0}_{1}".format(self.bl_names[port],col))
|
||||
temp.append("{0}_{1}".format(self.br_names[port],col))
|
||||
for col in range(self.num_cols):
|
||||
temp.append("{0}_{1}".format(self.bl_names[port], col))
|
||||
temp.append("{0}_{1}".format(self.br_names[port], col))
|
||||
if port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
temp.append("dout{0}_{1}".format(port,bit))
|
||||
temp.append("dout{0}_{1}".format(port, bit))
|
||||
if port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
temp.append("din{0}_{1}".format(port,bit))
|
||||
temp.append("din{0}_{1}".format(port, bit))
|
||||
# Will be empty if no col addr lines
|
||||
sel_names = ["sel{0}_{1}".format(port,x) for x in range(self.num_col_addr_lines)]
|
||||
sel_names = ["sel{0}_{1}".format(port, x) for x in range(self.num_col_addr_lines)]
|
||||
temp.extend(sel_names)
|
||||
if port in self.read_ports:
|
||||
temp.append("s_en{0}".format(port))
|
||||
|
|
@ -439,17 +438,16 @@ class bank(design.design):
|
|||
temp.append("w_en{0}".format(port))
|
||||
for bit in range(self.num_wmasks):
|
||||
temp.append("bank_wmask{0}_{1}".format(port, bit))
|
||||
temp.extend(["vdd","gnd"])
|
||||
temp.extend(["vdd", "gnd"])
|
||||
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_port_data(self, offsets):
|
||||
""" Placing Port Data """
|
||||
|
||||
for port in self.all_ports:
|
||||
# Top one is unflipped, bottom is flipped along X direction
|
||||
if port%2 == 1:
|
||||
if port % 2 == 1:
|
||||
mirror = "R0"
|
||||
else:
|
||||
mirror = "MX"
|
||||
|
|
@ -458,42 +456,40 @@ class bank(design.design):
|
|||
def create_port_address(self):
|
||||
""" Create the hierarchical row decoder """
|
||||
|
||||
self.port_address_inst = [None]*len(self.all_ports)
|
||||
self.port_address_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
|
||||
self.port_address_inst[port] = self.add_inst(name="port_address{}".format(port),
|
||||
mod=self.port_address)
|
||||
|
||||
temp = []
|
||||
for bit in range(self.row_addr_size):
|
||||
temp.append("addr{0}_{1}".format(port,bit+self.col_addr_size))
|
||||
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
|
||||
temp.append("wl_en{0}".format(port))
|
||||
for row in range(self.num_rows):
|
||||
temp.append("{0}_{1}".format(self.wl_names[port],row))
|
||||
temp.append("{0}_{1}".format(self.wl_names[port], row))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_port_address(self, offsets):
|
||||
""" Place the hierarchical row decoder """
|
||||
|
||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
|
||||
|
||||
# The address and control bus will be in between decoder and the main memory array
|
||||
# This bus will route address bits to the decoder input and column mux inputs.
|
||||
# The address and control bus will be in between decoder and the main memory array
|
||||
# This bus will route address bits to the decoder input and column mux inputs.
|
||||
# The wires are actually routed after we placed the stuff on both sides.
|
||||
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
||||
# The address flop and decoder are aligned in the x coord.
|
||||
|
||||
for port in self.all_ports:
|
||||
if port%2:
|
||||
if port % 2:
|
||||
mirror = "MY"
|
||||
else:
|
||||
mirror = "R0"
|
||||
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||
|
||||
|
||||
def create_column_decoder(self):
|
||||
"""
|
||||
"""
|
||||
Create a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
|
||||
|
|
@ -511,40 +507,38 @@ class bank(design.design):
|
|||
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height)
|
||||
else:
|
||||
# No error checking before?
|
||||
debug.error("Invalid column decoder?",-1)
|
||||
debug.error("Invalid column decoder?", -1)
|
||||
self.add_mod(self.column_decoder)
|
||||
|
||||
self.column_decoder_inst = [None]*len(self.all_ports)
|
||||
self.column_decoder_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
|
||||
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
|
||||
mod=self.column_decoder)
|
||||
|
||||
temp = []
|
||||
for bit in range(self.col_addr_size):
|
||||
temp.append("addr{0}_{1}".format(port,bit))
|
||||
temp.append("addr{0}_{1}".format(port, bit))
|
||||
for bit in range(self.num_col_addr_lines):
|
||||
temp.append("sel{0}_{1}".format(port,bit))
|
||||
temp.append("sel{0}_{1}".format(port, bit))
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_column_decoder(self, offsets):
|
||||
"""
|
||||
"""
|
||||
Place a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
if self.col_addr_size == 0:
|
||||
return
|
||||
|
||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.")
|
||||
debug.check(len(offsets)>=len(self.all_ports),
|
||||
"Insufficient offsets to place column decoder.")
|
||||
|
||||
for port in self.all_ports:
|
||||
if port%2 == 1:
|
||||
if port % 2 == 1:
|
||||
mirror = "XY"
|
||||
else:
|
||||
mirror = "R0"
|
||||
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
|
||||
|
||||
|
||||
|
||||
def create_bank_select(self):
|
||||
""" Create the bank select logic. """
|
||||
|
|
@ -552,7 +546,7 @@ class bank(design.design):
|
|||
if not self.num_banks > 1:
|
||||
return
|
||||
|
||||
self.bank_select_inst = [None]*len(self.all_ports)
|
||||
self.bank_select_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
|
||||
mod=self.bank_select)
|
||||
|
|
@ -564,24 +558,23 @@ class bank(design.design):
|
|||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_bank_select(self, offsets):
|
||||
""" Place the bank select logic. """
|
||||
|
||||
if not self.num_banks > 1:
|
||||
return
|
||||
|
||||
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place bank select logic.")
|
||||
debug.check(len(offsets)>=len(self.all_ports),
|
||||
"Insufficient offsets to place bank select logic.")
|
||||
|
||||
for port in self.all_ports:
|
||||
self.bank_select_inst[port].place(offsets[port])
|
||||
|
||||
|
||||
def route_supplies(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
for inst in self.insts:
|
||||
self.copy_power_pins(inst,"vdd")
|
||||
self.copy_power_pins(inst,"gnd")
|
||||
self.copy_power_pins(inst, "vdd")
|
||||
self.copy_power_pins(inst, "gnd")
|
||||
|
||||
def route_bank_select(self, port):
|
||||
""" Route the bank select logic. """
|
||||
|
|
@ -596,7 +589,7 @@ class bank(design.design):
|
|||
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
|
||||
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
|
||||
|
||||
copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)]
|
||||
copy_control_signals = self.input_control_signals[port] + ["bank_sel{}".format(port)]
|
||||
for signal in range(len(copy_control_signals)):
|
||||
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
|
||||
|
||||
|
|
@ -605,25 +598,24 @@ class bank(design.design):
|
|||
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
|
||||
name = self.control_signals[port][signal]
|
||||
bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y)
|
||||
self.add_path("m3",[out_pos, bus_pos])
|
||||
self.add_path("m3", [out_pos, bus_pos])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=bus_pos)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=out_pos)
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=out_pos)
|
||||
|
||||
|
||||
def setup_routing_constraints(self):
|
||||
"""
|
||||
"""
|
||||
After the modules are instantiated, find the dimensions for the
|
||||
control bus, power ring, etc.
|
||||
control bus, power ring, etc.
|
||||
"""
|
||||
|
||||
self.max_y_offset = max([x.uy() for x in self.insts]) + 3*self.m1_width
|
||||
self.max_y_offset = max([x.uy() for x in self.insts]) + 3 * self.m1_width
|
||||
self.min_y_offset = min([x.by() for x in self.insts])
|
||||
|
||||
self.max_x_offset = max([x.rx() for x in self.insts]) + 3*self.m1_width
|
||||
self.max_x_offset = max([x.rx() for x in self.insts]) + 3 * self.m1_width
|
||||
self.min_x_offset = min([x.lx() for x in self.insts])
|
||||
|
||||
# # Create the core bbox for the power rings
|
||||
|
|
@ -633,7 +625,6 @@ class bank(design.design):
|
|||
|
||||
self.height = ur.y - ll.y
|
||||
self.width = ur.x - ll.x
|
||||
|
||||
|
||||
def route_central_bus(self):
|
||||
""" Create the address, supply, and control signal central bus lines. """
|
||||
|
|
@ -641,13 +632,13 @@ class bank(design.design):
|
|||
# Overall central bus width. It includes all the column mux lines,
|
||||
# and control lines.
|
||||
|
||||
self.bus_xoffset = [None]*len(self.all_ports)
|
||||
self.bus_xoffset = [None] * len(self.all_ports)
|
||||
# Port 0
|
||||
# The bank is at (0,0), so this is to the left of the y-axis.
|
||||
# 2 pitches on the right for vias/jogs to access the inputs
|
||||
# 2 pitches on the right for vias/jogs to access the inputs
|
||||
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset)
|
||||
# The control bus is routed up to two pitches below the bitcell array
|
||||
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2*self.m1_pitch
|
||||
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
|
||||
self.bus_xoffset[0] = self.create_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=control_bus_offset,
|
||||
|
|
@ -659,7 +650,7 @@ class bank(design.design):
|
|||
# Port 1
|
||||
if len(self.all_ports)==2:
|
||||
# The other control bus is routed up to two pitches above the bitcell array
|
||||
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2*self.m1_pitch
|
||||
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
|
||||
control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch,
|
||||
self.max_y_offset - control_bus_length)
|
||||
# The bus for the right port is reversed so that the rbl_wl is closest to the array
|
||||
|
|
@ -670,7 +661,6 @@ class bank(design.design):
|
|||
length=control_bus_length,
|
||||
vertical=True,
|
||||
make_pins=(self.num_banks==1))
|
||||
|
||||
|
||||
def route_port_data_to_bitcell_array(self, port):
|
||||
""" Routing of BL and BR between port data and bitcell array """
|
||||
|
|
@ -678,15 +668,19 @@ class bank(design.design):
|
|||
# Connect the regular bitlines
|
||||
inst2 = self.port_data_inst[port]
|
||||
inst1 = self.bitcell_array_inst
|
||||
inst1_bl_name = self.bl_names[port]+"_{}"
|
||||
inst1_br_name = self.br_names[port]+"_{}"
|
||||
inst1_bl_name = self.bl_names[port] + "_{}"
|
||||
inst1_br_name = self.br_names[port] + "_{}"
|
||||
|
||||
inst2_bl_name = inst2.mod.get_bl_names()+"_{}"
|
||||
inst2_br_name = inst2.mod.get_br_names()+"_{}"
|
||||
inst2_bl_name = inst2.mod.get_bl_names() + "_{}"
|
||||
inst2_br_name = inst2.mod.get_br_names() + "_{}"
|
||||
|
||||
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
|
||||
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name,
|
||||
inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name)
|
||||
self.connect_bitlines(inst1=inst1,
|
||||
inst2=inst2,
|
||||
num_bits=self.num_cols,
|
||||
inst1_bl_name=inst1_bl_name,
|
||||
inst1_br_name=inst1_br_name,
|
||||
inst2_bl_name=inst2_bl_name,
|
||||
inst2_br_name=inst2_br_name)
|
||||
|
||||
# Connect the replica bitlines
|
||||
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
|
||||
|
|
@ -694,20 +688,17 @@ class bank(design.design):
|
|||
self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl")
|
||||
self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br")
|
||||
|
||||
|
||||
|
||||
def route_port_data_out(self, port):
|
||||
""" Add pins for the port data out """
|
||||
|
||||
for bit in range(self.word_size):
|
||||
data_pin = self.port_data_inst[port].get_pin("dout_{0}".format(bit))
|
||||
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port,bit),
|
||||
layer=data_pin.layer,
|
||||
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port, bit),
|
||||
layer=data_pin.layer,
|
||||
offset=data_pin.center(),
|
||||
height=data_pin.height(),
|
||||
width=data_pin.width())
|
||||
|
||||
|
||||
def route_port_address_in(self, port):
|
||||
""" Routes the row decoder inputs and supplies """
|
||||
|
||||
|
|
@ -715,17 +706,15 @@ class bank(design.design):
|
|||
for row in range(self.row_addr_size):
|
||||
addr_idx = row + self.col_addr_size
|
||||
decoder_name = "addr_{}".format(row)
|
||||
addr_name = "addr{0}_{1}".format(port,addr_idx)
|
||||
addr_name = "addr{0}_{1}".format(port, addr_idx)
|
||||
self.copy_layout_pin(self.port_address_inst[port], decoder_name, addr_name)
|
||||
|
||||
|
||||
|
||||
def route_port_data_in(self, port):
|
||||
""" Connecting port data in """
|
||||
|
||||
for row in range(self.word_size):
|
||||
data_name = "din_{}".format(row)
|
||||
din_name = "din{0}_{1}".format(port,row)
|
||||
din_name = "din{0}_{1}".format(port, row)
|
||||
self.copy_layout_pin(self.port_data_inst[port], data_name, din_name)
|
||||
|
||||
if self.word_size:
|
||||
|
|
@ -733,8 +722,6 @@ class bank(design.design):
|
|||
wmask_name = "bank_wmask_{}".format(row)
|
||||
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
|
||||
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
|
||||
|
||||
|
||||
|
||||
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
|
||||
|
|
@ -752,19 +739,18 @@ class bank(design.design):
|
|||
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name)
|
||||
(top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name)
|
||||
|
||||
|
||||
# Channel route each mux separately since we don't minimize the number
|
||||
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
||||
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
|
||||
offset = bottom_inst.ul() + vector(0, self.m1_pitch)
|
||||
for bit in range(num_bits):
|
||||
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
|
||||
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
|
||||
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
|
||||
route_map = list(zip(bottom_names, top_names))
|
||||
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
|
||||
|
||||
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
|
||||
"""
|
||||
Connect two pins of two modules.
|
||||
Connect two pins of two modules.
|
||||
This assumes that they have sufficient space to create a jog
|
||||
in the middle between the two modules (if needed).
|
||||
"""
|
||||
|
|
@ -785,11 +771,13 @@ class bank(design.design):
|
|||
bottom_loc = bottom_pin.uc()
|
||||
top_loc = top_pin.bc()
|
||||
|
||||
yoffset = 0.5*(top_loc.y+bottom_loc.y)
|
||||
self.add_path(top_pin.layer,[bottom_loc, vector(bottom_loc.x,yoffset),
|
||||
vector(top_loc.x,yoffset), top_loc])
|
||||
yoffset = 0.5 * (top_loc.y + bottom_loc.y)
|
||||
self.add_path(top_pin.layer,
|
||||
[bottom_loc,
|
||||
vector(bottom_loc.x, yoffset),
|
||||
vector(top_loc.x, yoffset),
|
||||
top_loc])
|
||||
|
||||
|
||||
def connect_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bl_name, inst1_br_name,
|
||||
inst2_bl_name, inst2_br_name):
|
||||
|
|
@ -800,14 +788,13 @@ class bank(design.design):
|
|||
for col in range(num_bits):
|
||||
self.connect_bitline(inst1, inst2, inst1_bl_name.format(col), inst2_bl_name.format(col))
|
||||
self.connect_bitline(inst1, inst2, inst1_br_name.format(col), inst2_br_name.format(col))
|
||||
|
||||
|
||||
def route_port_address(self, port):
|
||||
""" Connect Wordline driver to bitcell array wordline """
|
||||
|
||||
self.route_port_address_in(port)
|
||||
|
||||
if port%2:
|
||||
if port % 2:
|
||||
self.route_port_address_right(port)
|
||||
else:
|
||||
self.route_port_address_left(port)
|
||||
|
|
@ -818,21 +805,20 @@ class bank(design.design):
|
|||
for row in range(self.num_rows):
|
||||
# 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()
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).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)
|
||||
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0.5,1)
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).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)
|
||||
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1)
|
||||
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||
|
||||
|
||||
def route_port_address_right(self, port):
|
||||
""" Connecting Wordline driver output to Bitcell WL connection """
|
||||
|
||||
for row in range(self.num_rows):
|
||||
# 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()
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).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)
|
||||
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0,1)
|
||||
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port] + "_{}".format(row)).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)
|
||||
mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
|
||||
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||
|
||||
def route_column_address_lines(self, port):
|
||||
|
|
@ -846,7 +832,7 @@ class bank(design.design):
|
|||
decode_names = ["Zb", "Z"]
|
||||
|
||||
# The Address LSB
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
||||
|
||||
elif self.col_addr_size > 1:
|
||||
decode_names = []
|
||||
|
|
@ -855,11 +841,11 @@ class bank(design.design):
|
|||
|
||||
for i in range(self.col_addr_size):
|
||||
decoder_name = "in_{}".format(i)
|
||||
addr_name = "addr{0}_{1}".format(port,i)
|
||||
addr_name = "addr{0}_{1}".format(port, i)
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||
|
||||
if port%2:
|
||||
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
|
||||
if port % 2:
|
||||
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_pitch, 0)
|
||||
else:
|
||||
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
||||
|
||||
|
|
@ -872,7 +858,8 @@ class bank(design.design):
|
|||
self.create_vertical_channel_route(route_map, offset, self.m1_stack)
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
""" This adds some points for easier debugging if LVS goes wrong.
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
|
|
@ -881,7 +868,7 @@ class bank(design.design):
|
|||
wl_name = "wl_{}".format(i)
|
||||
wl_pin = self.bitcell_array_inst.get_pin(wl_name)
|
||||
self.add_label(text=wl_name,
|
||||
layer="m1",
|
||||
layer="m1",
|
||||
offset=wl_pin.center())
|
||||
|
||||
# Add the bitline names
|
||||
|
|
@ -891,35 +878,35 @@ class bank(design.design):
|
|||
bl_pin = self.bitcell_array_inst.get_pin(bl_name)
|
||||
br_pin = self.bitcell_array_inst.get_pin(br_name)
|
||||
self.add_label(text=bl_name,
|
||||
layer="m2",
|
||||
layer="m2",
|
||||
offset=bl_pin.center())
|
||||
self.add_label(text=br_name,
|
||||
layer="m2",
|
||||
layer="m2",
|
||||
offset=br_pin.center())
|
||||
|
||||
# # Add the data output names to the sense amp output
|
||||
# # Add the data output names to the sense amp output
|
||||
# for i in range(self.word_size):
|
||||
# data_name = "data_{}".format(i)
|
||||
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
|
||||
# self.add_label(text="sa_out_{}".format(i),
|
||||
# layer="m2",
|
||||
# layer="m2",
|
||||
# offset=data_pin.center())
|
||||
|
||||
# Add labels on the decoder
|
||||
for port in self.write_ports:
|
||||
for i in range(self.word_size):
|
||||
data_name = "dec_out_{}".format(i)
|
||||
pin_name = "in_{}".format(i)
|
||||
pin_name = "in_{}".format(i)
|
||||
data_pin = self.wordline_driver_inst[port].get_pin(pin_name)
|
||||
self.add_label(text=data_name,
|
||||
layer="m1",
|
||||
layer="m1",
|
||||
offset=data_pin.center())
|
||||
|
||||
def route_control_lines(self, port):
|
||||
""" Route the control lines of the entire bank """
|
||||
|
||||
# Make a list of tuples that we will connect.
|
||||
# From control signal to the module pin
|
||||
# From control signal to the module pin
|
||||
# Connection from the central bus to the main control block crosses
|
||||
# pre-decoder and this connection is in metal3
|
||||
connection = []
|
||||
|
|
@ -944,58 +931,59 @@ class bank(design.design):
|
|||
|
||||
for (control_signal, pin_pos) in connection:
|
||||
control_mid_pos = self.bus_xoffset[port][control_signal]
|
||||
control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y)
|
||||
control_pos = vector(self.bus_xoffset[port][control_signal].x, pin_pos.y)
|
||||
self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=control_pos)
|
||||
|
||||
|
||||
# clk to wordline_driver
|
||||
control_signal = self.prefix+"wl_en{}".format(port)
|
||||
if port%2:
|
||||
control_signal = self.prefix + "wl_en{}".format(port)
|
||||
if port % 2:
|
||||
pin_pos = self.port_address_inst[port].get_pin("wl_en").uc()
|
||||
mid_pos = pin_pos + vector(0,2*self.m2_gap) # to route down to the top of the bus
|
||||
mid_pos = pin_pos + vector(0, 2 * self.m2_gap) # to route down to the top of the bus
|
||||
else:
|
||||
pin_pos = self.port_address_inst[port].get_pin("wl_en").bc()
|
||||
mid_pos = pin_pos - vector(0,2*self.m2_gap) # to route down to the top of the bus
|
||||
mid_pos = pin_pos - vector(0, 2 * self.m2_gap) # to route down to the top of the bus
|
||||
control_x_offset = self.bus_xoffset[port][control_signal].x
|
||||
control_pos = vector(control_x_offset, mid_pos.y)
|
||||
self.add_wire(self.m1_stack,[pin_pos, mid_pos, control_pos])
|
||||
self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=control_pos)
|
||||
|
||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
|
||||
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption
|
||||
# Decoder is assumed to have settled before the negative edge of the clock.
|
||||
# Delay model relies on this assumption
|
||||
stage_effort_list = []
|
||||
wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout
|
||||
stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,inp_is_rise)
|
||||
stage_effort_list += self.port_address.wordline_driver.determine_wordline_stage_efforts(wordline_cout,
|
||||
inp_is_rise)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_wl_en_cin(self):
|
||||
"""Get the relative capacitance of all the clk connections in the bank"""
|
||||
#wl_en only used in the wordline driver.
|
||||
# wl_en only used in the wordline driver.
|
||||
return self.port_address.wordline_driver.get_wl_en_cin()
|
||||
|
||||
def get_w_en_cin(self):
|
||||
"""Get the relative capacitance of all the clk connections in the bank"""
|
||||
#wl_en only used in the wordline driver.
|
||||
port = self.write_ports[0]
|
||||
# wl_en only used in the wordline driver.
|
||||
port = self.write_ports[0]
|
||||
return self.port_data[port].write_driver.get_w_en_cin()
|
||||
|
||||
def get_clk_bar_cin(self):
|
||||
"""Get the relative capacitance of all the clk_bar connections in the bank"""
|
||||
#Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array.
|
||||
# Current bank only uses clock bar (clk_buf_bar) as an enable for the precharge array.
|
||||
|
||||
#Precharges are the all the same in Mulitport, one is picked
|
||||
# Precharges are the all the same in Mulitport, one is picked
|
||||
port = self.read_ports[0]
|
||||
return self.port_data[port].precharge_array.get_en_cin()
|
||||
|
||||
def get_sen_cin(self):
|
||||
"""Get the relative capacitance of all the sense amp enable connections in the bank"""
|
||||
#Current bank only uses sen as an enable for the sense amps.
|
||||
port = self.read_ports[0]
|
||||
# Current bank only uses sen as an enable for the sense amps.
|
||||
port = self.read_ports[0]
|
||||
return self.port_data[port].sense_amp_array.get_en_cin()
|
||||
|
||||
def graph_exclude_precharge(self):
|
||||
|
|
@ -1006,5 +994,7 @@ class bank(design.design):
|
|||
|
||||
def get_cell_name(self, inst_name, row, col):
|
||||
"""Gets the spice name of the target bitcell."""
|
||||
return self.bitcell_array_inst.mod.get_cell_name(inst_name+'.x'+self.bitcell_array_inst.name, row, col)
|
||||
return self.bitcell_array_inst.mod.get_cell_name(inst_name + '.x' + self.bitcell_array_inst.name,
|
||||
row,
|
||||
col)
|
||||
|
||||
|
|
|
|||
|
|
@ -101,13 +101,20 @@ class bitcell_base_array(design.design):
|
|||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
|
||||
# For every second row and column, add a via for gnd and vdd
|
||||
# For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space
|
||||
# Default uses prefered directions for each layer; this cell property is only currently used by s8 tech (03/20)
|
||||
try:
|
||||
force_power_pins_vertical = cell_properties.bitcell_force_power_pins_vertical
|
||||
except AttributeError:
|
||||
force_power_pins_vertical = None
|
||||
|
||||
# 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(), vertical=True, start_layer=pin.layer)
|
||||
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=force_power_pins_vertical, start_layer=pin.layer)
|
||||
|
||||
def _adjust_x_offset(self, xoffset, col, col_offset):
|
||||
tempx = xoffset
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ class hierarchical_decoder(design.design):
|
|||
def __init__(self, name, rows):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.NAND_FORMAT = "DEC_NAND_{0}"
|
||||
self.INV_FORMAT = "DEC_INV_{0}"
|
||||
self.AND_FORMAT = "DEC_AND_{0}"
|
||||
|
||||
self.pre2x4_inst = []
|
||||
self.pre3x8_inst = []
|
||||
|
|
@ -58,12 +57,12 @@ class hierarchical_decoder(design.design):
|
|||
self.inv = factory.create(module_type="pinv",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.inv)
|
||||
self.nand2 = factory.create(module_type="pnand2",
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and2)
|
||||
self.and3 = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.nand2)
|
||||
self.nand3 = factory.create(module_type="pnand3",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.nand3)
|
||||
self.add_mod(self.and3)
|
||||
|
||||
self.add_decoders()
|
||||
|
||||
|
|
@ -77,27 +76,27 @@ class hierarchical_decoder(design.design):
|
|||
height=self.cell_height)
|
||||
self.add_mod(self.pre3_8)
|
||||
|
||||
def determine_predecodes(self,num_inputs):
|
||||
def determine_predecodes(self, num_inputs):
|
||||
""" Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
|
||||
needed based on the number of inputs """
|
||||
if (num_inputs == 2):
|
||||
return (1,0)
|
||||
return (1, 0)
|
||||
elif (num_inputs == 3):
|
||||
return(0,1)
|
||||
return(0, 1)
|
||||
elif (num_inputs == 4):
|
||||
return(2,0)
|
||||
return(2, 0)
|
||||
elif (num_inputs == 5):
|
||||
return(1,1)
|
||||
return(1, 1)
|
||||
elif (num_inputs == 6):
|
||||
return(3,0)
|
||||
return(3, 0)
|
||||
elif (num_inputs == 7):
|
||||
return(2,1)
|
||||
return(2, 1)
|
||||
elif (num_inputs == 8):
|
||||
return(1,2)
|
||||
return(1, 2)
|
||||
elif (num_inputs == 9):
|
||||
return(0,3)
|
||||
return(0, 3)
|
||||
else:
|
||||
debug.error("Invalid number of inputs for hierarchical decoder",-1)
|
||||
debug.error("Invalid number of inputs for hierarchical decoder", -1)
|
||||
|
||||
def setup_netlist_constants(self):
|
||||
self.predec_groups = [] # This array is a 2D array.
|
||||
|
|
@ -122,35 +121,35 @@ class hierarchical_decoder(design.design):
|
|||
index = index + 1
|
||||
self.predec_groups.append(lines)
|
||||
|
||||
|
||||
def setup_layout_constants(self):
|
||||
""" Calculate the overall dimensions of the hierarchical decoder """
|
||||
|
||||
# If we have 4 or fewer rows, the predecoder is the decoder itself
|
||||
if self.num_inputs>=4:
|
||||
self.total_number_of_predecoder_outputs = 4*self.no_of_pre2x4 + 8*self.no_of_pre3x8
|
||||
self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8
|
||||
else:
|
||||
self.total_number_of_predecoder_outputs = 0
|
||||
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),-1)
|
||||
self.total_number_of_predecoder_outputs = 0
|
||||
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),
|
||||
-1)
|
||||
|
||||
# Calculates height and width of pre-decoder,
|
||||
if self.no_of_pre3x8 > 0:
|
||||
self.predecoder_width = self.pre3_8.width
|
||||
self.predecoder_width = self.pre3_8.width
|
||||
else:
|
||||
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
|
||||
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
|
||||
|
||||
# Calculates height and width of row-decoder
|
||||
# Calculates height and width of row-decoder
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
nand_width = self.nand2.width
|
||||
nand_width = self.and2.width
|
||||
else:
|
||||
nand_width = self.nand3.width
|
||||
self.internal_routing_width = self.m2_pitch*self.total_number_of_predecoder_outputs
|
||||
nand_width = self.and3.width
|
||||
self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs
|
||||
self.row_decoder_height = self.inv.height * self.rows
|
||||
|
||||
self.input_routing_width = (self.num_inputs+1) * self.m2_pitch
|
||||
# Calculates height and width of hierarchical decoder
|
||||
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
||||
# Calculates height and width of hierarchical decoder
|
||||
self.height = self.row_decoder_height
|
||||
self.width = self.input_routing_width + self.predecoder_width \
|
||||
+ self.internal_routing_width + nand_width + self.inv.width
|
||||
|
|
@ -158,7 +157,7 @@ class hierarchical_decoder(design.design):
|
|||
def route_input_rails(self):
|
||||
""" Create input rails 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
|
||||
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
|
||||
|
||||
# Find the left-most predecoder
|
||||
min_x = 0
|
||||
|
|
@ -166,7 +165,7 @@ class hierarchical_decoder(design.design):
|
|||
min_x = min(min_x, -self.pre2_4.width)
|
||||
if self.no_of_pre3x8 > 0:
|
||||
min_x = min(min_x, -self.pre3_8.width)
|
||||
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)]
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
||||
|
|
@ -177,7 +176,6 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
self.route_input_to_predecodes()
|
||||
|
||||
|
||||
def route_input_to_predecodes(self):
|
||||
""" Route the vertical input rail to the predecoders """
|
||||
for pre_num in range(self.no_of_pre2x4):
|
||||
|
|
@ -191,11 +189,10 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||
|
||||
self.route_input_rail(decoder_offset, input_offset)
|
||||
|
||||
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
for i in range(3):
|
||||
|
|
@ -208,11 +205,10 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
# To prevent conflicts, we will offset each input connect so
|
||||
# that it aligns with the vdd/gnd rails
|
||||
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
|
||||
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
|
||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||
|
||||
self.route_input_rail(decoder_offset, input_offset)
|
||||
|
||||
self.route_input_rail(decoder_offset, input_offset)
|
||||
|
||||
def route_input_rail(self, input_offset, output_offset):
|
||||
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
|
||||
|
|
@ -222,7 +218,6 @@ class hierarchical_decoder(design.design):
|
|||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=output_offset)
|
||||
self.add_path(("m3"), [input_offset, output_offset])
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the module pins """
|
||||
|
|
@ -235,7 +230,6 @@ class hierarchical_decoder(design.design):
|
|||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
|
||||
def create_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
|
||||
|
|
@ -245,7 +239,7 @@ class hierarchical_decoder(design.design):
|
|||
for i in range(self.no_of_pre3x8):
|
||||
self.create_pre3x8(i)
|
||||
|
||||
def create_pre2x4(self,num):
|
||||
def create_pre2x4(self, num):
|
||||
""" Add a 2x4 predecoder to the left of the origin """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
|
|
@ -265,8 +259,7 @@ class hierarchical_decoder(design.design):
|
|||
mod=self.pre2_4))
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def create_pre3x8(self,num):
|
||||
def create_pre3x8(self, num):
|
||||
""" Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||
# If we had 2x4 predecodes, those are used as the lower
|
||||
# decode output bits
|
||||
|
|
@ -280,11 +273,10 @@ class hierarchical_decoder(design.design):
|
|||
pins.append("out_{0}".format(output_index + out_index_offset))
|
||||
pins.extend(["vdd", "gnd"])
|
||||
|
||||
self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num),
|
||||
self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num),
|
||||
mod=self.pre3_8))
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def place_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
|
||||
|
|
@ -294,196 +286,131 @@ class hierarchical_decoder(design.design):
|
|||
for i in range(self.no_of_pre3x8):
|
||||
self.place_pre3x8(i)
|
||||
|
||||
def place_pre2x4(self,num):
|
||||
def place_pre2x4(self, num):
|
||||
""" Place 2x4 predecoder to the left of the origin """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
base = vector(-self.pre2_4.width,0)
|
||||
base = vector(-self.pre2_4.width, 0)
|
||||
else:
|
||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
|
||||
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 """
|
||||
if (self.num_inputs == 3):
|
||||
offset = vector(-self.pre_3_8.width,0)
|
||||
mirror ="R0"
|
||||
offset = vector(-self.pre_3_8.width, 0)
|
||||
mirror = "R0"
|
||||
else:
|
||||
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
|
||||
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
|
||||
offset = vector(-self.pre3_8.width, height)
|
||||
|
||||
self.pre3x8_inst[num].place(offset)
|
||||
|
||||
def create_row_decoder(self):
|
||||
""" Create the row-decoder by placing NAND2/NAND3 and Inverters
|
||||
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
||||
and add the primary decoder output pins. """
|
||||
if (self.num_inputs >= 4):
|
||||
self.create_decoder_nand_array()
|
||||
self.create_decoder_inv_array()
|
||||
self.create_decoder_and_array()
|
||||
|
||||
def create_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
def create_decoder_and_array(self):
|
||||
""" Add a column of AND gates for final decode """
|
||||
|
||||
self.nand_inst = []
|
||||
self.and_inst = []
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs <5.
|
||||
# Row Decoder AND GATE array for address inputs <5.
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
row = len(self.predec_groups[0])*j + i
|
||||
row = len(self.predec_groups[0]) * j + i
|
||||
if (row < self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand2))
|
||||
name = self.AND_FORMAT.format(row)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and2))
|
||||
pins =["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"Z_{0}".format(row),
|
||||
"decode_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs >5.
|
||||
# Row Decoder AND GATE array for address inputs >5.
|
||||
elif (self.num_inputs > 5):
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
for k in range(len(self.predec_groups[2])):
|
||||
row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \
|
||||
+ len(self.predec_groups[0])*j + i
|
||||
row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
|
||||
+ len(self.predec_groups[0]) * j + i
|
||||
|
||||
if (row < self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand3))
|
||||
name = self.AND_FORMAT.format(row)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and3))
|
||||
|
||||
pins = ["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||
"Z_{0}".format(row),
|
||||
"decode_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
self.connect_inst(pins)
|
||||
|
||||
|
||||
def create_decoder_inv_array(self):
|
||||
"""
|
||||
Add a column of INV gates for the decoder.
|
||||
"""
|
||||
|
||||
self.inv_inst = []
|
||||
for row in range(self.rows):
|
||||
name = self.INV_FORMAT.format(row)
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.connect_inst(args=["Z_{0}".format(row),
|
||||
"decode_{0}".format(row),
|
||||
"vdd", "gnd"])
|
||||
|
||||
|
||||
def place_decoder_inv_array(self):
|
||||
"""
|
||||
Place the column of INV gates for the decoder above the predecoders
|
||||
and to the right of the NAND decoders.
|
||||
"""
|
||||
|
||||
z_pin = self.inv.get_pin("Z")
|
||||
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
x_off = self.internal_routing_width + self.nand2.width
|
||||
else:
|
||||
x_off = self.internal_routing_width + self.nand3.width
|
||||
|
||||
for row in range(self.rows):
|
||||
if (row % 2 == 0):
|
||||
inv_row_height = self.inv.height * row
|
||||
mirror = "R0"
|
||||
y_dir = 1
|
||||
else:
|
||||
inv_row_height = self.inv.height * (row + 1)
|
||||
mirror = "MX"
|
||||
y_dir = -1
|
||||
y_off = inv_row_height
|
||||
offset = vector(x_off,y_off)
|
||||
self.inv_inst[row].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def place_row_decoder(self):
|
||||
"""
|
||||
Place the row-decoder by placing NAND2/NAND3 and Inverters
|
||||
and add the primary decoder output pins.
|
||||
"""
|
||||
Place the row-decoder by placing AND2/AND3 and Inverters
|
||||
and add the primary decoder output pins.
|
||||
"""
|
||||
if (self.num_inputs >= 4):
|
||||
self.place_decoder_nand_array()
|
||||
self.place_decoder_inv_array()
|
||||
self.place_decoder_and_array()
|
||||
self.route_decoder()
|
||||
|
||||
|
||||
def place_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
def place_decoder_and_array(self):
|
||||
""" Add a column of AND gates for final decode """
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs <5.
|
||||
# Row Decoder AND GATE array for address inputs <5.
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
self.place_nand_array(nand_mod=self.nand2)
|
||||
self.place_and_array(and_mod=self.and2)
|
||||
|
||||
# Row Decoder NAND GATE array for address inputs >5.
|
||||
# Row Decoder AND GATE array for address inputs >5.
|
||||
# FIXME: why this correct offset?)
|
||||
elif (self.num_inputs > 5):
|
||||
self.place_nand_array(nand_mod=self.nand3)
|
||||
self.place_and_array(and_mod=self.and3)
|
||||
|
||||
def place_nand_array(self, nand_mod):
|
||||
""" Add a column of NAND gates for the decoder above the predecoders."""
|
||||
def place_and_array(self, and_mod):
|
||||
""" Add a column of AND gates for the decoder above the predecoders."""
|
||||
|
||||
for row in range(self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
if ((row % 2) == 0):
|
||||
y_off = nand_mod.height*row
|
||||
y_dir = 1
|
||||
y_off = and_mod.height * row
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = nand_mod.height*(row + 1)
|
||||
y_dir = -1
|
||||
y_off = and_mod.height * (row + 1)
|
||||
mirror = "MX"
|
||||
|
||||
self.nand_inst[row].place(offset=[self.internal_routing_width, y_off],
|
||||
mirror=mirror)
|
||||
|
||||
|
||||
|
||||
self.and_inst[row].place(offset=[self.internal_routing_width, y_off],
|
||||
mirror=mirror)
|
||||
|
||||
def route_decoder(self):
|
||||
""" Route the nand to inverter in the decoder and add the pins. """
|
||||
""" Add the pins. """
|
||||
|
||||
for row in range(self.rows):
|
||||
|
||||
# route nand output to output inv input
|
||||
zr_pos = self.nand_inst[row].get_pin("Z").rc()
|
||||
al_pos = self.inv_inst[row].get_pin("A").lc()
|
||||
# ensure the bend is in the middle
|
||||
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
||||
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
||||
z_pin = self.inv_inst[row].get_pin("Z")
|
||||
z_pin = self.and_inst[row].get_pin("Z")
|
||||
self.add_layout_pin(text="decode_{0}".format(row),
|
||||
layer="m1",
|
||||
offset=z_pin.ll(),
|
||||
width=z_pin.width(),
|
||||
height=z_pin.height())
|
||||
|
||||
|
||||
|
||||
def route_predecode_rails(self):
|
||||
""" Creates vertical metal 2 rails to connect predecoder and decoder stages."""
|
||||
|
||||
# This is not needed for inputs <4 since they have no pre/decode stages.
|
||||
if (self.num_inputs >= 4):
|
||||
input_offset = vector(0.5*self.m2_width,0)
|
||||
input_offset = vector(0.5 * self.m2_width, 0)
|
||||
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||
self.predecode_rails = self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=self.height)
|
||||
|
||||
|
||||
self.route_rails_to_predecodes()
|
||||
self.route_rails_to_decoder()
|
||||
|
|
@ -497,8 +424,7 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
||||
out_name = "out_{}".format(i)
|
||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
# FIXME: convert to connect_bus
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
|
|
@ -506,17 +432,15 @@ class hierarchical_decoder(design.design):
|
|||
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
||||
out_name = "out_{}".format(i)
|
||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
|
||||
|
||||
def route_rails_to_decoder(self):
|
||||
""" Use the self.predec_groups to determine the connections to the decoder NAND gates.
|
||||
Inputs of NAND2/NAND3 gates come from different groups.
|
||||
""" Use the self.predec_groups to determine the connections to the decoder AND gates.
|
||||
Inputs of AND2/AND3 gates come from different groups.
|
||||
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
|
||||
[8,9,10,11,12,13,14,15] ] the first NAND3 inputs are connected to
|
||||
[0,4,8] and second NAND3 is connected to [0,4,9] ........... and the
|
||||
128th NAND3 is connected to [3,7,15]
|
||||
[8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
|
||||
[0,4,8] and second AND3 is connected to [0,4,9] ........... and the
|
||||
128th AND3 is connected to [3,7,15]
|
||||
"""
|
||||
row_index = 0
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
|
|
@ -525,9 +449,9 @@ class hierarchical_decoder(design.design):
|
|||
# FIXME: convert to connect_bus?
|
||||
if (row_index < self.rows):
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
|
||||
row_index = row_index + 1
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
|
|
@ -536,61 +460,48 @@ class hierarchical_decoder(design.design):
|
|||
for index_A in self.predec_groups[0]:
|
||||
# FIXME: convert to connect_bus?
|
||||
if (row_index < self.rows):
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C"))
|
||||
row_index = row_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
|
||||
# The vias will be placed in the center and right of the cells, respectively.
|
||||
xoffset = self.nand_inst[0].rx()
|
||||
for num in range(0,self.rows):
|
||||
xoffset = self.and_inst[0].rx()
|
||||
for num in range(0, self.rows):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# The nand and inv are the same height rows...
|
||||
supply_pin = self.nand_inst[num].get_pin(pin_name)
|
||||
supply_pin = self.and_inst[num].get_pin(pin_name)
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin_pos)
|
||||
|
||||
# Make a redundant rail too
|
||||
for num in range(0,self.rows,2):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
start = self.nand_inst[num].get_pin(pin_name).lc()
|
||||
end = self.inv_inst[num].get_pin(pin_name).rc()
|
||||
mid = (start+end).scale(0.5,0.5)
|
||||
self.add_rect_center(layer="m1",
|
||||
offset=mid,
|
||||
width=end.x-start.x)
|
||||
|
||||
|
||||
# Copy the pins from the predecoders
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(pre, "vdd")
|
||||
self.copy_layout_pin(pre, "gnd")
|
||||
|
||||
|
||||
def route_predecode_rail(self, rail_name, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y)
|
||||
self.add_path("m1", [rail_pos, pin.lc()])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
def route_predecode_rail_m3(self, rail_name, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||
# It would be fixed with a channel router.
|
||||
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y)
|
||||
mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2)
|
||||
rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pin.center())
|
||||
self.add_wire(("m3","via2","m2"), [rail_pos, mid_point, pin.uc()])
|
||||
self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=rail_pos)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,61 +32,58 @@ class hierarchical_predecode(design.design):
|
|||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the INV and NAND 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_nand(self.number_of_inputs)
|
||||
self.add_mod(self.nand)
|
||||
self.add_and(self.number_of_inputs)
|
||||
self.add_mod(self.and_mod)
|
||||
|
||||
def add_nand(self, inputs):
|
||||
def add_and(self, inputs):
|
||||
""" Create the NAND for the predecode input stage """
|
||||
if inputs==2:
|
||||
self.nand = factory.create(module_type="pnand2",
|
||||
height=self.cell_height)
|
||||
self.and_mod = factory.create(module_type="pand2",
|
||||
height=self.cell_height)
|
||||
elif inputs==3:
|
||||
self.nand = factory.create(module_type="pnand3",
|
||||
height=self.cell_height)
|
||||
self.and_mod = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
else:
|
||||
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
|
||||
|
||||
def setup_layout_constraints(self):
|
||||
|
||||
self.height = self.number_of_outputs * self.nand.height
|
||||
self.height = self.number_of_outputs * self.and_mod.height
|
||||
|
||||
# x offset for input inverters
|
||||
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
|
||||
|
||||
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
||||
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
|
||||
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
||||
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
|
||||
|
||||
# x offset to output inverters
|
||||
self.x_off_inv_2 = self.x_off_nand + self.nand.width
|
||||
|
||||
# Height width are computed
|
||||
self.width = self.x_off_inv_2 + self.inv.width
|
||||
self.width = self.x_off_and + self.and_mod.width
|
||||
|
||||
def route_rails(self):
|
||||
""" 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)]
|
||||
offset = vector(0.5*self.m2_width,2*self.m1_width)
|
||||
offset = vector(0.5 * self.m2_width, self.m1_pitch)
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=offset,
|
||||
names=input_names,
|
||||
length=self.height - 2*self.m1_width)
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
|
||||
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)]
|
||||
decode_names = invert_names + non_invert_names
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 2*self.m1_width)
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m1_pitch)
|
||||
self.decode_rails = self.create_vertical_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=offset,
|
||||
names=decode_names,
|
||||
length=self.height - 2*self.m1_width)
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
|
||||
def create_input_inverters(self):
|
||||
""" Create the input inverters to invert input signals for the decode stage. """
|
||||
|
|
@ -112,62 +109,35 @@ class hierarchical_predecode(design.design):
|
|||
self.in_inst[inv_num].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def create_output_inverters(self):
|
||||
""" Create inverters for the inverted output decode signals. """
|
||||
self.inv_inst = []
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
name = "pre_nand_inv_{}".format(inv_num)
|
||||
self.inv_inst.append(self.add_inst(name=name,
|
||||
mod=self.inv))
|
||||
self.connect_inst(["Z_{}".format(inv_num),
|
||||
"out_{}".format(inv_num),
|
||||
"vdd", "gnd"])
|
||||
def create_and_array(self, connections):
|
||||
""" Create the AND stage for the decodes """
|
||||
self.and_inst = []
|
||||
for and_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
|
||||
name = "Xpre{0}_and_{1}".format(inout, and_input)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and_mod))
|
||||
self.connect_inst(connections[and_input])
|
||||
|
||||
|
||||
def place_output_inverters(self):
|
||||
""" Place inverters for the inverted output decode signals. """
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
if (inv_num % 2 == 0):
|
||||
y_off = inv_num * self.inv.height
|
||||
def place_and_array(self):
|
||||
""" Place the AND stage for the decodes """
|
||||
for and_input in range(self.number_of_outputs):
|
||||
# inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
|
||||
if (and_input % 2 == 0):
|
||||
y_off = and_input * self.and_mod.height
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off =(inv_num + 1)*self.inv.height
|
||||
y_off = (and_input + 1) * self.and_mod.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_inv_2, y_off)
|
||||
self.inv_inst[inv_num].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def create_nand_array(self,connections):
|
||||
""" Create the NAND stage for the decodes """
|
||||
self.nand_inst = []
|
||||
for nand_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
|
||||
name = "Xpre{0}_nand_{1}".format(inout,nand_input)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand))
|
||||
self.connect_inst(connections[nand_input])
|
||||
|
||||
|
||||
def place_nand_array(self):
|
||||
""" Place the NAND stage for the decodes """
|
||||
for nand_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
|
||||
if (nand_input % 2 == 0):
|
||||
y_off = nand_input * self.inv.height
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = (nand_input + 1) * self.inv.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_nand, y_off)
|
||||
self.nand_inst[nand_input].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
offset = vector(self.x_off_and, y_off)
|
||||
self.and_inst[and_input].place(offset=offset,
|
||||
mirror=mirror)
|
||||
|
||||
def route(self):
|
||||
self.route_input_inverters()
|
||||
self.route_inputs_to_rails()
|
||||
self.route_nand_to_rails()
|
||||
self.route_output_inverters()
|
||||
self.route_and_to_rails()
|
||||
self.route_output_and()
|
||||
self.route_vdd_gnd()
|
||||
|
||||
def route_inputs_to_rails(self):
|
||||
|
|
@ -175,39 +145,30 @@ class hierarchical_predecode(design.design):
|
|||
for num in range(self.number_of_inputs):
|
||||
# route one signal next to each vdd/gnd rail since this is
|
||||
# typically where the p/n devices are and there are no
|
||||
# pins in the nand gates.
|
||||
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
|
||||
in_pin = "in_{}".format(num)
|
||||
# pins in the and gates.
|
||||
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
|
||||
in_pin = "in_{}".format(num)
|
||||
a_pin = "A_{}".format(num)
|
||||
in_pos = vector(self.input_rails[in_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_via_center(layers = self.m1_stack,
|
||||
in_pos = vector(self.input_rails[in_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_via_center(layers=self.m1_stack,
|
||||
offset=[self.input_rails[in_pin].x, y_offset])
|
||||
self.add_via_center(layers = self.m1_stack,
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=[self.decode_rails[a_pin].x, y_offset])
|
||||
|
||||
def route_output_inverters(self):
|
||||
def route_output_and(self):
|
||||
"""
|
||||
Route all conections of the outputs inverters
|
||||
Route all conections of the outputs and gates
|
||||
"""
|
||||
for num in range(self.number_of_outputs):
|
||||
|
||||
# route nand output to output inv input
|
||||
zr_pos = self.nand_inst[num].get_pin("Z").rc()
|
||||
al_pos = self.inv_inst[num].get_pin("A").lc()
|
||||
# ensure the bend is in the middle
|
||||
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
||||
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
||||
z_pin = self.inv_inst[num].get_pin("Z")
|
||||
z_pin = self.and_inst[num].get_pin("Z")
|
||||
self.add_layout_pin(text="out_{}".format(num),
|
||||
layer="m1",
|
||||
offset=z_pin.ll(),
|
||||
height=z_pin.height(),
|
||||
width=z_pin.width())
|
||||
|
||||
|
||||
def route_input_inverters(self):
|
||||
"""
|
||||
|
|
@ -219,7 +180,7 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
#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
|
||||
# pins in the nand gates.
|
||||
# pins in the and gates.
|
||||
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
|
||||
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
|
||||
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
|
||||
|
|
@ -236,13 +197,12 @@ class hierarchical_predecode(design.design):
|
|||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=in_pos)
|
||||
|
||||
|
||||
def route_nand_to_rails(self):
|
||||
# This 2D array defines the connection mapping
|
||||
nand_input_line_combination = self.get_nand_input_line_combination()
|
||||
def route_and_to_rails(self):
|
||||
# This 2D array defines the connection mapping
|
||||
and_input_line_combination = self.get_and_input_line_combination()
|
||||
for k in range(self.number_of_outputs):
|
||||
# create x offset list
|
||||
index_lst= nand_input_line_combination[k]
|
||||
# create x offset list
|
||||
index_lst= and_input_line_combination[k]
|
||||
|
||||
if self.number_of_inputs == 2:
|
||||
gate_lst = ["A","B"]
|
||||
|
|
@ -251,35 +211,32 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
# this will connect pins A,B or A,B,C
|
||||
for rail_pin,gate_pin in zip(index_lst,gate_lst):
|
||||
pin_pos = self.nand_inst[k].get_pin(gate_pin).lc()
|
||||
pin_pos = self.and_inst[k].get_pin(gate_pin).lc()
|
||||
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
||||
self.add_path("m1", [rail_pos, pin_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" 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_xoffset = self.in_inst[0].rx() + self.m1_space
|
||||
out_xoffset = self.inv_inst[0].lx() - self.m1_space
|
||||
for num in range(0,self.number_of_outputs):
|
||||
# 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"]:
|
||||
nand_pin = self.nand_inst[num].get_pin(n)
|
||||
supply_offset = nand_pin.ll().scale(0,1)
|
||||
and_pin = self.and_inst[num].get_pin(n)
|
||||
supply_offset = and_pin.ll().scale(0, 1)
|
||||
self.add_rect(layer="m1",
|
||||
offset=supply_offset,
|
||||
width=self.inv_inst[num].rx())
|
||||
width=self.and_inst[num].rx())
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [in_xoffset, out_xoffset]:
|
||||
pin_pos = vector(xoffset, nand_pin.cy())
|
||||
for xoffset in [in_xoffset]:
|
||||
pin_pos = vector(xoffset, and_pin.cy())
|
||||
self.add_power_pin(n, pin_pos)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,33 +27,31 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "Z_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "Z_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "Z_3", "vdd", "gnd"]]
|
||||
self.create_nand_array(connections)
|
||||
connections =[["inbar_0", "inbar_1", "out_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "out_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "out_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "out_3", "vdd", "gnd"]]
|
||||
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
|
||||
4) a set of AND gates for inversion
|
||||
"""
|
||||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
self.place_nand_array()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
""" These are the decoder connections of the NAND gates to the A,B pins """
|
||||
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"],
|
||||
["A_0", "Abar_1"],
|
||||
["Abar_0", "A_1"],
|
||||
["A_0", "A_1"]]
|
||||
return combination
|
||||
return combination
|
||||
|
|
|
|||
|
|
@ -20,26 +20,25 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
hierarchical_predecode.__init__(self, name, 3, height)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "inbar_2", "Z_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "inbar_2", "Z_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "inbar_2", "Z_3", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "in_2", "Z_4", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "in_2", "Z_5", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "in_2", "Z_6", "vdd", "gnd"],
|
||||
["in_0", "in_1", "in_2", "Z_7", "vdd", "gnd"]]
|
||||
self.create_nand_array(connections)
|
||||
connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
|
||||
["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
|
||||
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
|
||||
|
|
@ -49,20 +48,19 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
self.place_nand_array()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_nand_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 """
|
||||
combination = [["Abar_0", "Abar_1", "Abar_2"],
|
||||
["A_0", "Abar_1", "Abar_2"],
|
||||
["Abar_0", "A_1", "Abar_2"],
|
||||
["A_0", "A_1", "Abar_2"],
|
||||
["Abar_0", "Abar_1", "A_2"],
|
||||
["Abar_0", "Abar_1", "A_2"],
|
||||
["A_0", "Abar_1", "A_2"],
|
||||
["Abar_0", "A_1", "A_2"],
|
||||
["A_0", "A_1", "A_2"]]
|
||||
return combination
|
||||
return combination
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
# 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.
|
||||
#
|
||||
import sys
|
||||
from tech import drc, parameter
|
||||
from tech import drc
|
||||
import debug
|
||||
import design
|
||||
from sram_factory import factory
|
||||
from collections import namedtuple
|
||||
from vector import vector
|
||||
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class port_data(design.design):
|
||||
"""
|
||||
Create the data port (column mux, sense amps, write driver, etc.) for the given port number.
|
||||
|
|
@ -23,18 +23,21 @@ class port_data(design.design):
|
|||
sram_config.set_local_config(self)
|
||||
self.port = port
|
||||
if self.write_size is not None:
|
||||
self.num_wmasks = int(self.word_size/self.write_size)
|
||||
self.num_wmasks = int(self.word_size / self.write_size)
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
if name == "":
|
||||
name = "port_data_{0}".format(self.port)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create data port of size {0} with {1} words per row".format(self.word_size,self.words_per_row))
|
||||
debug.info(2,
|
||||
"create data port of size {0} with {1} words per row".format(self.word_size,
|
||||
self.words_per_row))
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
|
||||
debug.check(len(self.all_ports)<=2,
|
||||
"Bank layout cannot handle more than two ports.")
|
||||
self.create_layout()
|
||||
self.add_boundary()
|
||||
|
||||
|
|
@ -46,7 +49,19 @@ class port_data(design.design):
|
|||
# br lines are connect from the precharger
|
||||
return self.precharge.get_br_names()
|
||||
|
||||
def get_bl_name(self, port=0):
|
||||
bl_name = "bl"
|
||||
if len(self.all_ports) == 1:
|
||||
return bl_name
|
||||
else:
|
||||
return bl_name + "{}".format(port)
|
||||
|
||||
def get_br_name(self, port=0):
|
||||
br_name = "br"
|
||||
if len(self.all_ports) == 1:
|
||||
return br_name
|
||||
else:
|
||||
return br_name + "{}".format(port)
|
||||
|
||||
def create_netlist(self):
|
||||
self.precompute_constants()
|
||||
|
|
@ -80,8 +95,6 @@ class port_data(design.design):
|
|||
else:
|
||||
self.column_mux_array_inst = None
|
||||
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
self.compute_instance_offsets()
|
||||
self.place_instances()
|
||||
|
|
@ -91,33 +104,32 @@ class port_data(design.design):
|
|||
def add_pins(self):
|
||||
""" Adding pins for port address module"""
|
||||
|
||||
self.add_pin("rbl_bl","INOUT")
|
||||
self.add_pin("rbl_br","INOUT")
|
||||
self.add_pin("rbl_bl", "INOUT")
|
||||
self.add_pin("rbl_br", "INOUT")
|
||||
for bit in range(self.num_cols):
|
||||
bl_name = self.precharge_array.get_bl_name(self.port)
|
||||
br_name = self.precharge_array.get_br_name(self.port)
|
||||
self.add_pin("{0}_{1}".format(bl_name, bit),"INOUT")
|
||||
self.add_pin("{0}_{1}".format(br_name, bit),"INOUT")
|
||||
bl_name = self.get_bl_name(self.port)
|
||||
br_name = self.get_br_name(self.port)
|
||||
self.add_pin("{0}_{1}".format(bl_name, bit), "INOUT")
|
||||
self.add_pin("{0}_{1}".format(br_name, bit), "INOUT")
|
||||
if self.port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("dout_{}".format(bit),"OUTPUT")
|
||||
self.add_pin("dout_{}".format(bit), "OUTPUT")
|
||||
if self.port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("din_{}".format(bit),"INPUT")
|
||||
self.add_pin("din_{}".format(bit), "INPUT")
|
||||
# Will be empty if no col addr lines
|
||||
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
||||
for pin_name in sel_names:
|
||||
self.add_pin(pin_name,"INPUT")
|
||||
self.add_pin(pin_name, "INPUT")
|
||||
if self.port in self.read_ports:
|
||||
self.add_pin("s_en", "INPUT")
|
||||
self.add_pin("p_en_bar", "INPUT")
|
||||
if self.port in self.write_ports:
|
||||
self.add_pin("w_en", "INPUT")
|
||||
for bit in range(self.num_wmasks):
|
||||
self.add_pin("bank_wmask_{}".format(bit),"INPUT")
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.add_pin("bank_wmask_{}".format(bit), "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def route_layout(self):
|
||||
""" Create routing among the modules """
|
||||
|
|
@ -159,8 +171,8 @@ class port_data(design.design):
|
|||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
|
||||
for inst in self.insts:
|
||||
self.copy_power_pins(inst,"vdd")
|
||||
self.copy_power_pins(inst,"gnd")
|
||||
self.copy_power_pins(inst, "vdd")
|
||||
self.copy_power_pins(inst, "gnd")
|
||||
|
||||
def add_modules(self):
|
||||
|
||||
|
|
@ -180,7 +192,6 @@ class port_data(design.design):
|
|||
else:
|
||||
self.sense_amp_array = None
|
||||
|
||||
|
||||
if self.col_addr_size > 0:
|
||||
self.column_mux_array = factory.create(module_type="column_mux_array",
|
||||
columns=self.num_cols,
|
||||
|
|
@ -191,7 +202,6 @@ class port_data(design.design):
|
|||
else:
|
||||
self.column_mux_array = None
|
||||
|
||||
|
||||
if self.port in self.write_ports:
|
||||
self.write_driver_array = factory.create(module_type="write_driver_array",
|
||||
columns=self.num_cols,
|
||||
|
|
@ -221,29 +231,27 @@ class port_data(design.design):
|
|||
else:
|
||||
self.num_col_addr_lines = 0
|
||||
|
||||
|
||||
# A space for wells or jogging m2 between modules
|
||||
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3*self.m2_pitch)
|
||||
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3 * self.m2_pitch)
|
||||
|
||||
|
||||
# create arrays of bitline and bitline_bar names for read, write, or all ports
|
||||
# create arrays of bitline and bitline_bar names for read,
|
||||
# write, or all ports
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.bl_names = self.bitcell.get_all_bl_names()
|
||||
self.br_names = self.bitcell.get_all_br_names()
|
||||
self.wl_names = self.bitcell.get_all_wl_names()
|
||||
# used for bl/br names
|
||||
self.precharge = factory.create(module_type="precharge",
|
||||
bitcell_bl = self.bl_names[0],
|
||||
bitcell_br = self.br_names[0])
|
||||
bitcell_bl=self.bl_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 + 1,
|
||||
bitcell_bl=self.bl_names[self.port],
|
||||
bitcell_br=self.br_names[self.port])
|
||||
|
||||
|
||||
columns=self.num_cols + 1,
|
||||
bitcell_bl=self.bl_names[self.port],
|
||||
bitcell_br=self.br_names[self.port])
|
||||
|
||||
def create_precharge_array(self):
|
||||
""" Creating Precharge """
|
||||
|
|
@ -253,8 +261,8 @@ class port_data(design.design):
|
|||
|
||||
self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port),
|
||||
mod=self.precharge_array)
|
||||
bl_name = self.precharge_array.get_bl_name(self.port)
|
||||
br_name = self.precharge_array.get_br_name(self.port)
|
||||
bl_name = self.get_bl_name(self.port)
|
||||
br_name = self.get_br_name(self.port)
|
||||
|
||||
temp = []
|
||||
# Use left BLs for RBL
|
||||
|
|
@ -272,21 +280,19 @@ class port_data(design.design):
|
|||
temp.extend(["p_en_bar", "vdd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_precharge_array(self, offset):
|
||||
""" Placing Precharge """
|
||||
|
||||
self.precharge_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_column_mux_array(self):
|
||||
""" Creating Column Mux when words_per_row > 1 . """
|
||||
|
||||
self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port),
|
||||
mod=self.column_mux_array)
|
||||
|
||||
bl_name = self.column_mux_array.get_bl_name(self.port)
|
||||
br_name = self.column_mux_array.get_br_name(self.port)
|
||||
bl_name = self.get_bl_name(self.port)
|
||||
br_name = self.get_br_name(self.port)
|
||||
temp = []
|
||||
for col in range(self.num_cols):
|
||||
temp.append("{0}_{1}".format(bl_name, col))
|
||||
|
|
@ -301,7 +307,6 @@ class port_data(design.design):
|
|||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_column_mux_array(self, offset):
|
||||
""" Placing Column Mux when words_per_row > 1 . """
|
||||
if self.col_addr_size == 0:
|
||||
|
|
@ -309,14 +314,13 @@ class port_data(design.design):
|
|||
|
||||
self.column_mux_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_sense_amp_array(self):
|
||||
""" Creating Sense amp """
|
||||
self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port),
|
||||
mod=self.sense_amp_array)
|
||||
|
||||
bl_name = self.sense_amp_array.get_bl_name(self.port)
|
||||
br_name = self.sense_amp_array.get_br_name(self.port)
|
||||
bl_name = self.get_bl_name(self.port)
|
||||
br_name = self.get_br_name(self.port)
|
||||
temp = []
|
||||
for bit in range(self.word_size):
|
||||
temp.append("dout_{}".format(bit))
|
||||
|
|
@ -327,22 +331,19 @@ class port_data(design.design):
|
|||
temp.append("{0}_out_{1}".format(bl_name, bit))
|
||||
temp.append("{0}_out_{1}".format(br_name, bit))
|
||||
|
||||
|
||||
temp.extend(["s_en", "vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_sense_amp_array(self, offset):
|
||||
""" Placing Sense amp """
|
||||
self.sense_amp_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_write_driver_array(self):
|
||||
""" Creating Write Driver """
|
||||
self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port),
|
||||
mod=self.write_driver_array)
|
||||
bl_name = self.write_driver_array.get_bl_name(self.port)
|
||||
br_name = self.write_driver_array.get_br_name(self.port)
|
||||
bl_name = self.get_bl_name(self.port)
|
||||
br_name = self.get_br_name(self.port)
|
||||
|
||||
temp = []
|
||||
for bit in range(self.word_size):
|
||||
|
|
@ -365,12 +366,10 @@ class port_data(design.design):
|
|||
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_write_driver_array(self, offset):
|
||||
""" Placing Write Driver """
|
||||
self.write_driver_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_write_mask_and_array(self):
|
||||
""" Creating Write Mask AND Array """
|
||||
self.write_mask_and_array_inst = self.add_inst(name="write_mask_and_array{}".format(self.port),
|
||||
|
|
@ -385,12 +384,10 @@ class port_data(design.design):
|
|||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_write_mask_and_array(self, offset):
|
||||
""" Placing Write Mask AND array """
|
||||
self.write_mask_and_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def compute_instance_offsets(self):
|
||||
"""
|
||||
Compute the empty instance offsets for port0 and port1 (if needed)
|
||||
|
|
@ -445,19 +442,17 @@ class port_data(design.design):
|
|||
if self.column_mux_offset:
|
||||
self.place_column_mux_array(self.column_mux_offset)
|
||||
|
||||
|
||||
def route_sense_amp_out(self, port):
|
||||
""" Add pins for the sense amp output """
|
||||
|
||||
for bit in range(self.word_size):
|
||||
data_pin = self.sense_amp_array_inst.get_pin("data_{}".format(bit))
|
||||
self.add_layout_pin_rect_center(text="dout_{0}".format(bit),
|
||||
layer=data_pin.layer,
|
||||
layer=data_pin.layer,
|
||||
offset=data_pin.center(),
|
||||
height=data_pin.height(),
|
||||
width=data_pin.width())
|
||||
|
||||
|
||||
def route_write_driver_in(self, port):
|
||||
""" Connecting write driver """
|
||||
|
||||
|
|
@ -466,7 +461,6 @@ class port_data(design.design):
|
|||
din_name = "din_{}".format(row)
|
||||
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
|
||||
|
||||
|
||||
def route_write_mask_and_array_in(self, port):
|
||||
""" Add pins for the write mask and array input """
|
||||
|
||||
|
|
@ -475,7 +469,6 @@ class port_data(design.design):
|
|||
bank_wmask_name = "bank_wmask_{}".format(bit)
|
||||
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):
|
||||
""" 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 """
|
||||
|
|
@ -498,15 +491,15 @@ class port_data(design.design):
|
|||
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.")
|
||||
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
|
||||
|
||||
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())
|
||||
middle_pos = vector(length, wmask_out_pin.cy())
|
||||
end_pos = vector(length, wdriver_en_pin.cy())
|
||||
|
||||
# Add via for the write driver array's enable input
|
||||
|
|
@ -516,21 +509,22 @@ class port_data(design.design):
|
|||
# Route between write mask AND array and write driver array
|
||||
self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos])
|
||||
|
||||
|
||||
def route_column_mux_to_precharge_array(self, port):
|
||||
""" Routing of BL and BR between col mux and precharge array """
|
||||
|
||||
# Only do this if we have a column mux!
|
||||
if self.col_addr_size==0:
|
||||
return
|
||||
|
||||
|
||||
inst1 = self.column_mux_array_inst
|
||||
inst2 = self.precharge_array_inst
|
||||
if self.port==0:
|
||||
self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1)
|
||||
else:
|
||||
self.connect_bitlines(inst1, inst2, self.num_cols)
|
||||
|
||||
insn2_start_bit = 1 if self.port == 0 else 0
|
||||
|
||||
self.connect_bitlines(inst1=inst1,
|
||||
inst2=inst2,
|
||||
num_bits=self.num_cols,
|
||||
inst2_start_bit=insn2_start_bit)
|
||||
|
||||
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
|
||||
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||
|
|
@ -539,47 +533,46 @@ class port_data(design.design):
|
|||
if self.col_addr_size>0:
|
||||
# Sense amp is connected to the col mux
|
||||
inst1 = self.column_mux_array_inst
|
||||
inst1_bl_name = "bl_out_{}"
|
||||
inst1_br_name = "br_out_{}"
|
||||
inst1_bls_templ = "{inst}_out_{bit}"
|
||||
start_bit = 0
|
||||
else:
|
||||
# Sense amp is directly connected to the precharge array
|
||||
inst1 = self.precharge_array_inst
|
||||
inst1_bl_name = "bl_{}"
|
||||
inst1_br_name = "br_{}"
|
||||
inst1_bls_templ="{inst}_{bit}"
|
||||
|
||||
if self.port==0:
|
||||
start_bit=1
|
||||
else:
|
||||
start_bit=0
|
||||
|
||||
|
||||
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
|
||||
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
|
||||
|
||||
self.channel_route_bitlines(inst1=inst1,
|
||||
inst1_bls_template=inst1_bls_templ,
|
||||
inst2=inst2,
|
||||
num_bits=self.word_size,
|
||||
inst1_start_bit=start_bit)
|
||||
|
||||
def route_write_driver_to_column_mux_or_precharge_array(self, port):
|
||||
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||
inst2 = self.write_driver_array_inst
|
||||
|
||||
|
||||
if self.col_addr_size>0:
|
||||
# Write driver is connected to the col mux
|
||||
inst1 = self.column_mux_array_inst
|
||||
inst1_bl_name = "bl_out_{}"
|
||||
inst1_br_name = "br_out_{}"
|
||||
inst1_bls_templ = "{inst}_out_{bit}"
|
||||
start_bit = 0
|
||||
else:
|
||||
# Sense amp is directly connected to the precharge array
|
||||
inst1 = self.precharge_array_inst
|
||||
inst1_bl_name = "bl_{}"
|
||||
inst1_br_name = "br_{}"
|
||||
inst1_bls_templ="{inst}_{bit}"
|
||||
if self.port==0:
|
||||
start_bit=1
|
||||
else:
|
||||
start_bit=0
|
||||
|
||||
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
|
||||
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
|
||||
|
||||
self.channel_route_bitlines(inst1=inst1, inst2=inst2,
|
||||
num_bits=self.word_size,
|
||||
inst1_bls_template=inst1_bls_templ,
|
||||
inst1_start_bit=start_bit)
|
||||
|
||||
def route_write_driver_to_sense_amp(self, port):
|
||||
""" Routing of BL and BR between write driver and sense amp """
|
||||
|
|
@ -589,8 +582,9 @@ class port_data(design.design):
|
|||
|
||||
# These should be pitch matched in the cell library,
|
||||
# but just in case, do a channel route.
|
||||
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size)
|
||||
|
||||
self.channel_route_bitlines(inst1=inst1,
|
||||
inst2=inst2,
|
||||
num_bits=self.word_size)
|
||||
|
||||
def route_bitline_pins(self):
|
||||
""" Add the bitline pins for the given port """
|
||||
|
|
@ -609,12 +603,15 @@ class port_data(design.design):
|
|||
|
||||
for bit in range(self.num_cols):
|
||||
if self.precharge_array_inst:
|
||||
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+bit_offset), "bl_{}".format(bit))
|
||||
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit))
|
||||
self.copy_layout_pin(self.precharge_array_inst,
|
||||
"bl_{}".format(bit + bit_offset),
|
||||
"bl_{}".format(bit))
|
||||
self.copy_layout_pin(self.precharge_array_inst,
|
||||
"br_{}".format(bit + bit_offset),
|
||||
"br_{}".format(bit))
|
||||
else:
|
||||
debug.error("Didn't find precharge array.")
|
||||
|
||||
|
||||
def route_control_pins(self):
|
||||
""" Add the control pins: s_en, p_en_bar, w_en """
|
||||
if self.precharge_array_inst:
|
||||
|
|
@ -635,65 +632,107 @@ class port_data(design.design):
|
|||
if self.write_mask_and_array_inst:
|
||||
self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en")
|
||||
|
||||
|
||||
|
||||
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
|
||||
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
|
||||
def _group_bitline_instances(self, inst1, inst2, num_bits,
|
||||
inst1_bls_template,
|
||||
inst1_start_bit,
|
||||
inst2_bls_template,
|
||||
inst2_start_bit):
|
||||
"""
|
||||
Route the bl and br of two modules using the channel router.
|
||||
Groups all the parameters into a named tuple and seperates them into
|
||||
top and bottom instances.
|
||||
"""
|
||||
|
||||
inst_group = namedtuple('InstanceGroup', ('inst', 'bls_template',
|
||||
'bl_name', 'br_name', 'start_bit'))
|
||||
|
||||
inst1_group = inst_group(inst1, inst1_bls_template,
|
||||
inst1.mod.get_bl_name(),
|
||||
inst1.mod.get_br_name(),
|
||||
inst1_start_bit)
|
||||
inst2_group = inst_group(inst2, inst2_bls_template,
|
||||
inst2.mod.get_bl_name(),
|
||||
inst2.mod.get_br_name(),
|
||||
inst2_start_bit)
|
||||
# determine top and bottom automatically.
|
||||
# since they don't overlap, we can just check the bottom y coordinate.
|
||||
if inst1.by() < inst2.by():
|
||||
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
|
||||
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
|
||||
bot_inst_group = inst1_group
|
||||
top_inst_group = inst2_group
|
||||
else:
|
||||
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
|
||||
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
|
||||
bot_inst_group = inst2_group
|
||||
top_inst_group = inst1_group
|
||||
|
||||
return (bot_inst_group, top_inst_group)
|
||||
|
||||
def _get_bitline_pins(self, inst_group, bit):
|
||||
"""
|
||||
Extracts bl/br pins from an InstanceGroup based on the bit modifier.
|
||||
"""
|
||||
full_bl_name = inst_group.bls_template.format(
|
||||
**{'inst': inst_group.bl_name,
|
||||
'bit': inst_group.start_bit + bit}
|
||||
)
|
||||
full_br_name = inst_group.bls_template.format(
|
||||
**{'inst': inst_group.br_name,
|
||||
'bit': inst_group.start_bit + bit}
|
||||
)
|
||||
return (inst_group.inst.get_pin(full_bl_name),
|
||||
inst_group.inst.get_pin(full_br_name))
|
||||
|
||||
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bls_template="{inst}_{bit}",
|
||||
inst1_start_bit=0,
|
||||
inst2_bls_template="{inst}_{bit}",
|
||||
inst2_start_bit=0):
|
||||
"""
|
||||
Route the bl and br of two modules using the channel router.
|
||||
"""
|
||||
|
||||
bot_inst_group, top_inst_group = self._group_bitline_instances(
|
||||
inst1, inst2, num_bits,
|
||||
inst1_bls_template, inst1_start_bit,
|
||||
inst2_bls_template, inst2_start_bit)
|
||||
|
||||
# Channel route each mux separately since we don't minimize the number
|
||||
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
||||
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
|
||||
offset = bot_inst_group.inst.ul() + vector(0, self.m1_pitch)
|
||||
for bit in range(num_bits):
|
||||
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))]
|
||||
top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))]
|
||||
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
|
||||
top_names = self._get_bitline_pins(top_inst_group, bit)
|
||||
|
||||
route_map = list(zip(bottom_names, top_names))
|
||||
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
|
||||
|
||||
|
||||
def connect_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
|
||||
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
|
||||
|
||||
inst1_bls_template="{inst}_{bit}",
|
||||
inst1_start_bit=0,
|
||||
inst2_bls_template="{inst}_{bit}",
|
||||
inst2_start_bit=0):
|
||||
"""
|
||||
Connect the bl and br of two modules.
|
||||
This assumes that they have sufficient space to create a jog
|
||||
in the middle between the two modules (if needed).
|
||||
"""
|
||||
|
||||
# determine top and bottom automatically.
|
||||
# since they don't overlap, we can just check the bottom y coordinate.
|
||||
if inst1.by() < inst2.by():
|
||||
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
|
||||
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
|
||||
else:
|
||||
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
|
||||
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
|
||||
bot_inst_group, top_inst_group = self._group_bitline_instances(
|
||||
inst1, inst2, num_bits,
|
||||
inst1_bls_template, inst1_start_bit,
|
||||
inst2_bls_template, inst2_start_bit)
|
||||
|
||||
for col in range(num_bits):
|
||||
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col+bottom_start_bit)).uc()
|
||||
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col+bottom_start_bit)).uc()
|
||||
top_bl = top_inst.get_pin(top_bl_name.format(col+top_start_bit)).bc()
|
||||
top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc()
|
||||
bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col)
|
||||
top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col)
|
||||
bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc()
|
||||
top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc()
|
||||
|
||||
yoffset = 0.5*(top_bl.y+bottom_bl.y)
|
||||
self.add_path("m2",[bottom_bl, vector(bottom_bl.x,yoffset),
|
||||
vector(top_bl.x,yoffset), top_bl])
|
||||
self.add_path("m2",[bottom_br, vector(bottom_br.x,yoffset),
|
||||
vector(top_br.x,yoffset), top_br])
|
||||
yoffset = 0.5 * (top_bl.y + bot_bl.y)
|
||||
self.add_path("m2", [bot_bl,
|
||||
vector(bot_bl.x, yoffset),
|
||||
vector(top_bl.x, yoffset),
|
||||
top_bl])
|
||||
self.add_path("m2", [bot_br,
|
||||
vector(bot_br.x, yoffset),
|
||||
vector(top_br.x, yoffset),
|
||||
top_br])
|
||||
|
||||
def graph_exclude_precharge(self):
|
||||
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity"""
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from vector import vector
|
|||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class precharge_array(design.design):
|
||||
"""
|
||||
Dynamically generated precharge array of all bitlines. Cols is number
|
||||
|
|
@ -32,20 +33,13 @@ class precharge_array(design.design):
|
|||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def get_bl_name(self, port=0):
|
||||
def get_bl_name(self):
|
||||
bl_name = self.pc_cell.get_bl_names()
|
||||
if len(self.all_ports) == 1:
|
||||
return bl_name
|
||||
else:
|
||||
return bl_name + "{}".format(port)
|
||||
return bl_name
|
||||
|
||||
def get_br_name(self, port=0):
|
||||
def get_br_name(self):
|
||||
br_name = self.pc_cell.get_br_names()
|
||||
if len(self.all_ports) == 1:
|
||||
return br_name
|
||||
else:
|
||||
return br_name + "{}".format(port)
|
||||
|
||||
return br_name
|
||||
|
||||
def add_pins(self):
|
||||
"""Adds pins for spice file"""
|
||||
|
|
@ -75,11 +69,8 @@ class precharge_array(design.design):
|
|||
size=self.size,
|
||||
bitcell_bl=self.bitcell_bl,
|
||||
bitcell_br=self.bitcell_br)
|
||||
|
||||
|
||||
self.add_mod(self.pc_cell)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
self.add_layout_pin(text="en_bar",
|
||||
|
|
@ -93,20 +84,9 @@ class precharge_array(design.design):
|
|||
|
||||
for i in range(len(self.local_insts)):
|
||||
inst = self.local_insts[i]
|
||||
bl_pin = inst.get_pin("bl")
|
||||
self.add_layout_pin(text="bl_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=bl_pin.ll(),
|
||||
width=drc("minwidth_m2"),
|
||||
height=bl_pin.height())
|
||||
br_pin = inst.get_pin("br")
|
||||
self.add_layout_pin(text="br_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=br_pin.ll(),
|
||||
width=drc("minwidth_m2"),
|
||||
height=bl_pin.height())
|
||||
self.copy_layout_pin(inst, "bl", "bl_{0}".format(i))
|
||||
self.copy_layout_pin(inst, "br", "br_{0}".format(i))
|
||||
|
||||
|
||||
def create_insts(self):
|
||||
"""Creates a precharge array by horizontally tiling the precharge cell"""
|
||||
self.local_insts = []
|
||||
|
|
@ -119,7 +99,6 @@ class precharge_array(design.design):
|
|||
self.local_insts.append(inst)
|
||||
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"])
|
||||
|
||||
|
||||
def place_insts(self):
|
||||
""" Places precharge array by horizontally tiling the precharge cell"""
|
||||
from tech import cell_properties
|
||||
|
|
@ -137,8 +116,11 @@ class precharge_array(design.design):
|
|||
xoffset = xoffset + self.pc_cell.width
|
||||
|
||||
def get_en_cin(self):
|
||||
"""Get the relative capacitance of all the clk connections in the precharge array"""
|
||||
#Assume single port
|
||||
"""
|
||||
Get the relative capacitance of all the clk connections
|
||||
in the precharge array
|
||||
"""
|
||||
# Assume single port
|
||||
precharge_en_cin = self.pc_cell.get_en_cin()
|
||||
return precharge_en_cin*self.columns
|
||||
return precharge_en_cin * self.columns
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import design
|
|||
import debug
|
||||
import utils
|
||||
from tech import GDS,layer, parameter,drc
|
||||
from tech import cell_properties as props
|
||||
from globals import OPTS
|
||||
import logical_effort
|
||||
|
||||
|
|
@ -19,8 +20,12 @@ class sense_amp(design.design):
|
|||
the technology library.
|
||||
Sense amplifier to read a pair of bit-lines.
|
||||
"""
|
||||
|
||||
pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"]
|
||||
pin_names = [props.sense_amp.pin.bl,
|
||||
props.sense_amp.pin.br,
|
||||
props.sense_amp.pin.dout,
|
||||
props.sense_amp.pin.en,
|
||||
props.sense_amp.pin.vdd,
|
||||
props.sense_amp.pin.gnd]
|
||||
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||
if not OPTS.netlist_only:
|
||||
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
|
||||
|
|
@ -30,10 +35,18 @@ class sense_amp(design.design):
|
|||
pin_map = []
|
||||
|
||||
def get_bl_names(self):
|
||||
return "bl"
|
||||
return props.sense_amp.pin.bl
|
||||
|
||||
def get_br_names(self):
|
||||
return "br"
|
||||
return props.sense_amp.pin.br
|
||||
|
||||
@property
|
||||
def dout_name(self):
|
||||
return props.sense_amp.pin.dout
|
||||
|
||||
@property
|
||||
def en_name(self):
|
||||
return props.sense_amp.pin.en
|
||||
|
||||
def __init__(self, name):
|
||||
design.design.__init__(self, name)
|
||||
|
|
@ -79,11 +92,10 @@ class sense_amp(design.design):
|
|||
def get_enable_name(self):
|
||||
"""Returns name used for enable net"""
|
||||
#FIXME: A better programmatic solution to designate pins
|
||||
enable_name = "en"
|
||||
enable_name = self.en_name
|
||||
debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name))
|
||||
return enable_name
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""Adds edges based on inputs/outputs. Overrides base class function."""
|
||||
self.add_graph_edges(graph, port_nets)
|
||||
|
||||
|
|
@ -33,20 +33,21 @@ class sense_amp_array(design.design):
|
|||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def get_bl_name(self):
|
||||
bl_name = "bl"
|
||||
return bl_name
|
||||
|
||||
def get_bl_name(self, port=0):
|
||||
bl_name = self.amp.get_bl_names()
|
||||
if len(self.all_ports) == 1:
|
||||
return bl_name
|
||||
else:
|
||||
return bl_name + "{}".format(port)
|
||||
def get_br_name(self):
|
||||
br_name = "br"
|
||||
return br_name
|
||||
|
||||
def get_br_name(self, port=0):
|
||||
br_name = self.amp.get_br_names()
|
||||
if len(self.all_ports) == 1:
|
||||
return br_name
|
||||
else:
|
||||
return br_name + "{}".format(port)
|
||||
@property
|
||||
def data_name(self):
|
||||
return "data"
|
||||
|
||||
@property
|
||||
def en_name(self):
|
||||
return "en"
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
|
|
@ -69,10 +70,10 @@ class sense_amp_array(design.design):
|
|||
|
||||
def add_pins(self):
|
||||
for i in range(0,self.word_size):
|
||||
self.add_pin("data_{0}".format(i), "OUTPUT")
|
||||
self.add_pin("bl_{0}".format(i), "INPUT")
|
||||
self.add_pin("br_{0}".format(i), "INPUT")
|
||||
self.add_pin("en", "INPUT")
|
||||
self.add_pin(self.data_name + "_{0}".format(i), "OUTPUT")
|
||||
self.add_pin(self.get_bl_name() + "_{0}".format(i), "INPUT")
|
||||
self.add_pin(self.get_br_name() + "_{0}".format(i), "INPUT")
|
||||
self.add_pin(self.en_name, "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
|
|
@ -92,10 +93,10 @@ class sense_amp_array(design.design):
|
|||
name = "sa_d{0}".format(i)
|
||||
self.local_insts.append(self.add_inst(name=name,
|
||||
mod=self.amp))
|
||||
self.connect_inst(["bl_{0}".format(i),
|
||||
"br_{0}".format(i),
|
||||
"data_{0}".format(i),
|
||||
"en", "vdd", "gnd"])
|
||||
self.connect_inst([self.get_bl_name() + "_{0}".format(i),
|
||||
self.get_br_name() + "_{0}".format(i),
|
||||
self.data_name + "_{0}".format(i),
|
||||
self.en_name, "vdd", "gnd"])
|
||||
|
||||
def place_sense_amp_array(self):
|
||||
from tech import cell_properties
|
||||
|
|
@ -135,22 +136,22 @@ class sense_amp_array(design.design):
|
|||
start_layer="m2",
|
||||
vertical=True)
|
||||
|
||||
bl_pin = inst.get_pin("bl")
|
||||
br_pin = inst.get_pin("br")
|
||||
dout_pin = inst.get_pin("dout")
|
||||
|
||||
self.add_layout_pin(text="bl_{0}".format(i),
|
||||
bl_pin = inst.get_pin(inst.mod.get_bl_names())
|
||||
br_pin = inst.get_pin(inst.mod.get_br_names())
|
||||
dout_pin = inst.get_pin(inst.mod.dout_name)
|
||||
|
||||
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=bl_pin.ll(),
|
||||
width=bl_pin.width(),
|
||||
height=bl_pin.height())
|
||||
self.add_layout_pin(text="br_{0}".format(i),
|
||||
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=br_pin.ll(),
|
||||
width=br_pin.width(),
|
||||
height=br_pin.height())
|
||||
|
||||
self.add_layout_pin(text="data_{0}".format(i),
|
||||
|
||||
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=dout_pin.ll(),
|
||||
width=dout_pin.width(),
|
||||
|
|
@ -159,8 +160,8 @@ class sense_amp_array(design.design):
|
|||
|
||||
def route_rails(self):
|
||||
# add sclk rail across entire array
|
||||
sclk_offset = self.amp.get_pin("en").ll().scale(0,1)
|
||||
self.add_layout_pin(text="en",
|
||||
sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0,1)
|
||||
self.add_layout_pin(text=self.en_name,
|
||||
layer="m1",
|
||||
offset=sclk_offset,
|
||||
width=self.width,
|
||||
|
|
|
|||
|
|
@ -37,20 +37,13 @@ class single_level_column_mux_array(design.design):
|
|||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def get_bl_name(self, port=0):
|
||||
def get_bl_name(self):
|
||||
bl_name = self.mux.get_bl_names()
|
||||
if len(self.all_ports) == 1:
|
||||
return bl_name
|
||||
else:
|
||||
return bl_name + "{}".format(port)
|
||||
return bl_name
|
||||
|
||||
def get_br_name(self, port=0):
|
||||
br_name = self.mux.get_br_names()
|
||||
if len(self.all_ports) == 1:
|
||||
return br_name
|
||||
else:
|
||||
return br_name + "{}".format(port)
|
||||
|
||||
return br_name
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
|
|
|
|||
|
|
@ -75,8 +75,7 @@ class wordline_driver(design.design):
|
|||
"""
|
||||
|
||||
# Find the x offsets for where the vias/pins should be placed
|
||||
a_xoffset = self.nand_inst[0].rx()
|
||||
b_xoffset = self.inv2_inst[0].lx()
|
||||
a_xoffset = self.nand_inst[0].lx()
|
||||
for num in range(self.rows):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
|
|
@ -90,7 +89,7 @@ class wordline_driver(design.design):
|
|||
supply_pin = self.inv2_inst[num].get_pin(name)
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [a_xoffset, b_xoffset]:
|
||||
for xoffset in [a_xoffset]:
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name, pin_pos)
|
||||
|
||||
|
|
@ -116,7 +115,7 @@ class wordline_driver(design.design):
|
|||
"vdd", "gnd"])
|
||||
|
||||
def place_drivers(self):
|
||||
nand2_xoffset = 2*self.m1_width + 5*self.m1_space
|
||||
nand2_xoffset = 2 * self.m1_width + 5 * self.m1_space
|
||||
inv2_xoffset = nand2_xoffset + self.nand2.width
|
||||
|
||||
self.width = inv2_xoffset + self.inv.width
|
||||
|
|
@ -124,10 +123,10 @@ class wordline_driver(design.design):
|
|||
|
||||
for row in range(self.rows):
|
||||
if (row % 2):
|
||||
y_offset = self.inv.height*(row + 1)
|
||||
y_offset = self.inv.height * (row + 1)
|
||||
inst_mirror = "MX"
|
||||
else:
|
||||
y_offset = self.inv.height*row
|
||||
y_offset = self.inv.height * row
|
||||
inst_mirror = "R0"
|
||||
|
||||
nand2_offset = [nand2_xoffset, y_offset]
|
||||
|
|
@ -169,8 +168,8 @@ class wordline_driver(design.design):
|
|||
zr_pos = nand_inst.get_pin("Z").rc()
|
||||
al_pos = inv2_inst.get_pin("A").lc()
|
||||
# ensure the bend is in the middle
|
||||
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
|
||||
mid1_pos = vector(0.5 * (zr_pos.x + al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5 * (zr_pos.x + al_pos.x), al_pos.y)
|
||||
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
||||
# connect the decoder input pin to nand2 B
|
||||
|
|
@ -181,7 +180,7 @@ class wordline_driver(design.design):
|
|||
up_or_down = self.m2_space if row % 2 else -self.m2_space
|
||||
input_offset = vector(0, b_pos.y + up_or_down)
|
||||
base_offset = vector(clk_offset.x, input_offset.y)
|
||||
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
|
||||
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
|
||||
mid_via_offset = base_offset + contact_offset
|
||||
|
||||
# must under the clk line in M1
|
||||
|
|
@ -208,7 +207,7 @@ class wordline_driver(design.design):
|
|||
end=wl_offset - vector(self.m1_width, 0))
|
||||
|
||||
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
|
||||
"""
|
||||
"""
|
||||
Follows the clk_buf to a wordline signal adding
|
||||
each stages stage effort to a list.
|
||||
"""
|
||||
|
|
@ -225,7 +224,7 @@ class wordline_driver(design.design):
|
|||
return stage_effort_list
|
||||
|
||||
def get_wl_en_cin(self):
|
||||
"""
|
||||
"""
|
||||
Get the relative capacitance of all
|
||||
the enable connections in the bank
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import design
|
|||
import utils
|
||||
from globals import OPTS
|
||||
from tech import GDS,layer
|
||||
from tech import cell_properties as props
|
||||
|
||||
class write_driver(design.design):
|
||||
"""
|
||||
|
|
@ -19,7 +20,13 @@ class write_driver(design.design):
|
|||
the technology library.
|
||||
"""
|
||||
|
||||
pin_names = ["din", "bl", "br", "en", "vdd", "gnd"]
|
||||
pin_names = [props.write_driver.pin.din,
|
||||
props.write_driver.pin.bl,
|
||||
props.write_driver.pin.br,
|
||||
props.write_driver.pin.en,
|
||||
props.write_driver.pin.vdd,
|
||||
props.write_driver.pin.gnd]
|
||||
|
||||
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
|
||||
if not OPTS.netlist_only:
|
||||
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
|
||||
|
|
@ -38,10 +45,18 @@ class write_driver(design.design):
|
|||
self.add_pin_types(self.type_list)
|
||||
|
||||
def get_bl_names(self):
|
||||
return "bl"
|
||||
return props.write_driver.pin.bl
|
||||
|
||||
def get_br_names(self):
|
||||
return "br"
|
||||
return props.write_driver.pin.br
|
||||
|
||||
@property
|
||||
def din_name(self):
|
||||
return props.write_driver.pin.din
|
||||
|
||||
@property
|
||||
def en_name(self):
|
||||
return props.write_driver.pin.en
|
||||
|
||||
def get_w_en_cin(self):
|
||||
"""Get the relative capacitance of a single input"""
|
||||
|
|
|
|||
|
|
@ -37,19 +37,21 @@ class write_driver_array(design.design):
|
|||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def get_bl_name(self, port=0):
|
||||
bl_name = self.driver.get_bl_names()
|
||||
if len(self.all_ports) == 1:
|
||||
return bl_name
|
||||
else:
|
||||
return bl_name + "{}".format(port)
|
||||
def get_bl_name(self):
|
||||
bl_name = "bl"
|
||||
return bl_name
|
||||
|
||||
def get_br_name(self, port=0):
|
||||
br_name = self.driver.get_br_names()
|
||||
if len(self.all_ports) == 1:
|
||||
return br_name
|
||||
else:
|
||||
return br_name + "{}".format(port)
|
||||
def get_br_name(self):
|
||||
br_name = "br"
|
||||
return br_name
|
||||
|
||||
@property
|
||||
def data_name(self):
|
||||
return "data"
|
||||
|
||||
@property
|
||||
def en_name(self):
|
||||
return "en"
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
|
|
@ -71,15 +73,15 @@ class write_driver_array(design.design):
|
|||
|
||||
def add_pins(self):
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("data_{0}".format(i), "INPUT")
|
||||
for i in range(self.word_size):
|
||||
self.add_pin("bl_{0}".format(i), "OUTPUT")
|
||||
self.add_pin("br_{0}".format(i), "OUTPUT")
|
||||
self.add_pin(self.data_name + "_{0}".format(i), "INPUT")
|
||||
for i in range(self.word_size):
|
||||
self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT")
|
||||
self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT")
|
||||
if self.write_size:
|
||||
for i in range(self.num_wmasks):
|
||||
self.add_pin("en_{0}".format(i), "INPUT")
|
||||
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
|
||||
else:
|
||||
self.add_pin("en", "INPUT")
|
||||
self.add_pin(self.en_name, "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
|
|
@ -102,20 +104,20 @@ class write_driver_array(design.design):
|
|||
mod=self.driver)
|
||||
|
||||
if self.write_size:
|
||||
self.connect_inst(["data_{0}".format(index),
|
||||
"bl_{0}".format(index),
|
||||
"br_{0}".format(index),
|
||||
"en_{0}".format(windex), "vdd", "gnd"])
|
||||
self.connect_inst([self.data_name + "_{0}".format(index),
|
||||
self.get_bl_name() + "_{0}".format(index),
|
||||
self.get_br_name() + "_{0}".format(index),
|
||||
self.en_name + "_{0}".format(windex), "vdd", "gnd"])
|
||||
w+=1
|
||||
# when w equals write size, the next en pin can be connected since we are now at the next wmask bit
|
||||
if w == self.write_size:
|
||||
w = 0
|
||||
windex+=1
|
||||
else:
|
||||
self.connect_inst(["data_{0}".format(index),
|
||||
"bl_{0}".format(index),
|
||||
"br_{0}".format(index),
|
||||
"en", "vdd", "gnd"])
|
||||
self.connect_inst([self.data_name + "_{0}".format(index),
|
||||
self.get_bl_name() + "_{0}".format(index),
|
||||
self.get_br_name() + "_{0}".format(index),
|
||||
self.en_name, "vdd", "gnd"])
|
||||
|
||||
|
||||
def place_write_array(self):
|
||||
|
|
@ -140,21 +142,22 @@ class write_driver_array(design.design):
|
|||
|
||||
def add_layout_pins(self):
|
||||
for i in range(self.word_size):
|
||||
din_pin = self.driver_insts[i].get_pin("din")
|
||||
self.add_layout_pin(text="data_{0}".format(i),
|
||||
inst = self.driver_insts[i]
|
||||
din_pin = inst.get_pin(inst.mod.din_name)
|
||||
self.add_layout_pin(text=self.data_name + "_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=din_pin.ll(),
|
||||
width=din_pin.width(),
|
||||
height=din_pin.height())
|
||||
bl_pin = self.driver_insts[i].get_pin("bl")
|
||||
self.add_layout_pin(text="bl_{0}".format(i),
|
||||
bl_pin = inst.get_pin(inst.mod.get_bl_names())
|
||||
self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=bl_pin.ll(),
|
||||
width=bl_pin.width(),
|
||||
height=bl_pin.height())
|
||||
|
||||
br_pin = self.driver_insts[i].get_pin("br")
|
||||
self.add_layout_pin(text="br_{0}".format(i),
|
||||
|
||||
br_pin = inst.get_pin(inst.mod.get_br_names())
|
||||
self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i),
|
||||
layer="m2",
|
||||
offset=br_pin.ll(),
|
||||
width=br_pin.width(),
|
||||
|
|
@ -169,7 +172,8 @@ class write_driver_array(design.design):
|
|||
start_layer = "m2")
|
||||
if self.write_size:
|
||||
for bit in range(self.num_wmasks):
|
||||
en_pin = self.driver_insts[bit*self.write_size].get_pin("en")
|
||||
inst = self.driver_insts[bit*self.write_size]
|
||||
en_pin = inst.get_pin(inst.mod.en_name)
|
||||
# Determine width of wmask modified en_pin with/without col mux
|
||||
wmask_en_len = self.words_per_row*(self.write_size * self.driver_spacing)
|
||||
if (self.words_per_row == 1):
|
||||
|
|
@ -177,15 +181,16 @@ class write_driver_array(design.design):
|
|||
else:
|
||||
en_gap = self.driver_spacing
|
||||
|
||||
self.add_layout_pin(text="en_{0}".format(bit),
|
||||
self.add_layout_pin(text=self.en_name + "_{0}".format(bit),
|
||||
layer=en_pin.layer,
|
||||
offset=en_pin.ll(),
|
||||
width=wmask_en_len-en_gap,
|
||||
height=en_pin.height())
|
||||
else:
|
||||
self.add_layout_pin(text="en",
|
||||
inst = self.driver_insts[0]
|
||||
self.add_layout_pin(text=self.en_name,
|
||||
layer="m1",
|
||||
offset=self.driver_insts[0].get_pin("en").ll().scale(0,1),
|
||||
offset=inst.get_pin(inst.mod.en_name).ll().scale(0,1),
|
||||
width=self.width)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ class pgate(design.design):
|
|||
if height:
|
||||
self.height = height
|
||||
elif not height:
|
||||
# By default, we make it 8 M1 pitch tall
|
||||
self.height = 8*self.m1_pitch
|
||||
# By default, we make it 10 M1 pitch tall
|
||||
self.height = 10*self.m1_pitch
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
|
|
@ -103,7 +103,7 @@ class pgate(design.design):
|
|||
- vector(0.5 * contact_width - 0.5 * self.poly_width, 0)
|
||||
elif position == "right":
|
||||
contact_offset = left_gate_offset \
|
||||
+ vector(0.5 * contact.width + 0.5 * self.poly_width, 0)
|
||||
+ vector(0.5 * contact_width + 0.5 * self.poly_width, 0)
|
||||
else:
|
||||
debug.error("Invalid contact placement option.", -1)
|
||||
|
||||
|
|
@ -128,16 +128,16 @@ class pgate(design.design):
|
|||
""" Extend the n/p wells to cover whole cell """
|
||||
|
||||
# This should match the cells in the cell library
|
||||
nwell_y_offset = 0.48 * self.height
|
||||
full_height = self.height + 0.5*self.m1_width
|
||||
self.nwell_y_offset = 0.48 * self.height
|
||||
full_height = self.height + 0.5* self.m1_width
|
||||
|
||||
# FIXME: float rounding problem
|
||||
if "nwell" in layer:
|
||||
# Add a rail width to extend the well to the top of the rail
|
||||
nwell_max_offset = max(self.find_highest_layer_coords("nwell").y,
|
||||
full_height)
|
||||
nwell_position = vector(0, nwell_y_offset) - vector(self.well_extend_active, 0)
|
||||
nwell_height = nwell_max_offset - nwell_y_offset
|
||||
nwell_position = vector(0, self.nwell_y_offset) - vector(self.well_extend_active, 0)
|
||||
nwell_height = nwell_max_offset - self.nwell_y_offset
|
||||
self.add_rect(layer="nwell",
|
||||
offset=nwell_position,
|
||||
width=self.well_width,
|
||||
|
|
@ -153,7 +153,7 @@ class pgate(design.design):
|
|||
pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y,
|
||||
-0.5 * self.m1_width)
|
||||
pwell_position = vector(-self.well_extend_active, pwell_min_offset)
|
||||
pwell_height = nwell_y_offset - pwell_position.y
|
||||
pwell_height = self.nwell_y_offset - pwell_position.y
|
||||
self.add_rect(layer="pwell",
|
||||
offset=pwell_position,
|
||||
width=self.well_width,
|
||||
|
|
@ -264,3 +264,12 @@ class pgate(design.design):
|
|||
# offset=implant_offset,
|
||||
# width=implant_width,
|
||||
# height=implant_height)
|
||||
|
||||
def determine_width(self):
|
||||
""" Determine the width based on the well contacts (assumed to be on the right side) """
|
||||
# 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
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -51,9 +51,9 @@ class pinv(pgate.pgate):
|
|||
|
||||
def create_layout(self):
|
||||
""" Calls all functions related to the generation of the layout """
|
||||
self.setup_layout_constants()
|
||||
self.place_ptx()
|
||||
self.add_well_contacts()
|
||||
self.determine_width()
|
||||
self.extend_wells()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
|
|
@ -147,21 +147,6 @@ class pinv(pgate.pgate):
|
|||
debug.check(self.pmos_width >= drc("minwidth_tx"),
|
||||
"Cannot finger PMOS transistors to fit cell height.")
|
||||
|
||||
def setup_layout_constants(self):
|
||||
"""
|
||||
Compute the width and height
|
||||
"""
|
||||
|
||||
# the width is determined the multi-finger PMOS device width plus
|
||||
# the well contact width, spacing between them
|
||||
# space is for power supply contact to nwell m1 spacing
|
||||
self.width = self.pmos.active_offset.x + self.pmos.active_width \
|
||||
+ self.active_space + contact.nwell_contact.width \
|
||||
+ 0.5 * self.nwell_enclose_active \
|
||||
+ self.m1_space
|
||||
# This includes full enclosures on each end
|
||||
self.well_width = self.width + 2*self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
def add_ptx(self):
|
||||
""" Create the PMOS and NMOS transistors. """
|
||||
|
|
|
|||
|
|
@ -49,10 +49,11 @@ class pnand2(pgate.pgate):
|
|||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.setup_layout_constants()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.determine_width()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
|
@ -96,15 +97,6 @@ class pnand2(pgate.pgate):
|
|||
# source and drain pins
|
||||
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
||||
|
||||
# Two PMOS devices and a well contact. Separation between each.
|
||||
# Enclosure space on the sides.
|
||||
self.width = 2 * self.pmos.active_width + contact.active_contact.width \
|
||||
+ 2 * self.active_space \
|
||||
+ 0.5 * self.nwell_enclose_active
|
||||
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
# This is the extra space needed to ensure DRC rules
|
||||
# to the active contacts
|
||||
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
|
||||
|
|
@ -190,16 +182,16 @@ class pnand2(pgate.pgate):
|
|||
|
||||
def route_inputs(self):
|
||||
""" Route the A and B inputs """
|
||||
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
|
||||
+ self.m2_space + 0.5 * self.m2_width
|
||||
inputB_yoffset = self.nmos2_inst.uy() + 0.5 * contact.poly_contact.height
|
||||
self.route_input_gate(self.pmos2_inst,
|
||||
self.nmos2_inst,
|
||||
inputB_yoffset,
|
||||
"B",
|
||||
position="center")
|
||||
position="right")
|
||||
|
||||
# This will help with the wells and the input/output placement
|
||||
self.inputA_yoffset = inputB_yoffset + self.input_spacing
|
||||
self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \
|
||||
- contact.poly_contact.height
|
||||
self.route_input_gate(self.pmos1_inst,
|
||||
self.nmos1_inst,
|
||||
self.inputA_yoffset,
|
||||
|
|
@ -242,8 +234,8 @@ class pnand2(pgate.pgate):
|
|||
self.add_layout_pin_rect_center(text="Z",
|
||||
layer="m1",
|
||||
offset=out_offset,
|
||||
width=contact.m1_via.first_layer_height,
|
||||
height=contact.m1_via.first_layer_width)
|
||||
width=contact.m1_via.first_layer_width,
|
||||
height=contact.m1_via.first_layer_height)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Returns dynamic and leakage power. Results in nW"""
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ class pnand3(pgate.pgate):
|
|||
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||
|
||||
# FIXME: Allow these to be sized
|
||||
debug.check(size == 1,"Size 1 pnand3 is only supported now.")
|
||||
debug.check(size == 1,
|
||||
"Size 1 pnand3 is only supported now.")
|
||||
self.tx_mults = 1
|
||||
|
||||
# Creates the netlist and layout
|
||||
|
|
@ -57,14 +58,15 @@ class pnand3(pgate.pgate):
|
|||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.setup_layout_constants()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.determine_width()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
||||
|
||||
def add_ptx(self):
|
||||
""" Create the PMOS and NMOS transistors. """
|
||||
self.nmos = factory.create(module_type="ptx",
|
||||
|
|
@ -87,16 +89,9 @@ class pnand3(pgate.pgate):
|
|||
""" Pre-compute some handy layout parameters. """
|
||||
|
||||
# Compute the overlap of the source and drain pins
|
||||
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
||||
|
||||
# Two PMOS devices and a well contact. Separation between each.
|
||||
# Enclosure space on the sides.
|
||||
self.width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
|
||||
+ 2 * self.active_space + 0.5 * self.nwell_enclose_active \
|
||||
- self.overlap_offset.x
|
||||
self.well_width = self.width + 2 * self.nwell_enclose_active
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
|
||||
overlap_xoffset = self.pmos.get_pin("D").ll().x - self.pmos.get_pin("S").ll().x
|
||||
self.ptx_offset = vector(overlap_xoffset, 0)
|
||||
|
||||
# This is the extra space needed to ensure DRC rules
|
||||
# to the active contacts
|
||||
nmos = factory.create(module_type="ptx", tx_type="nmos")
|
||||
|
|
@ -153,30 +148,25 @@ class pnand3(pgate.pgate):
|
|||
"""
|
||||
|
||||
pmos1_pos = vector(self.pmos.active_offset.x,
|
||||
self.height - self.pmos.active_height \
|
||||
- self.top_bottom_space)
|
||||
self.height - self.pmos.active_height - self.top_bottom_space)
|
||||
self.pmos1_inst.place(pmos1_pos)
|
||||
|
||||
pmos2_pos = pmos1_pos + self.overlap_offset
|
||||
pmos2_pos = pmos1_pos + self.ptx_offset
|
||||
self.pmos2_inst.place(pmos2_pos)
|
||||
|
||||
self.pmos3_pos = pmos2_pos + self.overlap_offset
|
||||
self.pmos3_pos = pmos2_pos + self.ptx_offset
|
||||
self.pmos3_inst.place(self.pmos3_pos)
|
||||
|
||||
|
||||
nmos1_pos = vector(self.pmos.active_offset.x,
|
||||
self.top_bottom_space)
|
||||
self.nmos1_inst.place(nmos1_pos)
|
||||
|
||||
nmos2_pos = nmos1_pos + self.overlap_offset
|
||||
nmos2_pos = nmos1_pos + self.ptx_offset
|
||||
self.nmos2_inst.place(nmos2_pos)
|
||||
|
||||
self.nmos3_pos = nmos2_pos + self.overlap_offset
|
||||
self.nmos3_pos = nmos2_pos + self.ptx_offset
|
||||
self.nmos3_inst.place(self.nmos3_pos)
|
||||
|
||||
# This will help with the wells and the input/output placement
|
||||
self.output_pos = vector(0, 0.5*self.height)
|
||||
|
||||
def add_well_contacts(self):
|
||||
""" Add n/p well taps to the layout and connect to supplies """
|
||||
|
||||
|
|
@ -188,40 +178,36 @@ class pnand3(pgate.pgate):
|
|||
|
||||
self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
|
||||
|
||||
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
|
||||
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
|
||||
|
||||
self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd")
|
||||
|
||||
def route_inputs(self):
|
||||
""" Route the A and B inputs """
|
||||
# wire space or wire and one contact space
|
||||
metal_spacing = max(self.m1_space + self.m1_width,
|
||||
self.m2_space + self.m2_width,
|
||||
self.m1_space + 0.5 *contact.poly_contact.width + 0.5 * self.m1_width)
|
||||
""" Route the A and B and C inputs """
|
||||
|
||||
active_spacing = max(self.m1_space,
|
||||
0.5 * contact.poly_contact.first_layer_width + self.poly_to_active)
|
||||
inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing
|
||||
m1_pitch = self.m1_space + contact.m1_via.first_layer_height
|
||||
# Put B right on the well line
|
||||
self.inputB_yoffset = self.nwell_y_offset
|
||||
self.route_input_gate(self.pmos2_inst,
|
||||
self.nmos2_inst,
|
||||
self.inputB_yoffset,
|
||||
"B",
|
||||
position="center")
|
||||
|
||||
self.inputC_yoffset = self.inputB_yoffset - m1_pitch
|
||||
self.route_input_gate(self.pmos3_inst,
|
||||
self.nmos3_inst,
|
||||
inputC_yoffset,
|
||||
self.inputC_yoffset,
|
||||
"C",
|
||||
position="center")
|
||||
|
||||
inputB_yoffset = inputC_yoffset + metal_spacing
|
||||
self.route_input_gate(self.pmos2_inst,
|
||||
self.nmos2_inst,
|
||||
inputB_yoffset,
|
||||
"B",
|
||||
position="center")
|
||||
|
||||
self.inputA_yoffset = inputB_yoffset + metal_spacing
|
||||
self.inputA_yoffset = self.inputB_yoffset + m1_pitch
|
||||
self.route_input_gate(self.pmos1_inst,
|
||||
self.nmos1_inst,
|
||||
self.inputA_yoffset,
|
||||
"A",
|
||||
position="center")
|
||||
|
||||
|
||||
def route_output(self):
|
||||
""" Route the Z output """
|
||||
# PMOS1 drain
|
||||
|
|
@ -243,7 +229,7 @@ class pnand3(pgate.pgate):
|
|||
directions=("V", "V"))
|
||||
|
||||
# PMOS3 and NMOS3 are drain aligned
|
||||
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.uc()])
|
||||
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.center()])
|
||||
# Route in the A input track (top track)
|
||||
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
|
||||
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
|
||||
|
|
@ -273,7 +259,7 @@ class pnand3(pgate.pgate):
|
|||
# In fF
|
||||
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
|
||||
transition_prob = 0.1094
|
||||
return transition_prob *(c_load + c_para)
|
||||
return transition_prob * (c_load + c_para)
|
||||
|
||||
def input_load(self):
|
||||
"""Return the relative input capacitance of a single input"""
|
||||
|
|
|
|||
|
|
@ -48,10 +48,11 @@ class pnor2(pgate.pgate):
|
|||
""" Calls all functions related to the generation of the layout """
|
||||
|
||||
self.setup_layout_constants()
|
||||
self.route_supply_rails()
|
||||
self.place_ptx()
|
||||
self.connect_rails()
|
||||
self.add_well_contacts()
|
||||
self.determine_width()
|
||||
self.route_supply_rails()
|
||||
self.connect_rails()
|
||||
self.extend_wells()
|
||||
self.route_inputs()
|
||||
self.route_output()
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from tech import parameter
|
|||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
from tech import drc
|
||||
|
||||
|
||||
class precharge(design.design):
|
||||
|
|
@ -30,7 +31,16 @@ class precharge(design.design):
|
|||
self.width = self.bitcell.width
|
||||
self.bitcell_bl = bitcell_bl
|
||||
self.bitcell_br = bitcell_br
|
||||
|
||||
self.bitcell_bl_pin =self.bitcell.get_pin(self.bitcell_bl)
|
||||
self.bitcell_br_pin =self.bitcell.get_pin(self.bitcell_br)
|
||||
|
||||
if self.bitcell_bl_pin.layer == "m1":
|
||||
self.bitline_layer = "m1"
|
||||
self.en_layer = "m2"
|
||||
else:
|
||||
self.bitline_layer = "m2"
|
||||
self.en_layer = "m1"
|
||||
|
||||
# Creates the netlist and layout
|
||||
# Since it has variable height, it is not a pgate.
|
||||
self.create_netlist()
|
||||
|
|
@ -50,6 +60,7 @@ class precharge(design.design):
|
|||
self.create_ptx()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place_ptx()
|
||||
self.connect_poly()
|
||||
self.route_en()
|
||||
|
|
@ -78,18 +89,24 @@ class precharge(design.design):
|
|||
|
||||
# Adds the rail across the width of the cell
|
||||
vdd_position = vector(0.5 * self.width, self.height)
|
||||
self.add_rect_center(layer="m1",
|
||||
layer_width = drc("minwidth_" + self.en_layer)
|
||||
self.add_rect_center(layer=self.en_layer,
|
||||
offset=vdd_position,
|
||||
width=self.width,
|
||||
height=self.m1_width)
|
||||
height=layer_width)
|
||||
|
||||
pmos_pin = self.upper_pmos2_inst.get_pin("S")
|
||||
# center of vdd rail
|
||||
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
|
||||
self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos])
|
||||
if self.en_layer != "m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pmos_vdd_pos)
|
||||
|
||||
|
||||
|
||||
# Add vdd pin above the transistor
|
||||
self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
|
||||
self.add_power_pin("vdd", self.well_contact_pos, vertical=True)
|
||||
|
||||
def create_ptx(self):
|
||||
"""
|
||||
|
|
@ -113,6 +130,8 @@ class precharge(design.design):
|
|||
Place both the upper_pmos and lower_pmos to the module
|
||||
"""
|
||||
|
||||
# reserve some offset to jog the bitlines
|
||||
self.initial_yoffset = self.pmos.active_offset.y + self.m2_pitch
|
||||
# Compute the other pmos2 location,
|
||||
# but determining offset to overlap the source and drain pins
|
||||
overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
|
||||
|
|
@ -120,17 +139,18 @@ class precharge(design.design):
|
|||
contact_xdiff = self.pmos.get_pin("S").lx()
|
||||
|
||||
# adds the lower pmos to layout
|
||||
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
|
||||
bl_xoffset = self.bitcell_bl_pin.lx()
|
||||
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
|
||||
self.nwell_enclose_active),
|
||||
self.pmos.active_offset.y)
|
||||
self.initial_yoffset)
|
||||
self.lower_pmos_inst.place(self.lower_pmos_position)
|
||||
|
||||
# adds the upper pmos(s) to layout
|
||||
ydiff = self.pmos.height + 2 * self.m1_space + contact.poly_contact.width
|
||||
# adds the upper pmos(s) to layout with 2 M2 tracks
|
||||
ydiff = self.pmos.height + self.m2_pitch
|
||||
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
|
||||
self.upper_pmos1_inst.place(self.upper_pmos1_pos)
|
||||
|
||||
# Second pmos to the right of the first
|
||||
upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset
|
||||
self.upper_pmos2_inst.place(upper_pmos2_pos)
|
||||
|
||||
|
|
@ -161,16 +181,21 @@ class precharge(design.design):
|
|||
"""
|
||||
Adds the en input rail, en contact/vias, and connects to the pmos
|
||||
"""
|
||||
|
||||
# adds the en contact to connect the gates to the en rail on metal1
|
||||
|
||||
# adds the en contact to connect the gates to the en rail
|
||||
# midway in the 4 M2 tracks
|
||||
offset = self.lower_pmos_inst.get_pin("G").ul() \
|
||||
+ vector(0, 0.5 * self.poly_space)
|
||||
+ vector(0, 0.5 * self.m2_pitch)
|
||||
self.add_via_center(layers=self.poly_stack,
|
||||
offset=offset)
|
||||
if self.en_layer == "m2":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=offset)
|
||||
|
||||
|
||||
# adds the en rail on metal1
|
||||
self.add_layout_pin_segment_center(text="en_bar",
|
||||
layer="m1",
|
||||
layer=self.en_layer,
|
||||
start=offset.scale(0, 1),
|
||||
end=offset.scale(0, 1) + vector(self.width, 0))
|
||||
|
||||
|
|
@ -180,16 +205,15 @@ class precharge(design.design):
|
|||
"""
|
||||
|
||||
# adds the contact from active to metal1
|
||||
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
|
||||
+ vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \
|
||||
+ self.nwell_extend_active)
|
||||
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
|
||||
+ vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \
|
||||
+ self.nwell_extend_active)
|
||||
self.add_via_center(layers=self.active_stack,
|
||||
offset=well_contact_pos,
|
||||
offset=self.well_contact_pos,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
|
||||
# leave an extra pitch for the height
|
||||
self.height = well_contact_pos.y + contact.active_contact.height + self.m1_pitch
|
||||
self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space
|
||||
|
||||
# nwell should span the whole design since it is pmos only
|
||||
self.add_rect(layer="nwell",
|
||||
|
|
@ -201,80 +225,86 @@ class precharge(design.design):
|
|||
"""
|
||||
Adds both bit-line and bit-line-bar to the module
|
||||
"""
|
||||
|
||||
# adds the BL on metal 2
|
||||
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \
|
||||
- vector(0.5 * self.m2_width, 0)
|
||||
self.bl_pin = self.add_layout_pin(text="bl",
|
||||
layer="m2",
|
||||
offset=offset,
|
||||
height=self.height)
|
||||
layer_width = drc("minwidth_" + self.bitline_layer)
|
||||
layer_space = drc("{0}_to_{0}".format(self.bitline_layer))
|
||||
|
||||
# adds the BR on metal 2
|
||||
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \
|
||||
- vector(0.5 * self.m2_width, 0)
|
||||
self.br_pin = self.add_layout_pin(text="br",
|
||||
layer="m2",
|
||||
offset=offset,
|
||||
height=self.height)
|
||||
# adds the BL
|
||||
self.bl_xoffset = layer_space + 0.5 * layer_width
|
||||
top_pos = vector(self.bl_xoffset, self.height)
|
||||
pin_pos = vector(self.bl_xoffset, 0)
|
||||
self.add_path(self.bitline_layer, [top_pos, pin_pos])
|
||||
self.bl_pin = self.add_layout_pin_segment_center(text="bl",
|
||||
layer=self.bitline_layer,
|
||||
start=pin_pos,
|
||||
end=top_pos)
|
||||
|
||||
# adds the BR
|
||||
self.br_xoffset = self.width - layer_space - 0.5 * layer_width
|
||||
top_pos = vector(self.br_xoffset, self.height)
|
||||
pin_pos = vector(self.br_xoffset, 0)
|
||||
self.add_path(self.bitline_layer, [top_pos, pin_pos])
|
||||
self.br_pin = self.add_layout_pin_segment_center(text="br",
|
||||
layer=self.bitline_layer,
|
||||
start=pin_pos,
|
||||
end=top_pos)
|
||||
|
||||
def connect_to_bitlines(self):
|
||||
"""
|
||||
Connect the bitlines to the devices
|
||||
"""
|
||||
self.add_bitline_contacts()
|
||||
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),
|
||||
self.get_pin("bl"))
|
||||
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),
|
||||
self.get_pin("bl"))
|
||||
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),
|
||||
self.get_pin("br"))
|
||||
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),
|
||||
self.get_pin("br"))
|
||||
self.connect_pmos(self.lower_pmos_inst.get_pin("S"),
|
||||
self.bl_xoffset)
|
||||
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),
|
||||
self.br_xoffset)
|
||||
|
||||
self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),
|
||||
self.bl_xoffset)
|
||||
self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),
|
||||
self.br_xoffset)
|
||||
|
||||
def add_bitline_contacts(self):
|
||||
"""
|
||||
Adds contacts/via from metal1 to metal2 for bit-lines
|
||||
"""
|
||||
|
||||
upper_pin = self.upper_pmos1_inst.get_pin("S")
|
||||
lower_pin = self.lower_pmos_inst.get_pin("S")
|
||||
# No contacts needed if M1
|
||||
if self.bitline_layer == "m1":
|
||||
return
|
||||
|
||||
# BL goes up to M2 at the transistor
|
||||
self.bl_contact =self.add_via_center(layers=self.m1_stack,
|
||||
offset=upper_pin.center(),
|
||||
# BL
|
||||
lower_pin = self.lower_pmos_inst.get_pin("S")
|
||||
self.lower_via = self.add_via_center(layers=self.m1_stack,
|
||||
offset=lower_pin.center(),
|
||||
directions=("V", "V"))
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=lower_pin.center(),
|
||||
directions=("V", "V"))
|
||||
|
||||
# BR routes over on M1 first
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=vector(self.br_pin.cx(), upper_pin.cy()),
|
||||
directions=("V", "V"))
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=vector(self.br_pin.cx(), lower_pin.cy()),
|
||||
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
|
||||
upper_pin = self.upper_pmos1_inst.get_pin("S")
|
||||
self.upper_via2 = self.add_via_center(layers=self.m1_stack,
|
||||
offset=upper_pin.center(),
|
||||
directions=("V", "V"))
|
||||
|
||||
def connect_pmos_m1(self, pmos_pin, bit_pin):
|
||||
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):
|
||||
"""
|
||||
Connect a pmos pin to bitline pin
|
||||
"""
|
||||
|
||||
left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
|
||||
right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
|
||||
left_pos = vector(min(pmos_pin.cx(), bit_xoffset), pmos_pin.cy())
|
||||
right_pos = vector(max(pmos_pin.cx(), bit_xoffset), pmos_pin.cy())
|
||||
|
||||
self.add_path("m1", [left_pos, right_pos] )
|
||||
|
||||
def connect_pmos_m2(self, pmos_pin, bit_pin):
|
||||
"""
|
||||
Connect a pmos pin to bitline pin
|
||||
"""
|
||||
|
||||
left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
|
||||
right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
|
||||
|
||||
self.add_path("m2", [left_pos, right_pos], self.bl_contact.height)
|
||||
self.add_path(self.bitline_layer,
|
||||
[left_pos, right_pos],
|
||||
width=pmos_pin.height())
|
||||
|
||||
def get_en_cin(self):
|
||||
"""Get the relative capacitance of the enable in the precharge cell"""
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import debug
|
|||
from tech import layer, drc, spice
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
import contact
|
||||
|
||||
|
||||
class ptx(design.design):
|
||||
|
|
@ -28,6 +29,7 @@ class ptx(design.design):
|
|||
tx_type="nmos",
|
||||
connect_active=False,
|
||||
connect_poly=False,
|
||||
series_devices=False,
|
||||
num_contacts=None):
|
||||
# We need to keep unique names because outputting to GDSII
|
||||
# will use the last record with a given name. I.e., you will
|
||||
|
|
@ -50,6 +52,7 @@ class ptx(design.design):
|
|||
self.tx_width = width
|
||||
self.connect_active = connect_active
|
||||
self.connect_poly = connect_poly
|
||||
self.series_devices = series_devices
|
||||
self.num_contacts = num_contacts
|
||||
|
||||
# Since it has variable height, it is not a pgate.
|
||||
|
|
@ -127,27 +130,25 @@ class ptx(design.design):
|
|||
directions=("V", "V"),
|
||||
dimensions=(1, self.num_contacts))
|
||||
|
||||
# The contacted poly pitch
|
||||
self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width,
|
||||
self.poly_space)
|
||||
|
||||
# The contacted poly pitch
|
||||
self.contact_spacing = 2 * self.contact_to_gate + \
|
||||
self.contact_width + self.poly_width
|
||||
|
||||
# This is measured because of asymmetric enclosure rules
|
||||
active_enclose_contact = 0.5*(self.active_contact.width - self.contact_width)
|
||||
# This is the extra poly spacing due to the poly contact to poly contact pitch
|
||||
# of contacted gates
|
||||
extra_poly_contact_width = contact.poly_contact.width - self.poly_width
|
||||
|
||||
# This is the distance from the side of
|
||||
# poly gate to the contacted end of active
|
||||
# (i.e. the "outside" contacted diffusion sizes)
|
||||
self.end_to_poly = self.active_contact.width - active_enclose_contact + \
|
||||
self.contact_to_gate
|
||||
# This is the spacing between S/D contacts
|
||||
# This is the spacing between the poly gates
|
||||
self.min_poly_pitch = self.poly_space + self.poly_width
|
||||
self.contacted_poly_pitch = self.poly_space + contact.poly_contact.width
|
||||
self.contact_pitch = 2 * self.contact_to_gate + self.poly_width + self.contact_width
|
||||
self.poly_pitch = max(self.min_poly_pitch,
|
||||
self.contacted_poly_pitch,
|
||||
self.contact_pitch)
|
||||
|
||||
self.end_to_contact = 0.5 * self.active_contact.width
|
||||
|
||||
# Active width is determined by enclosure on both ends and contacted pitch,
|
||||
# at least one poly and n-1 poly pitches
|
||||
self.active_width = 2 * self.end_to_poly + self.poly_width + \
|
||||
(self.mults - 1) * self.poly_pitch
|
||||
self.active_width = 2 * self.end_to_contact + self.active_contact.width \
|
||||
+ 2 * self.contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch
|
||||
|
||||
# Active height is just the transistor width
|
||||
self.active_height = self.tx_width
|
||||
|
|
@ -287,13 +288,13 @@ class ptx(design.design):
|
|||
Add the poly gates(s) and (optionally) connect them.
|
||||
"""
|
||||
# poly is one contacted spacing from the end and down an extension
|
||||
poly_offset = self.active_offset \
|
||||
+ vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \
|
||||
+ vector(self.end_to_poly, -self.poly_extend_active)
|
||||
|
||||
poly_offset = self.contact_offset \
|
||||
+ vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.contact_to_gate, 0)
|
||||
|
||||
# poly_positions are the bottom center of the poly gates
|
||||
poly_positions = []
|
||||
|
||||
self.poly_positions = []
|
||||
self.poly_gates = []
|
||||
|
||||
# It is important that these are from left to right,
|
||||
# so that the pins are in the right
|
||||
# order for the accessors
|
||||
|
|
@ -304,16 +305,18 @@ class ptx(design.design):
|
|||
offset=poly_offset,
|
||||
height=self.poly_height,
|
||||
width=self.poly_width)
|
||||
self.add_layout_pin_rect_center(text="G",
|
||||
layer="poly",
|
||||
offset=poly_offset,
|
||||
height=self.poly_height,
|
||||
width=self.poly_width)
|
||||
poly_positions.append(poly_offset)
|
||||
poly_offset = poly_offset + vector(self.poly_pitch,0)
|
||||
gate = self.add_layout_pin_rect_center(text="G",
|
||||
layer="poly",
|
||||
offset=poly_offset,
|
||||
height=self.poly_height,
|
||||
width=self.poly_width)
|
||||
self.poly_positions.append(poly_offset)
|
||||
self.poly_gates.append(gate)
|
||||
|
||||
poly_offset = poly_offset + vector(self.poly_pitch, 0)
|
||||
|
||||
if self.connect_poly:
|
||||
self.connect_fingered_poly(poly_positions)
|
||||
self.connect_fingered_poly(self.poly_positions)
|
||||
|
||||
def add_active(self):
|
||||
"""
|
||||
|
|
@ -362,59 +365,88 @@ class ptx(design.design):
|
|||
"""
|
||||
return 1
|
||||
|
||||
def get_contact_positions(self):
|
||||
"""
|
||||
Create a list of the centers of drain and source contact positions.
|
||||
"""
|
||||
# The first one will always be a source
|
||||
source_positions = [self.contact_offset]
|
||||
drain_positions = []
|
||||
# It is important that these are from left to right,
|
||||
# so that the pins are in the right
|
||||
# order for the accessors.
|
||||
for i in range(self.mults):
|
||||
if i%2:
|
||||
# It's a source... so offset from previous drain.
|
||||
source_positions.append(drain_positions[-1] + vector(self.contact_spacing, 0))
|
||||
else:
|
||||
# It's a drain... so offset from previous source.
|
||||
drain_positions.append(source_positions[-1] + vector(self.contact_spacing, 0))
|
||||
|
||||
return [source_positions,drain_positions]
|
||||
|
||||
def add_active_contacts(self):
|
||||
"""
|
||||
Add the active contacts to the transistor.
|
||||
"""
|
||||
drain_positions = []
|
||||
source_positions = []
|
||||
|
||||
[source_positions,drain_positions] = self.get_contact_positions()
|
||||
|
||||
for pos in source_positions:
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
# Keep a list of the source/drain contacts
|
||||
self.source_contacts = []
|
||||
self.drain_contacts = []
|
||||
|
||||
# First one is always a SOURCE
|
||||
label = "S"
|
||||
pos = self.contact_offset
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text=label,
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text="S",
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
self.source_contacts.append(contact)
|
||||
source_positions.append(pos)
|
||||
|
||||
# Skip these if they are going to be in series
|
||||
if not self.series_devices:
|
||||
for (poly1, poly2) in zip(self.poly_positions, self.poly_positions[1:]):
|
||||
pos = vector(0.5 * (poly1.x + poly2.x),
|
||||
self.contact_offset.y)
|
||||
# Alternate source and drains
|
||||
if label == "S":
|
||||
label = "D"
|
||||
drain_positions.append(pos)
|
||||
else:
|
||||
label = "S"
|
||||
source_positions.append(pos)
|
||||
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text=label,
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
|
||||
for pos in drain_positions:
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
if label == "S":
|
||||
self.source_contacts.append(contact)
|
||||
else:
|
||||
self.drain_contacts.append(contact)
|
||||
|
||||
pos = vector(self.active_offset.x + self.active_width - 0.5 * self.active_contact.width,
|
||||
self.contact_offset.y)
|
||||
# Last one is the opposite of previous
|
||||
if label == "S":
|
||||
label = "D"
|
||||
drain_positions.append(pos)
|
||||
else:
|
||||
label = "S"
|
||||
source_positions.append(pos)
|
||||
contact=self.add_via_center(layers=self.active_stack,
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text=label,
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
size=(1, self.num_contacts),
|
||||
directions=("V","V"),
|
||||
implant_type=self.implant_type,
|
||||
well_type=self.well_type)
|
||||
self.add_layout_pin_rect_center(text="D",
|
||||
layer="m1",
|
||||
offset=pos,
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
width=contact.mod.second_layer_width,
|
||||
height=contact.mod.second_layer_height)
|
||||
if label == "S":
|
||||
self.source_contacts.append(contact)
|
||||
else:
|
||||
self.drain_contacts.append(contact)
|
||||
|
||||
if self.connect_active:
|
||||
self.connect_fingered_active(drain_positions, source_positions)
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
import datetime
|
||||
import getpass
|
||||
import debug
|
||||
from globals import OPTS, print_time
|
||||
from sram_config import sram_config
|
||||
|
||||
|
||||
|
||||
class sram():
|
||||
"""
|
||||
This is not a design module, but contains an SRAM design instance.
|
||||
|
|
@ -28,7 +26,7 @@ class sram():
|
|||
from design import design
|
||||
design.name_map=[]
|
||||
|
||||
debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size,
|
||||
debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size,
|
||||
self.num_words,
|
||||
self.num_banks))
|
||||
start_time = datetime.datetime.now()
|
||||
|
|
@ -40,30 +38,28 @@ class sram():
|
|||
elif self.num_banks == 2:
|
||||
from sram_2bank import sram_2bank as sram
|
||||
else:
|
||||
debug.error("Invalid number of banks.",-1)
|
||||
debug.error("Invalid number of banks.", -1)
|
||||
|
||||
self.s = sram(name, sram_config)
|
||||
self.s = sram(name, sram_config)
|
||||
self.s.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.s.create_layout()
|
||||
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("SRAM creation", datetime.datetime.now(), start_time)
|
||||
|
||||
|
||||
def sp_write(self,name):
|
||||
def sp_write(self, name):
|
||||
self.s.sp_write(name)
|
||||
|
||||
def lef_write(self,name):
|
||||
def lef_write(self, name):
|
||||
self.s.lef_write(name)
|
||||
|
||||
def gds_write(self,name):
|
||||
def gds_write(self, name):
|
||||
self.s.gds_write(name)
|
||||
|
||||
def verilog_write(self,name):
|
||||
def verilog_write(self, name):
|
||||
self.s.verilog_write(name)
|
||||
|
||||
|
||||
def save(self):
|
||||
""" Save all the output files while reporting time to do it as well. """
|
||||
|
||||
|
|
@ -107,7 +103,6 @@ class sram():
|
|||
debug.print_raw("LIB: Characterizing... ")
|
||||
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
|
||||
print_time("Characterization", datetime.datetime.now(), start_time)
|
||||
|
||||
|
||||
# Write the config file
|
||||
start_time = datetime.datetime.now()
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ class sram_1bank(sram_base):
|
|||
sram_base.__init__(self, name, sram_config)
|
||||
|
||||
def create_modules(self):
|
||||
"""
|
||||
"""
|
||||
This adds the modules for a single bank SRAM with control
|
||||
logic.
|
||||
logic.
|
||||
"""
|
||||
|
||||
self.bank_inst=self.create_bank(0)
|
||||
|
|
@ -40,7 +40,7 @@ class sram_1bank(sram_base):
|
|||
self.data_dff_insts = self.create_data_dff()
|
||||
|
||||
def place_instances(self):
|
||||
"""
|
||||
"""
|
||||
This places the instances for a single bank SRAM with control
|
||||
logic and up to 2 ports.
|
||||
"""
|
||||
|
|
@ -53,19 +53,19 @@ class sram_1bank(sram_base):
|
|||
# the sense amps/column mux and cell array)
|
||||
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
||||
# up to the row address DFFs.
|
||||
control_pos = [None]*len(self.all_ports)
|
||||
row_addr_pos = [None]*len(self.all_ports)
|
||||
col_addr_pos = [None]*len(self.all_ports)
|
||||
wmask_pos = [None]*len(self.all_ports)
|
||||
data_pos = [None]*len(self.all_ports)
|
||||
control_pos = [None] * len(self.all_ports)
|
||||
row_addr_pos = [None] * len(self.all_ports)
|
||||
col_addr_pos = [None] * len(self.all_ports)
|
||||
wmask_pos = [None] * len(self.all_ports)
|
||||
data_pos = [None] * len(self.all_ports)
|
||||
|
||||
if self.write_size:
|
||||
max_gap_size = self.m3_pitch*self.word_size + 2*self.m1_pitch
|
||||
max_gap_size_wmask = self.m2_pitch*max(self.num_wmasks+1,self.col_addr_size+1) + 2*self.m1_pitch
|
||||
max_gap_size = self.m3_pitch * self.word_size + 2 * self.m1_pitch
|
||||
max_gap_size_wmask = self.m2_pitch * max(self.num_wmasks + 1, self.col_addr_size + 1) + 2 * self.m1_pitch
|
||||
else:
|
||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||
# The M1 pitch is for supply rail spacings
|
||||
max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch
|
||||
max_gap_size = self.m2_pitch * max(self.word_size + 1,self.col_addr_size + 1) + 2 * self.m1_pitch
|
||||
|
||||
# Port 0
|
||||
port = 0
|
||||
|
|
@ -79,7 +79,7 @@ class sram_1bank(sram_base):
|
|||
|
||||
# Add the data flops below the write mask flops.
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||
-max_gap_size - max_gap_size_wmask - 2*self.dff.height)
|
||||
-max_gap_size - max_gap_size_wmask - 2 * self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port])
|
||||
else:
|
||||
# Add the data flops below the bank to the right of the lower-left of bank array
|
||||
|
|
@ -93,8 +93,7 @@ class sram_1bank(sram_base):
|
|||
self.data_dff_insts[port].place(data_pos[port])
|
||||
else:
|
||||
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,0)
|
||||
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||
|
||||
# Add the col address flops below the bank to the left of the lower-left of bank array
|
||||
if self.col_addr_dff:
|
||||
|
|
@ -106,11 +105,11 @@ class sram_1bank(sram_base):
|
|||
-max_gap_size - self.col_addr_dff_insts[port].height)
|
||||
self.col_addr_dff_insts[port].place(col_addr_pos[port])
|
||||
else:
|
||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x,0)
|
||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||
|
||||
# This includes 2 M2 pitches for the row addr clock line.
|
||||
control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch,
|
||||
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2*self.bank.m2_gap )
|
||||
control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch,
|
||||
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - 2 * self.bank.m2_gap)
|
||||
self.control_logic_insts[port].place(control_pos[port])
|
||||
|
||||
# The row address bits are placed above the control logic aligned on the right.
|
||||
|
|
@ -133,7 +132,7 @@ class sram_1bank(sram_base):
|
|||
|
||||
# Add the data flops below the write mask flops
|
||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||
self.bank.height + max_gap_size_wmask + max_gap_size + 2*self.dff.height)
|
||||
self.bank.height + max_gap_size_wmask + max_gap_size + 2 * self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||
else:
|
||||
# Add the data flops above the bank to the left of the upper-right of bank array
|
||||
|
|
@ -158,11 +157,10 @@ class sram_1bank(sram_base):
|
|||
col_addr_pos[port] = self.bank_inst.ur()
|
||||
|
||||
# This includes 2 M2 pitches for the row addr clock line
|
||||
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch,
|
||||
self.bank.bank_array_ur.y + self.control_logic_insts[port].height -
|
||||
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch,
|
||||
self.bank.bank_array_ur.y + self.control_logic_insts[port].height - \
|
||||
(self.control_logic_insts[port].height - self.control_logic_insts[port].mod.control_logic_center.y)
|
||||
+ 2*self.bank.m2_gap)
|
||||
#import pdb; pdb.set_trace()
|
||||
+ 2 * self.bank.m2_gap)
|
||||
self.control_logic_insts[port].place(control_pos[port], mirror="XY")
|
||||
|
||||
# The row address bits are placed above the control logic aligned on the left.
|
||||
|
|
@ -172,7 +170,6 @@ class sram_1bank(sram_base):
|
|||
row_addr_pos[port] = vector(x_offset, y_offset)
|
||||
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY")
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
"""
|
||||
Add the top-level pins for a single bank SRAM with control.
|
||||
|
|
@ -180,28 +177,39 @@ class sram_1bank(sram_base):
|
|||
for port in self.all_ports:
|
||||
# Connect the control pins as inputs
|
||||
for signal in self.control_logic_inputs[port] + ["clk"]:
|
||||
self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port))
|
||||
self.copy_layout_pin(self.control_logic_insts[port],
|
||||
signal,
|
||||
signal + "{}".format(port))
|
||||
|
||||
if port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "dout{0}[{1}]".format(port,bit))
|
||||
self.copy_layout_pin(self.bank_inst,
|
||||
"dout{0}_{1}".format(port, bit),
|
||||
"dout{0}[{1}]".format(port, bit))
|
||||
|
||||
# Lower address bits
|
||||
for bit in range(self.col_addr_size):
|
||||
self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit),"addr{0}[{1}]".format(port,bit))
|
||||
self.copy_layout_pin(self.col_addr_dff_insts[port],
|
||||
"din_{}".format(bit),
|
||||
"addr{0}[{1}]".format(port, bit))
|
||||
# Upper address bits
|
||||
for bit in range(self.row_addr_size):
|
||||
self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit),"addr{0}[{1}]".format(port,bit+self.col_addr_size))
|
||||
self.copy_layout_pin(self.row_addr_dff_insts[port],
|
||||
"din_{}".format(bit),
|
||||
"addr{0}[{1}]".format(port, bit + self.col_addr_size))
|
||||
|
||||
if port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "din{0}[{1}]".format(port,bit))
|
||||
self.copy_layout_pin(self.data_dff_insts[port],
|
||||
"din_{}".format(bit),
|
||||
"din{0}[{1}]".format(port, bit))
|
||||
|
||||
if self.write_size:
|
||||
for bit in range(self.num_wmasks):
|
||||
self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port,bit))
|
||||
self.copy_layout_pin(self.wmask_dff_insts[port],
|
||||
"din_{}".format(bit),
|
||||
"wmask{0}[{1}]".format(port, bit))
|
||||
|
||||
|
||||
def route_layout(self):
|
||||
""" Route a single bank SRAM """
|
||||
|
||||
|
|
@ -255,13 +263,15 @@ class sram_1bank(sram_base):
|
|||
offset=clk_steiner_pos)
|
||||
|
||||
# Note, the via to the control logic is taken care of above
|
||||
self.add_wire(("m3","via2","m2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
|
||||
|
||||
if self.col_addr_dff:
|
||||
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
|
||||
dff_clk_pos = dff_clk_pin.center()
|
||||
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
|
||||
self.add_wire(("m3","via2","m2"),[dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
|
||||
if port in self.write_ports:
|
||||
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
|
||||
|
|
@ -269,8 +279,11 @@ class sram_1bank(sram_base):
|
|||
mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y)
|
||||
# In some designs, the steiner via will be too close to the mid_pos via
|
||||
# so make the wire as wide as the contacts
|
||||
self.add_path("m2",[mid_pos, clk_steiner_pos], width=max(m2_via.width,m2_via.height))
|
||||
self.add_wire(("m3","via2","m2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
self.add_path("m2",
|
||||
[mid_pos, clk_steiner_pos],
|
||||
width=max(m2_via.width, m2_via.height))
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
|
||||
if self.write_size:
|
||||
wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk")
|
||||
|
|
@ -280,7 +293,6 @@ class sram_1bank(sram_base):
|
|||
# so make the wire as wide as the contacts
|
||||
self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height))
|
||||
self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
|
||||
|
||||
def route_control_logic(self):
|
||||
""" Route the control logic pins that are not inputs """
|
||||
|
|
@ -291,28 +303,29 @@ class sram_1bank(sram_base):
|
|||
if "clk" in signal:
|
||||
continue
|
||||
src_pin = self.control_logic_insts[port].get_pin(signal)
|
||||
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
|
||||
dest_pin = self.bank_inst.get_pin(signal + "{}".format(port))
|
||||
self.connect_vbus_m2m3(src_pin, dest_pin)
|
||||
|
||||
for port in self.all_ports:
|
||||
# Only input (besides pins) is the replica bitline
|
||||
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
|
||||
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
||||
self.connect_vbus_m2m3(src_pin, dest_pin)
|
||||
|
||||
|
||||
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
|
||||
|
||||
self.connect_hbus_m2m3(src_pin, dest_pin)
|
||||
|
||||
def route_row_addr_dff(self):
|
||||
""" Connect the output of the row flops to the bank pins """
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.row_addr_size):
|
||||
flop_name = "dout_{}".format(bit)
|
||||
bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size)
|
||||
bank_name = "addr{0}_{1}".format(port, bit + self.col_addr_size)
|
||||
flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name)
|
||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||
flop_pos = flop_pin.center()
|
||||
bank_pos = bank_pin.center()
|
||||
mid_pos = vector(bank_pos.x,flop_pos.y)
|
||||
self.add_wire(("m3","via2","m2"),[flop_pos, mid_pos,bank_pos])
|
||||
mid_pos = vector(bank_pos.x, flop_pos.y)
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[flop_pos, mid_pos, bank_pos])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=flop_pos)
|
||||
|
||||
|
|
@ -320,11 +333,11 @@ class sram_1bank(sram_base):
|
|||
""" Connect the output of the col flops to the bank pins """
|
||||
for port in self.all_ports:
|
||||
if port%2:
|
||||
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size+2)*self.m1_pitch)
|
||||
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size + 2) * self.m1_pitch)
|
||||
else:
|
||||
offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||
offset = self.col_addr_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
||||
|
||||
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
|
||||
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
|
||||
col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
|
||||
pitch=self.m1_pitch,
|
||||
offset=offset,
|
||||
|
|
@ -335,10 +348,9 @@ class sram_1bank(sram_base):
|
|||
data_dff_map = zip(dff_names, bus_names)
|
||||
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets)
|
||||
|
||||
bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)]
|
||||
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
|
||||
data_bank_map = zip(bank_names, bus_names)
|
||||
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets)
|
||||
|
||||
|
||||
def route_data_dff(self):
|
||||
""" Connect the output of the data flops to the write driver """
|
||||
|
|
@ -346,14 +358,14 @@ class sram_1bank(sram_base):
|
|||
for port in self.write_ports:
|
||||
if self.write_size:
|
||||
if port % 2:
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2)*self.m3_pitch)
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m3_pitch)
|
||||
else:
|
||||
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch)
|
||||
else:
|
||||
if port%2:
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch)
|
||||
if port % 2:
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m1_pitch)
|
||||
else:
|
||||
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
||||
|
||||
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
||||
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
|
||||
|
|
@ -362,13 +374,13 @@ class sram_1bank(sram_base):
|
|||
pin_offset = self.data_dff_insts[port].get_pin(x).center()
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pin_offset,
|
||||
directions = ("V", "V"))
|
||||
directions=("V", "V"))
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=pin_offset)
|
||||
self.add_via_center(layers=self.m3_stack,
|
||||
offset=pin_offset)
|
||||
|
||||
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
|
||||
bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size)]
|
||||
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
if self.write_size:
|
||||
for x in bank_names:
|
||||
|
|
@ -397,7 +409,7 @@ class sram_1bank(sram_base):
|
|||
# This is where the channel will start (y-dimension at least)
|
||||
for port in self.write_ports:
|
||||
if port % 2:
|
||||
offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks+2) * self.m1_pitch)
|
||||
offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks + 2) * self.m1_pitch)
|
||||
else:
|
||||
offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
||||
|
||||
|
|
@ -416,16 +428,14 @@ class sram_1bank(sram_base):
|
|||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=offset_pin)
|
||||
|
||||
|
||||
route_map = list(zip(bank_pins, dff_pins))
|
||||
self.create_horizontal_channel_route(netlist=route_map,
|
||||
offset=offset,
|
||||
layer_stack=self.m1_stack)
|
||||
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
"""
|
||||
This adds some points for easier debugging if LVS goes wrong.
|
||||
These should probably be turned off by default though, since extraction
|
||||
will show these as ports in the extracted netlist.
|
||||
"""
|
||||
|
|
@ -438,7 +448,7 @@ class sram_1bank(sram_base):
|
|||
|
||||
def graph_exclude_data_dff(self):
|
||||
"""Removes data dff and wmask dff (if applicable) from search graph. """
|
||||
#Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay.
|
||||
# Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay.
|
||||
for inst in self.data_dff_insts:
|
||||
self.graph_inst_exclude.add(inst)
|
||||
if self.write_size:
|
||||
|
|
@ -447,7 +457,7 @@ class sram_1bank(sram_base):
|
|||
|
||||
def graph_exclude_addr_dff(self):
|
||||
"""Removes data dff from search graph. """
|
||||
#Address is considered not part of the critical path, subjectively removed
|
||||
# Address is considered not part of the critical path, subjectively removed
|
||||
for inst in self.row_addr_dff_insts:
|
||||
self.graph_inst_exclude.add(inst)
|
||||
|
||||
|
|
@ -457,27 +467,27 @@ class sram_1bank(sram_base):
|
|||
|
||||
def graph_exclude_ctrl_dffs(self):
|
||||
"""Exclude dffs for CSB, WEB, etc from graph"""
|
||||
#Insts located in control logic, exclusion function called here
|
||||
# Insts located in control logic, exclusion function called here
|
||||
for inst in self.control_logic_insts:
|
||||
inst.mod.graph_exclude_dffs()
|
||||
|
||||
def get_sen_name(self, sram_name, port=0):
|
||||
"""Returns the s_en spice name."""
|
||||
#Naming scheme is hardcoded using this function, should be built into the
|
||||
#graph in someway.
|
||||
# Naming scheme is hardcoded using this function, should be built into the
|
||||
# graph in someway.
|
||||
sen_name = "s_en{}".format(port)
|
||||
control_conns = self.get_conns(self.control_logic_insts[port])
|
||||
#Sanity checks
|
||||
# Sanity checks
|
||||
if sen_name not in control_conns:
|
||||
debug.error("Signal={} not contained in control logic connections={}"\
|
||||
.format(sen_name, control_conns))
|
||||
debug.error("Signal={} not contained in control logic connections={}".format(sen_name,
|
||||
control_conns))
|
||||
if sen_name in self.pins:
|
||||
debug.error("Internal signal={} contained in port list. Name defined by the parent.")
|
||||
debug.error("Internal signal={} contained in port list. Name defined by the parent.".format(sen_name))
|
||||
return "X{}.{}".format(sram_name, sen_name)
|
||||
|
||||
def get_cell_name(self, inst_name, row, col):
|
||||
"""Gets the spice name of the target bitcell."""
|
||||
#Sanity check in case it was forgotten
|
||||
# Sanity check in case it was forgotten
|
||||
if inst_name.find('x') != 0:
|
||||
inst_name = 'x'+inst_name
|
||||
return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col)
|
||||
inst_name = 'x' + inst_name
|
||||
return self.bank_inst.mod.get_cell_name(inst_name + '.x' + self.bank_inst.name, row, col)
|
||||
|
|
|
|||
|
|
@ -5,20 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
import datetime
|
||||
import getpass
|
||||
import debug
|
||||
from datetime import datetime
|
||||
from importlib import reload
|
||||
from vector import vector
|
||||
from globals import OPTS, print_time
|
||||
import logical_effort
|
||||
from design import design
|
||||
from verilog import verilog
|
||||
from lef import lef
|
||||
from sram_factory import factory
|
||||
import logical_effort
|
||||
|
||||
|
||||
class sram_base(design, verilog, lef):
|
||||
"""
|
||||
|
|
@ -36,11 +32,11 @@ class sram_base(design, verilog, lef):
|
|||
self.bank_insts = []
|
||||
|
||||
if self.write_size:
|
||||
self.num_wmasks = int(self.word_size/self.write_size)
|
||||
self.num_wmasks = int(self.word_size / self.write_size)
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
#For logical effort delay calculations.
|
||||
# For logical effort delay calculations.
|
||||
self.all_mods_except_control_done = False
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -48,11 +44,11 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("din{0}[{1}]".format(port,bit),"INPUT")
|
||||
self.add_pin("din{0}[{1}]".format(port, bit), "INPUT")
|
||||
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.addr_size):
|
||||
self.add_pin("addr{0}[{1}]".format(port,bit),"INPUT")
|
||||
self.add_pin("addr{0}[{1}]".format(port, bit), "INPUT")
|
||||
|
||||
# These are used to create the physical pins
|
||||
self.control_logic_inputs = []
|
||||
|
|
@ -69,27 +65,26 @@ class sram_base(design, verilog, lef):
|
|||
self.control_logic_outputs.append(self.control_logic_r.get_outputs())
|
||||
|
||||
for port in self.all_ports:
|
||||
self.add_pin("csb{}".format(port),"INPUT")
|
||||
self.add_pin("csb{}".format(port), "INPUT")
|
||||
for port in self.readwrite_ports:
|
||||
self.add_pin("web{}".format(port),"INPUT")
|
||||
self.add_pin("web{}".format(port), "INPUT")
|
||||
for port in self.all_ports:
|
||||
self.add_pin("clk{}".format(port),"INPUT")
|
||||
self.add_pin("clk{}".format(port), "INPUT")
|
||||
# add the optional write mask pins
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.num_wmasks):
|
||||
self.add_pin("wmask{0}[{1}]".format(port,bit),"INPUT")
|
||||
self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT")
|
||||
for port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("dout{0}[{1}]".format(port,bit),"OUTPUT")
|
||||
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
|
||||
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def create_netlist(self):
|
||||
""" Netlist creation """
|
||||
|
||||
start_time = datetime.now()
|
||||
start_time = datetime.datetime.now()
|
||||
|
||||
# Must create the control logic before pins to get the pins
|
||||
self.add_modules()
|
||||
|
|
@ -100,23 +95,21 @@ class sram_base(design, verilog, lef):
|
|||
self.width=0
|
||||
self.height=0
|
||||
|
||||
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Submodules",datetime.now(), start_time)
|
||||
|
||||
print_time("Submodules", datetime.datetime.now(), start_time)
|
||||
|
||||
def create_layout(self):
|
||||
""" Layout creation """
|
||||
start_time = datetime.now()
|
||||
""" Layout creation """
|
||||
start_time = datetime.datetime.now()
|
||||
self.place_instances()
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Placement",datetime.now(), start_time)
|
||||
print_time("Placement", datetime.datetime.now(), start_time)
|
||||
|
||||
start_time = datetime.now()
|
||||
start_time = datetime.datetime.now()
|
||||
self.route_layout()
|
||||
self.route_supplies()
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Routing",datetime.now(), start_time)
|
||||
print_time("Routing", datetime.datetime.now(), start_time)
|
||||
|
||||
self.add_lvs_correspondence_points()
|
||||
|
||||
|
|
@ -126,14 +119,14 @@ class sram_base(design, verilog, lef):
|
|||
self.width = highest_coord[0]
|
||||
self.height = highest_coord[1]
|
||||
|
||||
start_time = datetime.now()
|
||||
start_time = datetime.datetime.now()
|
||||
# We only enable final verification if we have routed the design
|
||||
self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True)
|
||||
if not OPTS.is_unit_test:
|
||||
print_time("Verification",datetime.now(), start_time)
|
||||
print_time("Verification", datetime.datetime.now(), start_time)
|
||||
|
||||
def create_modules(self):
|
||||
debug.error("Must override pure virtual function.",-1)
|
||||
debug.error("Must override pure virtual function.", -1)
|
||||
|
||||
def route_supplies(self):
|
||||
""" Route the supply grid and connect the pins to them. """
|
||||
|
|
@ -141,8 +134,8 @@ class sram_base(design, verilog, lef):
|
|||
# Copy the pins to the top level
|
||||
# This will either be used to route or left unconnected.
|
||||
for inst in self.insts:
|
||||
self.copy_power_pins(inst,"vdd")
|
||||
self.copy_power_pins(inst,"gnd")
|
||||
self.copy_power_pins(inst, "vdd")
|
||||
self.copy_power_pins(inst, "gnd")
|
||||
|
||||
import tech
|
||||
if not OPTS.route_supplies:
|
||||
|
|
@ -164,33 +157,31 @@ class sram_base(design, verilog, lef):
|
|||
from supply_grid_router import supply_grid_router as router
|
||||
rtr=router(grid_stack, self)
|
||||
rtr.route()
|
||||
|
||||
|
||||
def compute_bus_sizes(self):
|
||||
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
||||
|
||||
# address size + control signals + one-hot bank select signals
|
||||
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks,2) + 1
|
||||
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks, 2) + 1
|
||||
# data bus size
|
||||
self.num_horizontal_line = self.word_size
|
||||
|
||||
self.vertical_bus_width = self.m2_pitch*self.num_vertical_line
|
||||
self.vertical_bus_width = self.m2_pitch * self.num_vertical_line
|
||||
# vertical bus height depends on 2 or 4 banks
|
||||
|
||||
self.data_bus_height = self.m3_pitch*self.num_horizontal_line
|
||||
self.data_bus_width = 2*(self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
|
||||
self.data_bus_height = self.m3_pitch * self.num_horizontal_line
|
||||
self.data_bus_width = 2 * (self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
|
||||
|
||||
self.control_bus_height = self.m1_pitch*(self.control_size+2)
|
||||
self.control_bus_height = self.m1_pitch * (self.control_size + 2)
|
||||
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
|
||||
|
||||
self.supply_bus_height = self.m1_pitch*2 # 2 for vdd/gnd placed with control bus
|
||||
self.supply_bus_height = self.m1_pitch * 2 # 2 for vdd/gnd placed with control bus
|
||||
self.supply_bus_width = self.data_bus_width
|
||||
|
||||
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
|
||||
debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width,
|
||||
debug.check(self.bank.width + self.vertical_bus_width > 0.9 * self.control_logic.width,
|
||||
"Bank is too small compared to control logic.")
|
||||
|
||||
|
||||
def add_busses(self):
|
||||
""" Add the horizontal and vertical busses """
|
||||
# Vertical bus
|
||||
|
|
@ -213,24 +204,22 @@ class sram_base(design, verilog, lef):
|
|||
names=self.control_bus_names[port],
|
||||
length=self.vertical_bus_height)
|
||||
|
||||
self.addr_bus_names=["A{0}[{1}]".format(port,i) for i in range(self.addr_size)]
|
||||
self.addr_bus_names=["A{0}[{1}]".format(port, i) for i in range(self.addr_size)]
|
||||
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=self.addr_bus_offset,
|
||||
names=self.addr_bus_names,
|
||||
length=self.addr_bus_height))
|
||||
|
||||
|
||||
self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port,i) for i in range(self.num_banks)]
|
||||
self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port, i) for i in range(self.num_banks)]
|
||||
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=self.bank_sel_bus_offset,
|
||||
names=self.bank_sel_bus_names,
|
||||
length=self.vertical_bus_height))
|
||||
|
||||
|
||||
# Horizontal data bus
|
||||
self.data_bus_names = ["DATA{0}[{1}]".format(port,i) for i in range(self.word_size)]
|
||||
self.data_bus_names = ["DATA{0}[{1}]".format(port, i) for i in range(self.word_size)]
|
||||
self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3",
|
||||
pitch=self.m3_pitch,
|
||||
offset=self.data_bus_offset,
|
||||
|
|
@ -249,7 +238,7 @@ class sram_base(design, verilog, lef):
|
|||
# the decoder in 4-bank SRAMs
|
||||
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
|
||||
pitch=self.m1_pitch,
|
||||
offset=self.supply_bus_offset+vector(0,self.m1_pitch),
|
||||
offset=self.supply_bus_offset + vector(0, self.m1_pitch),
|
||||
names=["gnd"],
|
||||
length=self.supply_bus_width))
|
||||
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
|
||||
|
|
@ -258,20 +247,17 @@ class sram_base(design, verilog, lef):
|
|||
names=self.control_bus_names[port],
|
||||
length=self.control_bus_width))
|
||||
|
||||
|
||||
|
||||
def add_multi_bank_modules(self):
|
||||
""" Create the multibank address flops and bank decoder """
|
||||
from dff_buf_array import dff_buf_array
|
||||
self.msb_address = dff_buf_array(name="msb_address",
|
||||
rows=1,
|
||||
columns=self.num_banks/2)
|
||||
columns=self.num_banks / 2)
|
||||
self.add_mod(self.msb_address)
|
||||
|
||||
if self.num_banks>2:
|
||||
self.msb_decoder = self.bank.decoder.pre2_4
|
||||
self.add_mod(self.msb_decoder)
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
self.bitcell = factory.create(module_type=OPTS.bitcell)
|
||||
|
|
@ -293,7 +279,6 @@ class sram_base(design, verilog, lef):
|
|||
if self.write_size:
|
||||
self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks)
|
||||
self.add_mod(self.wmask_dff)
|
||||
|
||||
|
||||
# Create the bank module (up to four are instantiated)
|
||||
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
|
||||
|
|
@ -305,7 +290,8 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
self.bank_count = 0
|
||||
|
||||
#The control logic can resize itself based on the other modules. Requires all other modules added before control logic.
|
||||
# The control logic can resize itself based on the other modules.
|
||||
# Requires all other modules added before control logic.
|
||||
self.all_mods_except_control_done = True
|
||||
|
||||
c = reload(__import__(OPTS.control_logic))
|
||||
|
|
@ -320,40 +306,40 @@ class sram_base(design, verilog, lef):
|
|||
port_type="rw")
|
||||
self.add_mod(self.control_logic_rw)
|
||||
if len(self.writeonly_ports)>0:
|
||||
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
|
||||
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
|
||||
words_per_row=self.words_per_row,
|
||||
word_size=self.word_size,
|
||||
sram=self,
|
||||
port_type="w")
|
||||
self.add_mod(self.control_logic_w)
|
||||
if len(self.readonly_ports)>0:
|
||||
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
|
||||
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
|
||||
words_per_row=self.words_per_row,
|
||||
word_size=self.word_size,
|
||||
sram=self,
|
||||
port_type="r")
|
||||
self.add_mod(self.control_logic_r)
|
||||
|
||||
def create_bank(self,bank_num):
|
||||
""" Create a bank """
|
||||
def create_bank(self, bank_num):
|
||||
""" Create a bank """
|
||||
self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num),
|
||||
mod=self.bank))
|
||||
|
||||
temp = []
|
||||
for port in self.read_ports:
|
||||
for bit in range(self.word_size):
|
||||
temp.append("dout{0}[{1}]".format(port,bit))
|
||||
temp.append("dout{0}[{1}]".format(port, bit))
|
||||
for port in self.all_ports:
|
||||
temp.append("rbl_bl{0}".format(port))
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
temp.append("bank_din{0}[{1}]".format(port,bit))
|
||||
temp.append("bank_din{0}[{1}]".format(port, bit))
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.bank_addr_size):
|
||||
temp.append("a{0}[{1}]".format(port,bit))
|
||||
temp.append("a{0}[{1}]".format(port, bit))
|
||||
if(self.num_banks > 1):
|
||||
for port in self.all_ports:
|
||||
temp.append("bank_sel{0}[{1}]".format(port,bank_num))
|
||||
temp.append("bank_sel{0}[{1}]".format(port, bank_num))
|
||||
for port in self.read_ports:
|
||||
temp.append("s_en{0}".format(port))
|
||||
for port in self.all_ports:
|
||||
|
|
@ -369,7 +355,6 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
return self.bank_insts[-1]
|
||||
|
||||
|
||||
def place_bank(self, bank_inst, position, x_flip, y_flip):
|
||||
""" Place a bank at the given position with orientations """
|
||||
|
||||
|
|
@ -400,7 +385,6 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
return bank_inst
|
||||
|
||||
|
||||
def create_row_addr_dff(self):
|
||||
""" Add all address flops for the main decoder """
|
||||
insts = []
|
||||
|
|
@ -412,13 +396,12 @@ class sram_base(design, verilog, lef):
|
|||
inputs = []
|
||||
outputs = []
|
||||
for bit in range(self.row_addr_size):
|
||||
inputs.append("addr{}[{}]".format(port,bit+self.col_addr_size))
|
||||
outputs.append("a{}[{}]".format(port,bit+self.col_addr_size))
|
||||
inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size))
|
||||
outputs.append("a{}[{}]".format(port, bit + self.col_addr_size))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
||||
|
||||
return insts
|
||||
|
||||
|
||||
def create_col_addr_dff(self):
|
||||
""" Add and place all address flops for the column decoder """
|
||||
|
|
@ -431,14 +414,13 @@ class sram_base(design, verilog, lef):
|
|||
inputs = []
|
||||
outputs = []
|
||||
for bit in range(self.col_addr_size):
|
||||
inputs.append("addr{}[{}]".format(port,bit))
|
||||
outputs.append("a{}[{}]".format(port,bit))
|
||||
inputs.append("addr{}[{}]".format(port, bit))
|
||||
outputs.append("a{}[{}]".format(port, bit))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
||||
|
||||
return insts
|
||||
|
||||
|
||||
def create_data_dff(self):
|
||||
""" Add and place all data flops """
|
||||
insts = []
|
||||
|
|
@ -454,8 +436,8 @@ class sram_base(design, verilog, lef):
|
|||
inputs = []
|
||||
outputs = []
|
||||
for bit in range(self.word_size):
|
||||
inputs.append("din{}[{}]".format(port,bit))
|
||||
outputs.append("bank_din{}[{}]".format(port,bit))
|
||||
inputs.append("din{}[{}]".format(port, bit))
|
||||
outputs.append("bank_din{}[{}]".format(port, bit))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
|
||||
|
||||
|
|
@ -483,7 +465,6 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
return insts
|
||||
|
||||
|
||||
def create_control_logic(self):
|
||||
""" Add control logic instances """
|
||||
|
||||
|
|
@ -516,12 +497,13 @@ class sram_base(design, verilog, lef):
|
|||
|
||||
return insts
|
||||
|
||||
|
||||
def connect_vbus_m2m3(self, src_pin, dest_pin):
|
||||
""" Helper routine to connect an instance to a vertical bus.
|
||||
"""
|
||||
Helper routine to connect an instance to a vertical bus.
|
||||
Routes horizontal then vertical L shape.
|
||||
Dest pin is assumed to be on M2.
|
||||
Src pin can be on M1/M2/M3."""
|
||||
Src pin can be on M1/M2/M3.
|
||||
"""
|
||||
|
||||
if src_pin.cx()<dest_pin.cx():
|
||||
in_pos = src_pin.rc()
|
||||
|
|
@ -533,16 +515,52 @@ class sram_base(design, verilog, lef):
|
|||
out_pos = dest_pin.uc()
|
||||
|
||||
# move horizontal first
|
||||
self.add_wire(("m3","via2","m2"),[in_pos, vector(out_pos.x,in_pos.y),out_pos])
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[in_pos,
|
||||
vector(out_pos.x, in_pos.y),
|
||||
out_pos])
|
||||
if src_pin.layer=="m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=in_pos)
|
||||
if src_pin.layer in ["m1","m2"]:
|
||||
if src_pin.layer in ["m1", "m2"]:
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=in_pos)
|
||||
|
||||
|
||||
def connect_hbus_m2m3(self, src_pin, dest_pin):
|
||||
"""
|
||||
Helper routine to connect an instance to a horizontal bus.
|
||||
Routes horizontal then vertical L shape.
|
||||
Dest pin is on M1/M2/M3.
|
||||
Src pin can be on M1/M2/M3.
|
||||
"""
|
||||
|
||||
if src_pin.cx()<dest_pin.cx():
|
||||
in_pos = src_pin.rc()
|
||||
else:
|
||||
in_pos = src_pin.lc()
|
||||
if src_pin.cy() < dest_pin.cy():
|
||||
out_pos = dest_pin.lc()
|
||||
else:
|
||||
out_pos = dest_pin.rc()
|
||||
|
||||
# move horizontal first
|
||||
self.add_wire(("m3", "via2", "m2"),
|
||||
[in_pos,
|
||||
vector(out_pos.x, in_pos.y),
|
||||
out_pos])
|
||||
if src_pin.layer=="m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=in_pos)
|
||||
if src_pin.layer in ["m1", "m2"]:
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=in_pos)
|
||||
if dest_pin.layer=="m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=out_pos)
|
||||
if dest_pin.layer=="m3":
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=out_pos)
|
||||
|
||||
def sp_write(self, sp_name):
|
||||
# Write the entire spice of the object to the file
|
||||
############################################################
|
||||
|
|
@ -571,40 +589,39 @@ class sram_base(design, verilog, lef):
|
|||
"""Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
|
||||
stage_effort_list = []
|
||||
|
||||
#Clk_buf originates from the control logic so only the bank is related to the wordline path
|
||||
external_wordline_cout = 0 #No loading on the wordline other than in the bank.
|
||||
# Clk_buf originates from the control logic so only the bank is related to the wordline path
|
||||
# No loading on the wordline other than in the bank.
|
||||
external_wordline_cout = 0
|
||||
stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
def get_wl_en_cin(self):
|
||||
"""Gets the capacitive load the of clock (clk_buf) for the sram"""
|
||||
#Only the wordline drivers within the bank use this signal
|
||||
# Only the wordline drivers within the bank use this signal
|
||||
return self.bank.get_wl_en_cin()
|
||||
|
||||
def get_w_en_cin(self):
|
||||
"""Gets the capacitive load the of write enable (w_en) for the sram"""
|
||||
#Only the write drivers within the bank use this signal
|
||||
# Only the write drivers within the bank use this signal
|
||||
return self.bank.get_w_en_cin()
|
||||
|
||||
|
||||
def get_p_en_bar_cin(self):
|
||||
"""Gets the capacitive load the of precharge enable (p_en_bar) for the sram"""
|
||||
#Only the precharges within the bank use this signal
|
||||
# Only the precharges within the bank use this signal
|
||||
return self.bank.get_p_en_bar_cin()
|
||||
|
||||
def get_clk_bar_cin(self):
|
||||
"""Gets the capacitive load the of clock (clk_buf_bar) for the sram"""
|
||||
#As clk_buf_bar is an output of the control logic. The cap for that module is not determined here.
|
||||
#Only the precharge cells use this signal (other than the control logic)
|
||||
# As clk_buf_bar is an output of the control logic. The cap for that module is not determined here.
|
||||
# Only the precharge cells use this signal (other than the control logic)
|
||||
return self.bank.get_clk_bar_cin()
|
||||
|
||||
def get_sen_cin(self):
|
||||
"""Gets the capacitive load the of sense amp enable for the sram"""
|
||||
#Only the sense_amps use this signal (other than the control logic)
|
||||
# Only the sense_amps use this signal (other than the control logic)
|
||||
return self.bank.get_sen_cin()
|
||||
|
||||
|
||||
def get_dff_clk_buf_cin(self):
|
||||
"""Get the relative capacitance of the clk_buf signal.
|
||||
Does not get the control logic loading but everything else"""
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from globals import OPTS
|
|||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
@unittest.skip("SKIPPING 20_psram_1bank_4mux_1rw_1r_test - Matt sucks, don't do this")
|
||||
class psram_1bank_4mux_1rw_1r_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
|
|||
Loading…
Reference in New Issue