Merge branch 'dev' into bisr

This commit is contained in:
Aditi Sinha 2020-03-22 21:58:04 +00:00
commit b75eeb7688
34 changed files with 1369 additions and 1394 deletions

2
.gitignore vendored
View File

@ -1,5 +1,7 @@
.DS_Store .DS_Store
*~ *~
*.orig
*.rej
*.pyc *.pyc
*.aux *.aux
*.out *.out

View File

@ -18,7 +18,7 @@ An open-source static random access memory (SRAM) compiler.
# What is OpenRAM? # What is OpenRAM?
<img align="right" width="25%" src="images/SCMOS_16kb_sram.jpg"> <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 netlists, timing and power models, placement and routing models, and
other views necessary to use SRAMs in ASIC design. OpenRAM supports other views necessary to use SRAMs in ASIC design. OpenRAM supports
integration in both commercial and open-source flows with both 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]. + Report bugs by submitting [Github issues].
+ Develop new features (see [how to contribute](./CONTRIBUTING.md)) + Develop new features (see [how to contribute](./CONTRIBUTING.md))
+ Submit code/fixes using a [Github pull request] + 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] + Read and cite our [ICCAD paper][OpenRAMpaper]
# Further Help # 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. - [Matthew Guthaus] from [VLSIDA] created the OpenRAM project and is the lead architect.
- [James Stine] from [VLSIARCH] co-founded the project. - [James Stine] from [VLSIARCH] co-founded the project.
- Hunter Nichols maintains and updates the timing characterization. - Many students: Hunter Nichols, Michael Grimes, Jennifer Sowash, Yusu Wang, Joey Kunzler, Jesse Cirimelli-Low, Samira Ataei, Bin Wu, Brian Chen, Jeff Butera
- 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.
If I forgot to add you, please let me know! 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 issues]: https://github.com/VLSIDA/OpenRAM/issues
[Github pull request]: https://github.com/VLSIDA/OpenRAM/pulls [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 [documentation]: https://docs.google.com/presentation/d/10InGB33N51I6oBHnqpU7_w9DXlx-qe9zdrlco2Yc5co/edit?usp=sharing
[dev-group]: mailto:openram-dev-group@ucsc.edu [dev-group]: mailto:openram-dev-group@ucsc.edu

View File

@ -116,6 +116,15 @@ class cell_properties():
self._dff_buff_array = _dff_buff_array(use_custom_ports = False, self._dff_buff_array = _dff_buff_array(use_custom_ports = False,
add_body_contacts = 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 @property
def bitcell(self): def bitcell(self):
return self._bitcell return self._bitcell
@ -132,3 +141,10 @@ class cell_properties():
def dff_buff_array(self): def dff_buff_array(self):
return self._dff_buff_array return self._dff_buff_array
@property
def write_driver(self):
return self._write_driver
@property
def sense_amp(self):
return self._sense_amp

View File

@ -329,6 +329,7 @@ class label(geometry):
debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text) debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text)
new_layout.addText(text=self.text, new_layout.addText(text=self.text,
layerNumber=self.layerNumber, layerNumber=self.layerNumber,
purposeNumber=self.layerPurpose,
offsetInMicrons=self.offset, offsetInMicrons=self.offset,
magnification=self.zoom, magnification=self.zoom,
rotate=None) rotate=None)

View File

@ -316,15 +316,16 @@ class layout():
You can optionally rename the pin to a new name. You can optionally rename the pin to a new name.
""" """
for pin_name in self.pin_map.keys(): for pin_name in self.pin_map.keys():
self.copy_layout_pin(instance, pin_name, prefix+pin_name) self.copy_layout_pin(instance, pin_name, prefix + pin_name)
def add_layout_pin_segment_center(self, text, layer, start, end): def add_layout_pin_segment_center(self, text, layer, start, end):
""" """
Creates a path like pin with center-line convention Creates a path like pin with center-line convention
""" """
if start.x != end.x and start.y != end.y:
debug.check(start.x == end.x or start.y == end.y, file_name = "non_rectilinear.gds"
"Cannot have a non-manhatten layout pin.") self.gds_write(file_name)
debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1)
minwidth_layer = drc["minwidth_{}".format(layer)] minwidth_layer = drc["minwidth_{}".format(layer)]
@ -516,12 +517,12 @@ class layout():
self.connect_inst([]) self.connect_inst([])
return inst return inst
def add_via_stack(self, offset, direction, from_layer, to_layer, def add_via_stack(self, offset, from_layer, to_layer,
size=[1,1]): direction=None,
size=[1, 1]):
""" """
Punch a stack of vias from a start layer to a target layer. Punch a stack of vias from a start layer to a target layer.
""" """
return self.__add_via_stack_internal(offset=offset, return self.__add_via_stack_internal(offset=offset,
direction=direction, direction=direction,
from_layer=from_layer, from_layer=from_layer,
@ -530,8 +531,9 @@ class layout():
last_via=None, last_via=None,
size=size) size=size)
def add_via_stack_center(self, offset, direction, from_layer, to_layer, def add_via_stack_center(self, offset, from_layer, to_layer,
size=[1,1]): direction=None,
size=[1, 1]):
""" """
Punch a stack of vias from a start layer to a target layer by the center Punch a stack of vias from a start layer to a target layer by the center
coordinate accounting for mirroring and rotation. coordinate accounting for mirroring and rotation.
@ -544,7 +546,6 @@ class layout():
last_via=None, last_via=None,
size=size) size=size)
def __add_via_stack_internal(self, offset, direction, from_layer, to_layer, def __add_via_stack_internal(self, offset, direction, from_layer, to_layer,
via_func, last_via, size): via_func, last_via, size):
""" """
@ -580,7 +581,6 @@ class layout():
last_via=via, last_via=via,
size=size) size=size)
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
"""Adds a ptx module to the design.""" """Adds a ptx module to the design."""
import ptx import ptx
@ -1201,10 +1201,16 @@ class layout():
the given center location. The starting layer is specified to determine the given center location. The starting layer is specified to determine
which vias are needed. which vias are needed.
""" """
# Force vdd/gnd via stack to be vertically or horizontally oriented
# Default: None, uses prefered metal directions
if vertical: if vertical:
direction = ("V", "V") direction = ("V", "V")
else: elif not vertical and vertical is not None:
direction = ("H", "H") direction = ("H", "H")
else:
direction = None
via = self.add_via_stack_center(from_layer=start_layer, via = self.add_via_stack_center(from_layer=start_layer,
to_layer=self.pwr_grid_layer, to_layer=self.pwr_grid_layer,
@ -1390,4 +1396,3 @@ class layout():
debug.info(0, "name={0} : mod={1} : offset={2}".format(inst.name, debug.info(0, "name={0} : mod={1} : offset={2}".format(inst.name,
inst.mod.name, inst.mod.name,
inst.offset)) inst.offset))

View File

@ -378,6 +378,7 @@ class pin_layout:
# imported into Magic. # imported into Magic.
newLayout.addText(text=self.name, newLayout.addText(text=self.name,
layerNumber=layer_num, layerNumber=layer_num,
purposeNumber=purpose,
offsetInMicrons=self.center(), offsetInMicrons=self.center(),
magnification=GDS["zoom"], magnification=GDS["zoom"],
rotate=None) rotate=None)

View File

@ -1,12 +1,5 @@
import math 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 GdsStructure:
"""Class represent a GDS Structure Object""" """Class represent a GDS Structure Object"""
def __init__(self): def __init__(self):
@ -147,7 +140,7 @@ class GdsText:
self.elementFlags="" self.elementFlags=""
self.plex="" self.plex=""
self.drawingLayer="" self.drawingLayer=""
self.purposeLayer=purposeLayer self.purposeLayer=0
self.transFlags=[0,0,0] self.transFlags=[0,0,0]
self.magFactor="" self.magFactor=""
self.rotateAngle="" self.rotateAngle=""

View File

@ -406,10 +406,11 @@ class VlsiLayout:
#add the sref to the root structure #add the sref to the root structure
self.structures[self.rootStructureName].paths.append(pathToAdd) 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])) offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
textToAdd = GdsText() textToAdd = GdsText()
textToAdd.drawingLayer = layerNumber textToAdd.drawingLayer = layerNumber
textToAdd.purposeLayer = purposeNumber
textToAdd.coordinates = [offsetInLayoutUnits] textToAdd.coordinates = [offsetInLayoutUnits]
textToAdd.transFlags = [0,0,0] textToAdd.transFlags = [0,0,0]
textToAdd.textString = self.padText(text) textToAdd.textString = self.padText(text)
@ -910,4 +911,3 @@ def boundaryArea(A):
""" """
area_A=(A[2]-A[0])*(A[3]-A[1]) area_A=(A[2]-A[0])*(A[3]-A[1])
return area_A return area_A

View File

@ -19,7 +19,7 @@ import re
import copy import copy
import importlib import importlib
VERSION = "1.1.3" VERSION = "1.1.4"
NAME = "OpenRAM v{}".format(VERSION) NAME = "OpenRAM v{}".format(VERSION)
USAGE = "openram.py [options] <config file>\nUse -h for help.\n" USAGE = "openram.py [options] <config file>\nUse -h for help.\n"

View File

@ -27,15 +27,15 @@ class bank(design.design):
self.sram_config = sram_config self.sram_config = sram_config
sram_config.set_local_config(self) sram_config.set_local_config(self)
if self.write_size: 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: else:
self.num_wmasks = 0 self.num_wmasks = 0
if name == "": if name == "":
name = "bank_{0}_{1}".format(self.word_size, self.num_words) name = "bank_{0}_{1}".format(self.word_size, self.num_words)
design.design.__init__(self, name) 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, # 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 # so this prefix will be added to all of the input signals to create
@ -47,11 +47,11 @@ class bank(design.design):
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: 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.create_layout()
self.add_boundary() self.add_boundary()
def create_netlist(self): def create_netlist(self):
self.compute_sizes() self.compute_sizes()
@ -59,42 +59,40 @@ class bank(design.design):
self.add_pins() # Must create the replica bitcell array first self.add_pins() # Must create the replica bitcell array first
self.create_instances() self.create_instances()
def create_layout(self): def create_layout(self):
self.place_instances() self.place_instances()
self.setup_routing_constraints() self.setup_routing_constraints()
self.route_layout() self.route_layout()
# Can remove the following, but it helps for debug! # 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 # 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_ur = self.bitcell_array_inst.ur()
self.bank_array_ul = self.bitcell_array_inst.ul() self.bank_array_ul = self.bitcell_array_inst.ul()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
""" Adding pins for Bank module""" """ Adding pins for Bank module"""
for port in self.read_ports: for port in self.read_ports:
for bit in range(self.word_size): 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: 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 port in self.write_ports:
for bit in range(self.word_size): 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 port in self.all_ports:
for bit in range(self.addr_size): 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 # For more than one bank, we have a bank select and name
# the signals gated_*. # the signals gated_*.
if self.num_banks > 1: if self.num_banks > 1:
for port in self.all_ports: 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: for port in self.read_ports:
self.add_pin("s_en{0}".format(port), "INPUT") self.add_pin("s_en{0}".format(port), "INPUT")
for port in self.all_ports: for port in self.all_ports:
@ -102,12 +100,11 @@ class bank(design.design):
for port in self.write_ports: for port in self.write_ports:
self.add_pin("w_en{0}".format(port), "INPUT") self.add_pin("w_en{0}".format(port), "INPUT")
for bit in range(self.num_wmasks): 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: for port in self.all_ports:
self.add_pin("wl_en{0}".format(port), "INPUT") self.add_pin("wl_en{0}".format(port), "INPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd", "GROUND")
def route_layout(self): def route_layout(self):
""" Create routing amoung the modules """ """ Create routing amoung the modules """
@ -124,18 +121,27 @@ class bank(design.design):
self.route_supplies() self.route_supplies()
def route_rbl(self,port): def route_rbl(self, port):
""" Route the rbl_bl and rbl_wl """ """ Route the rbl_bl and rbl_wl """
bl_pin_name = self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) 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) bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
self.add_layout_pin(text="rbl_bl{0}".format(port), # This will ensure the pin is only on the top or bottom edge
layer=bl_pin.layer, if port % 2:
offset=bl_pin.ll(), via_offset = bl_pin.uc() + vector(0, self.m2_pitch)
height=bl_pin.height(), left_right_offset = vector(self.max_x_offset, via_offset.y)
width=bl_pin.width()) 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): def route_bitlines(self, port):
""" Route the bitlines depending on the port type rw, w, or r. """ """ Route the bitlines depending on the port type rw, w, or r. """
@ -146,7 +152,6 @@ class bank(design.design):
self.route_port_data_out(port) self.route_port_data_out(port)
self.route_port_data_to_bitcell_array(port) self.route_port_data_to_bitcell_array(port)
def create_instances(self): def create_instances(self):
""" Create the instances of the netlist. """ """ Create the instances of the netlist. """
@ -161,12 +166,11 @@ class bank(design.design):
Compute the empty instance offsets for port0 and port1 (if needed) Compute the empty instance offsets for port0 and port1 (if needed)
""" """
self.port_data_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.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.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 center point for these cells are the upper-right corner of
# the bitcell array. # the bitcell array.
@ -186,7 +190,6 @@ class bank(design.design):
if len(self.all_ports)==2: if len(self.all_ports)==2:
self.compute_instance_port1_offsets() self.compute_instance_port1_offsets()
def compute_instance_port0_offsets(self): def compute_instance_port0_offsets(self):
""" """
Compute the instance offsets for port0 on the left/bottom of the bank. Compute the instance offsets for port0 on the left/bottom of the bank.
@ -196,16 +199,17 @@ class bank(design.design):
# UPPER RIGHT QUADRANT # UPPER RIGHT QUADRANT
# Bitcell array is placed at (0,0) # Bitcell array is placed at (0,0)
self.bitcell_array_offset = vector(0,0) self.bitcell_array_offset = vector(0, 0)
# LOWER RIGHT QUADRANT # LOWER RIGHT QUADRANT
# Below the bitcell array # 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 # UPPER LEFT QUADRANT
# To the left of the bitcell array # To the left of the bitcell array
x_offset = self.m2_gap + self.port_address.width x_offset = self.m2_gap + self.port_address.width
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)
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver # Place the col decoder left aligned with wordline driver
@ -215,10 +219,10 @@ class bank(design.design):
x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width x_offset = self.central_bus_width[port] + self.port_address.wordline_driver.width
if self.col_addr_size > 0: if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width 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: else:
y_offset = 0 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) # Bank select gets placed below the column decoder (x_offset doesn't change)
if self.col_addr_size > 0: if self.col_addr_size > 0:
@ -227,7 +231,7 @@ class bank(design.design):
y_offset = self.port_address_offsets[port].y y_offset = self.port_address_offsets[port].y
if self.num_banks > 1: if self.num_banks > 1:
y_offset += self.bank_select.height + drc("well_to_well") 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): def compute_instance_port1_offsets(self):
""" """
@ -246,7 +250,8 @@ class bank(design.design):
# LOWER RIGHT QUADRANT # LOWER RIGHT QUADRANT
# To the left of the bitcell array # To the left of the bitcell array
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap 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 # UPPER RIGHT QUADRANT
# Place the col decoder right aligned with wordline driver # Place the col decoder right aligned with wordline driver
@ -254,10 +259,10 @@ class bank(design.design):
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: if self.col_addr_size > 0:
x_offset += self.column_decoder.width + self.col_addr_bus_width 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: else:
y_offset = self.bitcell_array_top 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) # Bank select gets placed above the column decoder (x_offset doesn't change)
if self.col_addr_size > 0: 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) self.port_data[port].column_mux_offset.y + self.port_data[port].column_mux_array.height)
else: else:
y_offset = self.port_address_offsets[port].y 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): def place_instances(self):
""" Place the instances. """ """ Place the instances. """
@ -281,11 +286,10 @@ class bank(design.design):
self.place_column_decoder(self.column_decoder_offsets) self.place_column_decoder(self.column_decoder_offsets)
self.place_bank_select(self.bank_select_offsets) self.place_bank_select(self.bank_select_offsets)
def compute_sizes(self): def compute_sizes(self):
""" Computes the required sizes to create the bank """ """ 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_temp = int(self.num_words / self.words_per_row)
self.num_rows = self.num_rows_temp + self.num_spare_rows 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.col_addr_size = int(log(self.words_per_row, 2))
self.addr_size = self.col_addr_size + self.row_addr_size 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.num_rows_temp * self.num_cols==self.word_size * self.num_words,
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,"Invalid address break down.") "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: # The order of the control signals on the control bus:
self.input_control_signals = [] 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] 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 # 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. # These will be outputs of the gaters if this is multibank, if not, normal signals.
self.control_signals = [] self.control_signals = []
for port in self.all_ports: for port in self.all_ports:
if self.num_banks > 1: 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: else:
self.control_signals.append(self.input_control_signals[port]) self.control_signals.append(self.input_control_signals[port])
@ -328,12 +334,11 @@ class bank(design.design):
self.num_col_addr_lines = 2**self.col_addr_size self.num_col_addr_lines = 2**self.col_addr_size
else: else:
self.num_col_addr_lines = 0 self.num_col_addr_lines = 0
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
# A space for wells or jogging m2 # A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"), self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch) 3 * self.m2_pitch)
def add_modules(self): def add_modules(self):
""" Add all the modules using the class loader """ """ Add all the modules using the class loader """
@ -353,13 +358,11 @@ class bank(design.design):
self.port_data.append(temp_pre) self.port_data.append(temp_pre)
self.add_mod(self.port_data[port]) self.add_mod(self.port_data[port])
self.port_address = factory.create(module_type="port_address", self.port_address = factory.create(module_type="port_address",
cols=self.num_cols, cols=self.num_cols,
rows=self.num_rows) rows=self.num_rows)
self.add_mod(self.port_address) self.add_mod(self.port_address)
self.port_rbl_map = self.all_ports self.port_rbl_map = self.all_ports
self.num_rbl = len(self.all_ports) self.num_rbl = len(self.all_ports)
@ -371,12 +374,10 @@ class bank(design.design):
bitcell_ports=self.all_ports) bitcell_ports=self.all_ports)
self.add_mod(self.bitcell_array) self.add_mod(self.bitcell_array)
if(self.num_banks > 1): if(self.num_banks > 1):
self.bank_select = factory.create(module_type="bank_select") self.bank_select = factory.create(module_type="bank_select")
self.add_mod(self.bank_select) self.add_mod(self.bank_select)
def create_bitcell_array(self): def create_bitcell_array(self):
""" Creating Bitcell Array """ """ Creating Bitcell Array """
@ -386,7 +387,7 @@ class bank(design.design):
temp = [] temp = []
for col in range(self.num_cols): for col in range(self.num_cols):
for bitline in self.bitline_names: 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): for rbl in range(self.num_rbl):
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl) rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl)
temp.append(rbl_bl_name) temp.append(rbl_bl_name)
@ -394,23 +395,21 @@ class bank(design.design):
temp.append(rbl_br_name) temp.append(rbl_br_name)
for row in range(self.num_rows): for row in range(self.num_rows):
for wordline in self.wl_names: 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: for port in self.all_ports:
temp.append("wl_en{0}".format(port)) temp.append("wl_en{0}".format(port))
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_bitcell_array(self, offset): def place_bitcell_array(self, offset):
""" Placing Bitcell Array """ """ Placing Bitcell Array """
self.bitcell_array_inst.place(offset) self.bitcell_array_inst.place(offset)
def create_port_data(self): def create_port_data(self):
""" Creating Port Data """ """ 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: for port in self.all_ports:
self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port), self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port),
mod=self.port_data[port]) mod=self.port_data[port])
@ -421,16 +420,16 @@ class bank(design.design):
temp.append(rbl_bl_name) temp.append(rbl_bl_name)
temp.append(rbl_br_name) temp.append(rbl_br_name)
for col in range(self.num_cols): for col in range(self.num_cols):
temp.append("{0}_{1}".format(self.bl_names[port],col)) temp.append("{0}_{1}".format(self.bl_names[port], col))
temp.append("{0}_{1}".format(self.br_names[port],col)) temp.append("{0}_{1}".format(self.br_names[port], col))
if port in self.read_ports: if port in self.read_ports:
for bit in range(self.word_size): 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: if port in self.write_ports:
for bit in range(self.word_size): 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 # 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) temp.extend(sel_names)
if port in self.read_ports: if port in self.read_ports:
temp.append("s_en{0}".format(port)) temp.append("s_en{0}".format(port))
@ -439,17 +438,16 @@ class bank(design.design):
temp.append("w_en{0}".format(port)) temp.append("w_en{0}".format(port))
for bit in range(self.num_wmasks): for bit in range(self.num_wmasks):
temp.append("bank_wmask{0}_{1}".format(port, bit)) temp.append("bank_wmask{0}_{1}".format(port, bit))
temp.extend(["vdd","gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_port_data(self, offsets): def place_port_data(self, offsets):
""" Placing Port Data """ """ Placing Port Data """
for port in self.all_ports: for port in self.all_ports:
# Top one is unflipped, bottom is flipped along X direction # Top one is unflipped, bottom is flipped along X direction
if port%2 == 1: if port % 2 == 1:
mirror = "R0" mirror = "R0"
else: else:
mirror = "MX" mirror = "MX"
@ -458,21 +456,20 @@ class bank(design.design):
def create_port_address(self): def create_port_address(self):
""" Create the hierarchical row decoder """ """ 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: 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) mod=self.port_address)
temp = [] temp = []
for bit in range(self.row_addr_size): 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)) temp.append("wl_en{0}".format(port))
for row in range(self.num_rows): 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"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_port_address(self, offsets): def place_port_address(self, offsets):
""" Place the hierarchical row decoder """ """ Place the hierarchical row decoder """
@ -485,13 +482,12 @@ class bank(design.design):
# The address flop and decoder are aligned in the x coord. # The address flop and decoder are aligned in the x coord.
for port in self.all_ports: for port in self.all_ports:
if port%2: if port % 2:
mirror = "MY" mirror = "MY"
else: else:
mirror = "R0" mirror = "R0"
self.port_address_inst[port].place(offset=offsets[port], mirror=mirror) self.port_address_inst[port].place(offset=offsets[port], mirror=mirror)
def create_column_decoder(self): def create_column_decoder(self):
""" """
Create a 2:4 or 3:8 column address decoder. Create a 2:4 or 3:8 column address decoder.
@ -511,23 +507,22 @@ class bank(design.design):
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height) self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height)
else: else:
# No error checking before? # No error checking before?
debug.error("Invalid column decoder?",-1) debug.error("Invalid column decoder?", -1)
self.add_mod(self.column_decoder) 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: 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) mod=self.column_decoder)
temp = [] temp = []
for bit in range(self.col_addr_size): 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): 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"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_column_decoder(self, offsets): def place_column_decoder(self, offsets):
""" """
Place a 2:4 or 3:8 column address decoder. Place a 2:4 or 3:8 column address decoder.
@ -535,24 +530,23 @@ class bank(design.design):
if self.col_addr_size == 0: if self.col_addr_size == 0:
return 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: for port in self.all_ports:
if port%2 == 1: if port % 2 == 1:
mirror = "XY" mirror = "XY"
else: else:
mirror = "R0" mirror = "R0"
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror) self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
def create_bank_select(self): def create_bank_select(self):
""" Create the bank select logic. """ """ Create the bank select logic. """
if not self.num_banks > 1: if not self.num_banks > 1:
return 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: for port in self.all_ports:
self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port), self.bank_select_inst[port] = self.add_inst(name="bank_select{}".format(port),
mod=self.bank_select) mod=self.bank_select)
@ -564,24 +558,23 @@ class bank(design.design):
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_bank_select(self, offsets): def place_bank_select(self, offsets):
""" Place the bank select logic. """ """ Place the bank select logic. """
if not self.num_banks > 1: if not self.num_banks > 1:
return 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: for port in self.all_ports:
self.bank_select_inst[port].place(offsets[port]) self.bank_select_inst[port].place(offsets[port])
def route_supplies(self): def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst,"gnd") self.copy_power_pins(inst, "gnd")
def route_bank_select(self, port): def route_bank_select(self, port):
""" Route the bank select logic. """ """ 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"] 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"] 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)): 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]) self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
@ -605,7 +598,7 @@ class bank(design.design):
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc() out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
name = self.control_signals[port][signal] name = self.control_signals[port][signal]
bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y) 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, self.add_via_center(layers=self.m2_stack,
offset=bus_pos) offset=bus_pos)
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
@ -613,17 +606,16 @@ class bank(design.design):
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=out_pos) offset=out_pos)
def setup_routing_constraints(self): def setup_routing_constraints(self):
""" """
After the modules are instantiated, find the dimensions for the 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.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]) self.min_x_offset = min([x.lx() for x in self.insts])
# # Create the core bbox for the power rings # # Create the core bbox for the power rings
@ -634,20 +626,19 @@ class bank(design.design):
self.height = ur.y - ll.y self.height = ur.y - ll.y
self.width = ur.x - ll.x self.width = ur.x - ll.x
def route_central_bus(self): def route_central_bus(self):
""" Create the address, supply, and control signal central bus lines. """ """ Create the address, supply, and control signal central bus lines. """
# Overall central bus width. It includes all the column mux lines, # Overall central bus width. It includes all the column mux lines,
# and control lines. # and control lines.
self.bus_xoffset = [None]*len(self.all_ports) self.bus_xoffset = [None] * len(self.all_ports)
# Port 0 # Port 0
# The bank is at (0,0), so this is to the left of the y-axis. # 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) 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 # 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", self.bus_xoffset[0] = self.create_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=control_bus_offset, offset=control_bus_offset,
@ -659,7 +650,7 @@ class bank(design.design):
# Port 1 # Port 1
if len(self.all_ports)==2: if len(self.all_ports)==2:
# The other control bus is routed up to two pitches above the bitcell array # 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, control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch,
self.max_y_offset - control_bus_length) 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 # The bus for the right port is reversed so that the rbl_wl is closest to the array
@ -671,22 +662,25 @@ class bank(design.design):
vertical=True, vertical=True,
make_pins=(self.num_banks==1)) make_pins=(self.num_banks==1))
def route_port_data_to_bitcell_array(self, port): def route_port_data_to_bitcell_array(self, port):
""" Routing of BL and BR between port data and bitcell array """ """ Routing of BL and BR between port data and bitcell array """
# Connect the regular bitlines # Connect the regular bitlines
inst2 = self.port_data_inst[port] inst2 = self.port_data_inst[port]
inst1 = self.bitcell_array_inst inst1 = self.bitcell_array_inst
inst1_bl_name = self.bl_names[port]+"_{}" inst1_bl_name = self.bl_names[port] + "_{}"
inst1_br_name = self.br_names[port]+"_{}" inst1_br_name = self.br_names[port] + "_{}"
inst2_bl_name = inst2.mod.get_bl_names()+"_{}" inst2_bl_name = inst2.mod.get_bl_names() + "_{}"
inst2_br_name = inst2.mod.get_br_names()+"_{}" inst2_br_name = inst2.mod.get_br_names() + "_{}"
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols, self.connect_bitlines(inst1=inst1,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst2=inst2,
inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name) 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 # Connect the replica bitlines
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) 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_bl_name, "rbl_bl")
self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br") self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br")
def route_port_data_out(self, port): def route_port_data_out(self, port):
""" Add pins for the port data out """ """ Add pins for the port data out """
for bit in range(self.word_size): for bit in range(self.word_size):
data_pin = self.port_data_inst[port].get_pin("dout_{0}".format(bit)) 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), self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port, bit),
layer=data_pin.layer, layer=data_pin.layer,
offset=data_pin.center(), offset=data_pin.center(),
height=data_pin.height(), height=data_pin.height(),
width=data_pin.width()) width=data_pin.width())
def route_port_address_in(self, port): def route_port_address_in(self, port):
""" Routes the row decoder inputs and supplies """ """ Routes the row decoder inputs and supplies """
@ -715,17 +706,15 @@ class bank(design.design):
for row in range(self.row_addr_size): for row in range(self.row_addr_size):
addr_idx = row + self.col_addr_size addr_idx = row + self.col_addr_size
decoder_name = "addr_{}".format(row) 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) self.copy_layout_pin(self.port_address_inst[port], decoder_name, addr_name)
def route_port_data_in(self, port): def route_port_data_in(self, port):
""" Connecting port data in """ """ Connecting port data in """
for row in range(self.word_size): for row in range(self.word_size):
data_name = "din_{}".format(row) 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) self.copy_layout_pin(self.port_data_inst[port], data_name, din_name)
if self.word_size: if self.word_size:
@ -734,8 +723,6 @@ class bank(design.design):
bank_wmask_name = "bank_wmask{0}_{1}".format(port, 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) self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
def channel_route_bitlines(self, inst1, inst2, num_bits, def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"): inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
@ -752,10 +739,9 @@ class bank(design.design):
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name) (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) (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 # Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once! # of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch) offset = bottom_inst.ul() + vector(0, self.m1_pitch)
for bit in range(num_bits): 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))] 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))]
@ -785,10 +771,12 @@ class bank(design.design):
bottom_loc = bottom_pin.uc() bottom_loc = bottom_pin.uc()
top_loc = top_pin.bc() top_loc = top_pin.bc()
yoffset = 0.5*(top_loc.y+bottom_loc.y) yoffset = 0.5 * (top_loc.y + bottom_loc.y)
self.add_path(top_pin.layer,[bottom_loc, vector(bottom_loc.x,yoffset), self.add_path(top_pin.layer,
vector(top_loc.x,yoffset), top_loc]) [bottom_loc,
vector(bottom_loc.x, yoffset),
vector(top_loc.x, yoffset),
top_loc])
def connect_bitlines(self, inst1, inst2, num_bits, def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name, inst1_br_name, inst1_bl_name, inst1_br_name,
@ -801,13 +789,12 @@ class bank(design.design):
self.connect_bitline(inst1, inst2, inst1_bl_name.format(col), inst2_bl_name.format(col)) 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)) self.connect_bitline(inst1, inst2, inst1_br_name.format(col), inst2_br_name.format(col))
def route_port_address(self, port): def route_port_address(self, port):
""" Connect Wordline driver to bitcell array wordline """ """ Connect Wordline driver to bitcell array wordline """
self.route_port_address_in(port) self.route_port_address_in(port)
if port%2: if port % 2:
self.route_port_address_right(port) self.route_port_address_right(port)
else: else:
self.route_port_address_left(port) self.route_port_address_left(port)
@ -818,21 +805,20 @@ class bank(design.design):
for row in range(self.num_rows): for row in range(self.num_rows):
# The mid guarantees we exit the input cell to the right. # The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).rc() driver_wl_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() 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) mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].rx() + 0.5 * self.bitcell_array_inst.lx(), 0)
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0.5,1) mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0.5, 1)
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_port_address_right(self, port): def route_port_address_right(self, port):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
for row in range(self.num_rows): for row in range(self.num_rows):
# The mid guarantees we exit the input cell to the right. # The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.port_address_inst[port].get_pin("wl_{}".format(row)).lc() driver_wl_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() 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) mid1 = driver_wl_pos.scale(0, 1) + vector(0.5 * self.port_address_inst[port].lx() + 0.5 * self.bitcell_array_inst.rx(), 0)
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0,1) mid2 = mid1.scale(1, 0) + bitcell_wl_pos.scale(0, 1)
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_column_address_lines(self, port): def route_column_address_lines(self, port):
@ -855,11 +841,11 @@ class bank(design.design):
for i in range(self.col_addr_size): for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i) 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) self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
if port%2: if port % 2:
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0) offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_pitch, 0)
else: else:
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0) 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) self.create_vertical_channel_route(route_map, offset, self.m1_stack)
def add_lvs_correspondence_points(self): 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 These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist. will show these as ports in the extracted netlist.
""" """
@ -944,57 +931,58 @@ class bank(design.design):
for (control_signal, pin_pos) in connection: for (control_signal, pin_pos) in connection:
control_mid_pos = self.bus_xoffset[port][control_signal] 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_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=control_pos) offset=control_pos)
# clk to wordline_driver # clk to wordline_driver
control_signal = self.prefix+"wl_en{}".format(port) control_signal = self.prefix + "wl_en{}".format(port)
if port%2: if port % 2:
pin_pos = self.port_address_inst[port].get_pin("wl_en").uc() 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: else:
pin_pos = self.port_address_inst[port].get_pin("wl_en").bc() 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_x_offset = self.bus_xoffset[port][control_signal].x
control_pos = vector(control_x_offset, mid_pos.y) 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, self.add_via_center(layers=self.m1_stack,
offset=control_pos) offset=control_pos)
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): 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""" """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 = [] stage_effort_list = []
wordline_cout = self.bitcell_array.get_wordline_cin() + external_cout 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 return stage_effort_list
def get_wl_en_cin(self): def get_wl_en_cin(self):
"""Get the relative capacitance of all the clk connections in the bank""" """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() return self.port_address.wordline_driver.get_wl_en_cin()
def get_w_en_cin(self): def get_w_en_cin(self):
"""Get the relative capacitance of all the clk connections in the bank""" """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.
port = self.write_ports[0] port = self.write_ports[0]
return self.port_data[port].write_driver.get_w_en_cin() return self.port_data[port].write_driver.get_w_en_cin()
def get_clk_bar_cin(self): def get_clk_bar_cin(self):
"""Get the relative capacitance of all the clk_bar connections in the bank""" """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] port = self.read_ports[0]
return self.port_data[port].precharge_array.get_en_cin() return self.port_data[port].precharge_array.get_en_cin()
def get_sen_cin(self): def get_sen_cin(self):
"""Get the relative capacitance of all the sense amp enable connections in the bank""" """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. # Current bank only uses sen as an enable for the sense amps.
port = self.read_ports[0] port = self.read_ports[0]
return self.port_data[port].sense_amp_array.get_en_cin() return self.port_data[port].sense_amp_array.get_en_cin()
@ -1006,5 +994,7 @@ class bank(design.design):
def get_cell_name(self, inst_name, row, col): def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell.""" """Gets the spice name of the target bitcell."""
return self.bitcell_array_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)

View File

@ -101,13 +101,20 @@ class bitcell_base_array(design.design):
width=self.width, width=self.width,
height=wl_pin.height()) height=wl_pin.height())
# For every second row and column, add a via for gnd and vdd # For 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 row in range(self.row_size):
for col in range(self.column_size): for col in range(self.column_size):
inst = self.cell_inst[row,col] inst = self.cell_inst[row,col]
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name): for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin_name, 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): def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset tempx = xoffset

View File

@ -20,8 +20,7 @@ class hierarchical_decoder(design.design):
def __init__(self, name, rows): def __init__(self, name, rows):
design.design.__init__(self, name) design.design.__init__(self, name)
self.NAND_FORMAT = "DEC_NAND_{0}" self.AND_FORMAT = "DEC_AND_{0}"
self.INV_FORMAT = "DEC_INV_{0}"
self.pre2x4_inst = [] self.pre2x4_inst = []
self.pre3x8_inst = [] self.pre3x8_inst = []
@ -58,12 +57,12 @@ class hierarchical_decoder(design.design):
self.inv = factory.create(module_type="pinv", self.inv = factory.create(module_type="pinv",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.inv) 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) height=self.cell_height)
self.add_mod(self.nand2) self.add_mod(self.and3)
self.nand3 = factory.create(module_type="pnand3",
height=self.cell_height)
self.add_mod(self.nand3)
self.add_decoders() self.add_decoders()
@ -77,27 +76,27 @@ class hierarchical_decoder(design.design):
height=self.cell_height) height=self.cell_height)
self.add_mod(self.pre3_8) 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 """ Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
needed based on the number of inputs """ needed based on the number of inputs """
if (num_inputs == 2): if (num_inputs == 2):
return (1,0) return (1, 0)
elif (num_inputs == 3): elif (num_inputs == 3):
return(0,1) return(0, 1)
elif (num_inputs == 4): elif (num_inputs == 4):
return(2,0) return(2, 0)
elif (num_inputs == 5): elif (num_inputs == 5):
return(1,1) return(1, 1)
elif (num_inputs == 6): elif (num_inputs == 6):
return(3,0) return(3, 0)
elif (num_inputs == 7): elif (num_inputs == 7):
return(2,1) return(2, 1)
elif (num_inputs == 8): elif (num_inputs == 8):
return(1,2) return(1, 2)
elif (num_inputs == 9): elif (num_inputs == 9):
return(0,3) return(0, 3)
else: 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): def setup_netlist_constants(self):
self.predec_groups = [] # This array is a 2D array. self.predec_groups = [] # This array is a 2D array.
@ -122,16 +121,16 @@ class hierarchical_decoder(design.design):
index = index + 1 index = index + 1
self.predec_groups.append(lines) self.predec_groups.append(lines)
def setup_layout_constants(self): def setup_layout_constants(self):
""" Calculate the overall dimensions of the hierarchical decoder """ """ Calculate the overall dimensions of the hierarchical decoder """
# If we have 4 or fewer rows, the predecoder is the decoder itself # If we have 4 or fewer rows, the predecoder is the decoder itself
if self.num_inputs>=4: 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: else:
self.total_number_of_predecoder_outputs = 0 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) 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, # Calculates height and width of pre-decoder,
if self.no_of_pre3x8 > 0: if self.no_of_pre3x8 > 0:
@ -139,17 +138,17 @@ class hierarchical_decoder(design.design):
else: else:
self.predecoder_width = self.pre2_4.width self.predecoder_width = self.pre2_4.width
self.predecoder_height = self.pre2_4.height*self.no_of_pre2x4 + self.pre3_8.height*self.no_of_pre3x8 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): if (self.num_inputs == 4 or self.num_inputs == 5):
nand_width = self.nand2.width nand_width = self.and2.width
else: else:
nand_width = self.nand3.width nand_width = self.and3.width
self.internal_routing_width = self.m2_pitch*self.total_number_of_predecoder_outputs self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs
self.row_decoder_height = self.inv.height * self.rows self.row_decoder_height = self.inv.height * self.rows
self.input_routing_width = (self.num_inputs+1) * self.m2_pitch self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
# Calculates height and width of hierarchical decoder # Calculates height and width of hierarchical decoder
self.height = self.row_decoder_height self.height = self.row_decoder_height
self.width = self.input_routing_width + self.predecoder_width \ self.width = self.input_routing_width + self.predecoder_width \
@ -158,7 +157,7 @@ class hierarchical_decoder(design.design):
def route_input_rails(self): def route_input_rails(self):
""" Create input rails for the predecoders """ """ Create input rails for the predecoders """
# inputs should be as high as the decoders # 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 # Find the left-most predecoder
min_x = 0 min_x = 0
@ -166,7 +165,7 @@ class hierarchical_decoder(design.design):
min_x = min(min_x, -self.pre2_4.width) min_x = min(min_x, -self.pre2_4.width)
if self.no_of_pre3x8 > 0: if self.no_of_pre3x8 > 0:
min_x = min(min_x, -self.pre3_8.width) 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)] input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="m2", self.input_rails = self.create_vertical_pin_bus(layer="m2",
@ -177,7 +176,6 @@ class hierarchical_decoder(design.design):
self.route_input_to_predecodes() self.route_input_to_predecodes()
def route_input_to_predecodes(self): def route_input_to_predecodes(self):
""" Route the vertical input rail to the predecoders """ """ Route the vertical input rail to the predecoders """
for pre_num in range(self.no_of_pre2x4): for pre_num in range(self.no_of_pre2x4):
@ -191,12 +189,11 @@ class hierarchical_decoder(design.design):
# To prevent conflicts, we will offset each input connect so # To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails # that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height) decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_rail(decoder_offset, input_offset) self.route_input_rail(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
for i in range(3): for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2 index = pre_num * 3 + i + self.no_of_pre2x4 * 2
@ -208,12 +205,11 @@ class hierarchical_decoder(design.design):
# To prevent conflicts, we will offset each input connect so # To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails # that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height) decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_rail(decoder_offset, input_offset) self.route_input_rail(decoder_offset, input_offset)
def route_input_rail(self, input_offset, output_offset): def route_input_rail(self, input_offset, output_offset):
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ """ Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
@ -223,7 +219,6 @@ class hierarchical_decoder(design.design):
offset=output_offset) offset=output_offset)
self.add_path(("m3"), [input_offset, output_offset]) self.add_path(("m3"), [input_offset, output_offset])
def add_pins(self): def add_pins(self):
""" Add the module pins """ """ Add the module pins """
@ -235,7 +230,6 @@ class hierarchical_decoder(design.design):
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def create_pre_decoder(self): def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """ """ 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): for i in range(self.no_of_pre3x8):
self.create_pre3x8(i) self.create_pre3x8(i)
def create_pre2x4(self,num): def create_pre2x4(self, num):
""" Add a 2x4 predecoder to the left of the origin """ """ Add a 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2): if (self.num_inputs == 2):
@ -265,8 +259,7 @@ class hierarchical_decoder(design.design):
mod=self.pre2_4)) mod=self.pre2_4))
self.connect_inst(pins) 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 """ """ 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 # If we had 2x4 predecodes, those are used as the lower
# decode output bits # decode output bits
@ -284,7 +277,6 @@ class hierarchical_decoder(design.design):
mod=self.pre3_8)) mod=self.pre3_8))
self.connect_inst(pins) self.connect_inst(pins)
def place_pre_decoder(self): def place_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """ """ Creates pre-decoder and places labels input address [A] """
@ -294,189 +286,125 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8): for i in range(self.no_of_pre3x8):
self.place_pre3x8(i) self.place_pre3x8(i)
def place_pre2x4(self,num): def place_pre2x4(self, num):
""" Place 2x4 predecoder to the left of the origin """ """ Place 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2): if (self.num_inputs == 2):
base = vector(-self.pre2_4.width,0) base = vector(-self.pre2_4.width, 0)
else: else:
base= vector(-self.pre2_4.width, num * self.pre2_4.height) base= vector(-self.pre2_4.width, num * self.pre2_4.height)
self.pre2x4_inst[num].place(base) self.pre2x4_inst[num].place(base)
def place_pre3x8(self,num): def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3): if (self.num_inputs == 3):
offset = vector(-self.pre_3_8.width,0) offset = vector(-self.pre_3_8.width, 0)
mirror ="R0" mirror = "R0"
else: else:
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
offset = vector(-self.pre3_8.width, height) offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset) self.pre3x8_inst[num].place(offset)
def create_row_decoder(self): 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. """ and add the primary decoder output pins. """
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
self.create_decoder_nand_array() self.create_decoder_and_array()
self.create_decoder_inv_array()
def create_decoder_nand_array(self): def create_decoder_and_array(self):
""" Add a column of NAND gates for final decode """ """ 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): if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): 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): if (row < self.rows):
name = self.NAND_FORMAT.format(row) name = self.AND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.nand2)) mod=self.and2))
pins =["out_{0}".format(i), pins =["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(j + len(self.predec_groups[0])),
"Z_{0}".format(row), "decode_{0}".format(row),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(pins) self.connect_inst(pins)
# Row Decoder AND GATE array for address inputs >5.
# Row Decoder NAND GATE array for address inputs >5.
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])): for k in range(len(self.predec_groups[2])):
row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \ row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0])*j + i + len(self.predec_groups[0]) * j + i
if (row < self.rows): if (row < self.rows):
name = self.NAND_FORMAT.format(row) name = self.AND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.nand3)) mod=self.and3))
pins = ["out_{0}".format(i), pins = ["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(j + len(self.predec_groups[0])),
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), "out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
"Z_{0}".format(row), "decode_{0}".format(row),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(pins) 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): def place_row_decoder(self):
""" """
Place the row-decoder by placing NAND2/NAND3 and Inverters Place the row-decoder by placing AND2/AND3 and Inverters
and add the primary decoder output pins. and add the primary decoder output pins.
""" """
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
self.place_decoder_nand_array() self.place_decoder_and_array()
self.place_decoder_inv_array()
self.route_decoder() self.route_decoder()
def place_decoder_and_array(self):
""" Add a column of AND gates for final decode """
def place_decoder_nand_array(self): # Row Decoder AND GATE array for address inputs <5.
""" Add a column of NAND gates for final decode """
# Row Decoder NAND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_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?) # FIXME: why this correct offset?)
elif (self.num_inputs > 5): 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): def place_and_array(self, and_mod):
""" Add a column of NAND gates for the decoder above the predecoders.""" """ Add a column of AND gates for the decoder above the predecoders."""
for row in range(self.rows): for row in range(self.rows):
name = self.NAND_FORMAT.format(row)
if ((row % 2) == 0): if ((row % 2) == 0):
y_off = nand_mod.height*row y_off = and_mod.height * row
y_dir = 1
mirror = "R0" mirror = "R0"
else: else:
y_off = nand_mod.height*(row + 1) y_off = and_mod.height * (row + 1)
y_dir = -1
mirror = "MX" mirror = "MX"
self.nand_inst[row].place(offset=[self.internal_routing_width, y_off], self.and_inst[row].place(offset=[self.internal_routing_width, y_off],
mirror=mirror) mirror=mirror)
def route_decoder(self): 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): for row in range(self.rows):
z_pin = self.and_inst[row].get_pin("Z")
# 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")
self.add_layout_pin(text="decode_{0}".format(row), self.add_layout_pin(text="decode_{0}".format(row),
layer="m1", layer="m1",
offset=z_pin.ll(), offset=z_pin.ll(),
width=z_pin.width(), width=z_pin.width(),
height=z_pin.height()) height=z_pin.height())
def route_predecode_rails(self): def route_predecode_rails(self):
""" Creates vertical metal 2 rails to connect predecoder and decoder stages.""" """ 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. # This is not needed for inputs <4 since they have no pre/decode stages.
if (self.num_inputs >= 4): 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)] 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", self.predecode_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
@ -484,7 +412,6 @@ class hierarchical_decoder(design.design):
names=input_bus_names, names=input_bus_names,
length=self.height) length=self.height)
self.route_rails_to_predecodes() self.route_rails_to_predecodes()
self.route_rails_to_decoder() self.route_rails_to_decoder()
@ -499,7 +426,6 @@ class hierarchical_decoder(design.design):
pin = self.pre2x4_inst[pre_num].get_pin(out_name) 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 # FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
for i in range(8): for i in range(8):
@ -508,15 +434,13 @@ class hierarchical_decoder(design.design):
pin = self.pre3x8_inst[pre_num].get_pin(out_name) 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): def route_rails_to_decoder(self):
""" Use the self.predec_groups to determine the connections to the decoder NAND gates. """ Use the self.predec_groups to determine the connections to the decoder AND gates.
Inputs of NAND2/NAND3 gates come from different groups. Inputs of AND2/AND3 gates come from different groups.
For example for these groups [ [0,1,2,3] ,[4,5,6,7], 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 [8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
[0,4,8] and second NAND3 is connected to [0,4,9] ........... and the [0,4,8] and second AND3 is connected to [0,4,9] ........... and the
128th NAND3 is connected to [3,7,15] 128th AND3 is connected to [3,7,15]
""" """
row_index = 0 row_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
@ -525,9 +449,9 @@ class hierarchical_decoder(design.design):
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
if (row_index < self.rows): if (row_index < self.rows):
predecode_name = "predecode_{}".format(index_A) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
predecode_name = "predecode_{}".format(index_B) 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("B"))
row_index = row_index + 1 row_index = row_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
@ -537,60 +461,47 @@ class hierarchical_decoder(design.design):
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
if (row_index < self.rows): if (row_index < self.rows):
predecode_name = "predecode_{}".format(index_A) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
predecode_name = "predecode_{}".format(index_B) 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("B"))
predecode_name = "predecode_{}".format(index_C) predecode_name = "predecode_{}".format(index_C)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C")) self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C"))
row_index = row_index + 1 row_index = row_index + 1
def route_vdd_gnd(self): def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """ """ Add a pin for each row of vdd/gnd which are must-connects next level up. """
# The vias will be placed in the center and right of the cells, respectively. # The vias will be placed in the center and right of the cells, respectively.
xoffset = self.nand_inst[0].rx() xoffset = self.and_inst[0].rx()
for num in range(0,self.rows): for num in range(0, self.rows):
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
# The nand and inv are the same height rows... # 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()) pin_pos = vector(xoffset, supply_pin.cy())
self.add_power_pin(name=pin_name, self.add_power_pin(name=pin_name,
loc=pin_pos) 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 # Copy the pins from the predecoders
for pre in self.pre2x4_inst + self.pre3x8_inst: for pre in self.pre2x4_inst + self.pre3x8_inst:
self.copy_layout_pin(pre, "vdd") self.copy_layout_pin(pre, "vdd")
self.copy_layout_pin(pre, "gnd") self.copy_layout_pin(pre, "gnd")
def route_predecode_rail(self, rail_name, pin): def route_predecode_rail(self, rail_name, pin):
""" Connect the routing rail to the given metal1 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_path("m1", [rail_pos, pin.lc()])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=rail_pos) offset=rail_pos)
def route_predecode_rail_m3(self, rail_name, pin): def route_predecode_rail_m3(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """ """ Connect the routing rail to the given metal1 pin """
# This routes the pin up to the rail, basically, to avoid conflicts. # This routes the pin up to the rail, basically, to avoid conflicts.
# It would be fixed with a channel router. # It would be fixed with a channel router.
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2) mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2)
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y) rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y)
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=pin.center()) 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, self.add_via_center(layers=self.m2_stack,
offset=rail_pos) offset=rail_pos)

View File

@ -32,61 +32,58 @@ class hierarchical_predecode(design.design):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): 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", self.inv = factory.create(module_type="pinv",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.inv) self.add_mod(self.inv)
self.add_nand(self.number_of_inputs) self.add_and(self.number_of_inputs)
self.add_mod(self.nand) self.add_mod(self.and_mod)
def add_nand(self, inputs): def add_and(self, inputs):
""" Create the NAND for the predecode input stage """ """ Create the NAND for the predecode input stage """
if inputs==2: if inputs==2:
self.nand = factory.create(module_type="pnand2", self.and_mod = factory.create(module_type="pand2",
height=self.cell_height) height=self.cell_height)
elif inputs==3: elif inputs==3:
self.nand = factory.create(module_type="pnand3", self.and_mod = factory.create(module_type="pand3",
height=self.cell_height) height=self.cell_height)
else: else:
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1) debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
def setup_layout_constraints(self): 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 # x offset for input inverters
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch 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 # x offset to AND 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 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 # x offset to output inverters
self.x_off_inv_2 = self.x_off_nand + self.nand.width self.width = self.x_off_and + self.and_mod.width
# Height width are computed
self.width = self.x_off_inv_2 + self.inv.width
def route_rails(self): def route_rails(self):
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)] input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
offset = vector(0.5*self.m2_width,2*self.m1_width) offset = vector(0.5 * self.m2_width, self.m1_pitch)
self.input_rails = self.create_vertical_pin_bus(layer="m2", self.input_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=offset, offset=offset,
names=input_names, 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)] invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)] non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
decode_names = invert_names + non_invert_names decode_names = invert_names + non_invert_names
offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 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", self.decode_rails = self.create_vertical_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=offset, offset=offset,
names=decode_names, names=decode_names,
length=self.height - 2*self.m1_width) length=self.height - 2 * self.m1_pitch)
def create_input_inverters(self): def create_input_inverters(self):
""" Create the input inverters to invert input signals for the decode stage. """ """ Create the input inverters to invert input signals for the decode stage. """
@ -112,62 +109,35 @@ class hierarchical_predecode(design.design):
self.in_inst[inv_num].place(offset=offset, self.in_inst[inv_num].place(offset=offset,
mirror=mirror) mirror=mirror)
def create_output_inverters(self): def create_and_array(self, connections):
""" Create inverters for the inverted output decode signals. """ """ Create the AND stage for the decodes """
self.inv_inst = [] self.and_inst = []
for inv_num in range(self.number_of_outputs): for and_input in range(self.number_of_outputs):
name = "pre_nand_inv_{}".format(inv_num) inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
self.inv_inst.append(self.add_inst(name=name, name = "Xpre{0}_and_{1}".format(inout, and_input)
mod=self.inv)) self.and_inst.append(self.add_inst(name=name,
self.connect_inst(["Z_{}".format(inv_num), mod=self.and_mod))
"out_{}".format(inv_num), self.connect_inst(connections[and_input])
"vdd", "gnd"])
def place_and_array(self):
def place_output_inverters(self): """ Place the AND stage for the decodes """
""" Place inverters for the inverted output decode signals. """ for and_input in range(self.number_of_outputs):
for inv_num in range(self.number_of_outputs): # inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
if (inv_num % 2 == 0): if (and_input % 2 == 0):
y_off = inv_num * self.inv.height y_off = and_input * self.and_mod.height
mirror = "R0" mirror = "R0"
else: else:
y_off =(inv_num + 1)*self.inv.height y_off = (and_input + 1) * self.and_mod.height
mirror = "MX" mirror = "MX"
offset = vector(self.x_off_inv_2, y_off) offset = vector(self.x_off_and, y_off)
self.inv_inst[inv_num].place(offset=offset, self.and_inst[and_input].place(offset=offset,
mirror=mirror) 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)
def route(self): def route(self):
self.route_input_inverters() self.route_input_inverters()
self.route_inputs_to_rails() self.route_inputs_to_rails()
self.route_nand_to_rails() self.route_and_to_rails()
self.route_output_inverters() self.route_output_and()
self.route_vdd_gnd() self.route_vdd_gnd()
def route_inputs_to_rails(self): def route_inputs_to_rails(self):
@ -175,40 +145,31 @@ class hierarchical_predecode(design.design):
for num in range(self.number_of_inputs): for num in range(self.number_of_inputs):
# route one signal next to each vdd/gnd rail since this is # route one signal next to each vdd/gnd rail since this is
# typically where the p/n devices are and there are no # typically where the p/n devices are and there are no
# pins in the nand gates. # pins in the and gates.
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
in_pin = "in_{}".format(num) in_pin = "in_{}".format(num)
a_pin = "A_{}".format(num) a_pin = "A_{}".format(num)
in_pos = vector(self.input_rails[in_pin].x,y_offset) in_pos = vector(self.input_rails[in_pin].x, y_offset)
a_pos = vector(self.decode_rails[a_pin].x,y_offset) a_pos = vector(self.decode_rails[a_pin].x, y_offset)
self.add_path("m1",[in_pos, a_pos]) self.add_path("m1", [in_pos, a_pos])
self.add_via_center(layers = self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=[self.input_rails[in_pin].x, y_offset]) 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]) 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): for num in range(self.number_of_outputs):
# route nand output to output inv input z_pin = self.and_inst[num].get_pin("Z")
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")
self.add_layout_pin(text="out_{}".format(num), self.add_layout_pin(text="out_{}".format(num),
layer="m1", layer="m1",
offset=z_pin.ll(), offset=z_pin.ll(),
height=z_pin.height(), height=z_pin.height(),
width=z_pin.width()) width=z_pin.width())
def route_input_inverters(self): def route_input_inverters(self):
""" """
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd] Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
@ -219,7 +180,7 @@ class hierarchical_predecode(design.design):
#add output so that it is just below the vdd or gnd rail #add output so that it is just below the vdd or gnd rail
# since this is where the p/n devices are and there are no # since this is where the p/n devices are and there are no
# pins in the nand gates. # pins in the and gates.
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc() 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) 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, self.add_via_center(layers=self.m1_stack,
offset=in_pos) offset=in_pos)
def route_and_to_rails(self):
def route_nand_to_rails(self):
# This 2D array defines the connection mapping # This 2D array defines the connection mapping
nand_input_line_combination = self.get_nand_input_line_combination() and_input_line_combination = self.get_and_input_line_combination()
for k in range(self.number_of_outputs): for k in range(self.number_of_outputs):
# create x offset list # create x offset list
index_lst= nand_input_line_combination[k] index_lst= and_input_line_combination[k]
if self.number_of_inputs == 2: if self.number_of_inputs == 2:
gate_lst = ["A","B"] gate_lst = ["A","B"]
@ -251,35 +211,32 @@ class hierarchical_predecode(design.design):
# this will connect pins A,B or A,B,C # this will connect pins A,B or A,B,C
for rail_pin,gate_pin in zip(index_lst,gate_lst): 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) rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("m1", [rail_pos, pin_pos]) self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=rail_pos) offset=rail_pos)
def route_vdd_gnd(self): def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """ """ Add a pin for each row of vdd/gnd which are must-connects next level up. """
# Find the x offsets for where the vias/pins should be placed # Find the x offsets for where the vias/pins should be placed
in_xoffset = self.in_inst[0].rx() + self.m1_space in_xoffset = self.in_inst[0].rx() + self.m1_space
out_xoffset = self.inv_inst[0].lx() - self.m1_space # out_xoffset = self.and_inst[0].cx() + self.m1_space
for num in range(0,self.number_of_outputs): for num in range(0, self.number_of_outputs):
# this will result in duplicate polygons for rails, but who cares # this will result in duplicate polygons for rails, but who cares
# Route both supplies # Route both supplies
for n in ["vdd", "gnd"]: for n in ["vdd", "gnd"]:
nand_pin = self.nand_inst[num].get_pin(n) and_pin = self.and_inst[num].get_pin(n)
supply_offset = nand_pin.ll().scale(0,1) supply_offset = and_pin.ll().scale(0, 1)
self.add_rect(layer="m1", self.add_rect(layer="m1",
offset=supply_offset, offset=supply_offset,
width=self.inv_inst[num].rx()) width=self.and_inst[num].rx())
# Add pins in two locations # Add pins in two locations
for xoffset in [in_xoffset, out_xoffset]: for xoffset in [in_xoffset]:
pin_pos = vector(xoffset, nand_pin.cy()) pin_pos = vector(xoffset, and_pin.cy())
self.add_power_pin(n, pin_pos) self.add_power_pin(n, pin_pos)

View File

@ -27,31 +27,29 @@ class hierarchical_predecode2x4(hierarchical_predecode):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_input_inverters() self.create_input_inverters()
self.create_output_inverters() connections =[["inbar_0", "inbar_1", "out_0", "vdd", "gnd"],
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"], ["in_0", "inbar_1", "out_1", "vdd", "gnd"],
["in_0", "inbar_1", "Z_1", "vdd", "gnd"], ["inbar_0", "in_1", "out_2", "vdd", "gnd"],
["inbar_0", "in_1", "Z_2", "vdd", "gnd"], ["in_0", "in_1", "out_3", "vdd", "gnd"]]
["in_0", "in_1", "Z_3", "vdd", "gnd"]] self.create_and_array(connections)
self.create_nand_array(connections)
def create_layout(self): def create_layout(self):
""" The general organization is from left to right: """ The general organization is from left to right:
1) a set of M2 rails for input signals 1) a set of M2 rails for input signals
2) a set of inverters to invert input signals 2) a set of inverters to invert input signals
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs 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.setup_layout_constraints()
self.route_rails() self.route_rails()
self.place_input_inverters() self.place_input_inverters()
self.place_output_inverters() self.place_and_array()
self.place_nand_array()
self.route() self.route()
self.add_boundary() self.add_boundary()
self.DRC_LVS() 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 pins """ """ These are the decoder connections of the AND gates to the A,B pins """
combination = [["Abar_0", "Abar_1"], combination = [["Abar_0", "Abar_1"],
["A_0", "Abar_1"], ["A_0", "Abar_1"],
["Abar_0", "A_1"], ["Abar_0", "A_1"],

View File

@ -27,16 +27,15 @@ class hierarchical_predecode3x8(hierarchical_predecode):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_input_inverters() self.create_input_inverters()
self.create_output_inverters() connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"], ["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "Z_1", "vdd", "gnd"], ["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "Z_2", "vdd", "gnd"], ["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "Z_3", "vdd", "gnd"], ["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "Z_4", "vdd", "gnd"], ["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "Z_5", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "Z_6", "vdd", "gnd"], ["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
["in_0", "in_1", "in_2", "Z_7", "vdd", "gnd"]] self.create_and_array(connections)
self.create_nand_array(connections)
def create_layout(self): def create_layout(self):
""" """
@ -49,13 +48,12 @@ class hierarchical_predecode3x8(hierarchical_predecode):
self.setup_layout_constraints() self.setup_layout_constraints()
self.route_rails() self.route_rails()
self.place_input_inverters() self.place_input_inverters()
self.place_output_inverters() self.place_and_array()
self.place_nand_array()
self.route() self.route()
self.add_boundary() self.add_boundary()
self.DRC_LVS() 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 """ """ These are the decoder connections of the NAND gates to the A,B,C pins """
combination = [["Abar_0", "Abar_1", "Abar_2"], combination = [["Abar_0", "Abar_1", "Abar_2"],
["A_0", "Abar_1", "Abar_2"], ["A_0", "Abar_1", "Abar_2"],

View File

@ -3,15 +3,15 @@
# Copyright (c) 2016-2019 Regents of the University of California # Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved. # All rights reserved.
# #
import sys from tech import drc
from tech import drc, parameter
import debug import debug
import design import design
from sram_factory import factory from sram_factory import factory
from collections import namedtuple
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
class port_data(design.design): class port_data(design.design):
""" """
Create the data port (column mux, sense amps, write driver, etc.) for the given port number. 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) sram_config.set_local_config(self)
self.port = port self.port = port
if self.write_size is not None: if self.write_size is not None:
self.num_wmasks = int(self.word_size/self.write_size) self.num_wmasks = int(self.word_size / self.write_size)
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
if name == "": if name == "":
name = "port_data_{0}".format(self.port) name = "port_data_{0}".format(self.port)
design.design.__init__(self, name) 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() self.create_netlist()
if not OPTS.netlist_only: 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.create_layout()
self.add_boundary() self.add_boundary()
@ -46,7 +49,19 @@ class port_data(design.design):
# br lines are connect from the precharger # br lines are connect from the precharger
return self.precharge.get_br_names() 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): def create_netlist(self):
self.precompute_constants() self.precompute_constants()
@ -80,8 +95,6 @@ class port_data(design.design):
else: else:
self.column_mux_array_inst = None self.column_mux_array_inst = None
def create_layout(self): def create_layout(self):
self.compute_instance_offsets() self.compute_instance_offsets()
self.place_instances() self.place_instances()
@ -91,33 +104,32 @@ class port_data(design.design):
def add_pins(self): def add_pins(self):
""" Adding pins for port address module""" """ Adding pins for port address module"""
self.add_pin("rbl_bl","INOUT") self.add_pin("rbl_bl", "INOUT")
self.add_pin("rbl_br","INOUT") self.add_pin("rbl_br", "INOUT")
for bit in range(self.num_cols): for bit in range(self.num_cols):
bl_name = self.precharge_array.get_bl_name(self.port) bl_name = self.get_bl_name(self.port)
br_name = self.precharge_array.get_br_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(bl_name, bit), "INOUT")
self.add_pin("{0}_{1}".format(br_name, bit),"INOUT") self.add_pin("{0}_{1}".format(br_name, bit), "INOUT")
if self.port in self.read_ports: if self.port in self.read_ports:
for bit in range(self.word_size): 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: if self.port in self.write_ports:
for bit in range(self.word_size): 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 # Will be empty if no col addr lines
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)] sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
for pin_name in sel_names: 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: if self.port in self.read_ports:
self.add_pin("s_en", "INPUT") self.add_pin("s_en", "INPUT")
self.add_pin("p_en_bar", "INPUT") self.add_pin("p_en_bar", "INPUT")
if self.port in self.write_ports: if self.port in self.write_ports:
self.add_pin("w_en", "INPUT") self.add_pin("w_en", "INPUT")
for bit in range(self.num_wmasks): for bit in range(self.num_wmasks):
self.add_pin("bank_wmask_{}".format(bit),"INPUT") self.add_pin("bank_wmask_{}".format(bit), "INPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd", "GROUND")
def route_layout(self): def route_layout(self):
""" Create routing among the modules """ """ 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 """ """ Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst,"gnd") self.copy_power_pins(inst, "gnd")
def add_modules(self): def add_modules(self):
@ -180,7 +192,6 @@ class port_data(design.design):
else: else:
self.sense_amp_array = None self.sense_amp_array = None
if self.col_addr_size > 0: if self.col_addr_size > 0:
self.column_mux_array = factory.create(module_type="column_mux_array", self.column_mux_array = factory.create(module_type="column_mux_array",
columns=self.num_cols, columns=self.num_cols,
@ -191,7 +202,6 @@ class port_data(design.design):
else: else:
self.column_mux_array = None self.column_mux_array = None
if self.port in self.write_ports: if self.port in self.write_ports:
self.write_driver_array = factory.create(module_type="write_driver_array", self.write_driver_array = factory.create(module_type="write_driver_array",
columns=self.num_cols, columns=self.num_cols,
@ -221,29 +231,27 @@ class port_data(design.design):
else: else:
self.num_col_addr_lines = 0 self.num_col_addr_lines = 0
# A space for wells or jogging m2 between modules # A space for wells or jogging m2 between modules
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"), self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch) 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.bitcell = factory.create(module_type="bitcell")
self.bl_names = self.bitcell.get_all_bl_names() self.bl_names = self.bitcell.get_all_bl_names()
self.br_names = self.bitcell.get_all_br_names() self.br_names = self.bitcell.get_all_br_names()
self.wl_names = self.bitcell.get_all_wl_names() self.wl_names = self.bitcell.get_all_wl_names()
# used for bl/br names # used for bl/br names
self.precharge = factory.create(module_type="precharge", self.precharge = factory.create(module_type="precharge",
bitcell_bl = self.bl_names[0], bitcell_bl=self.bl_names[0],
bitcell_br = self.br_names[0]) bitcell_br=self.br_names[0])
# We create a dummy here to get bl/br names to add those pins to this # 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 # module, which happens before we create the real precharge_array
self.precharge_array = factory.create(module_type="precharge_array", self.precharge_array = factory.create(module_type="precharge_array",
columns=self.num_cols + 1, columns=self.num_cols + 1,
bitcell_bl=self.bl_names[self.port], bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port]) bitcell_br=self.br_names[self.port])
def create_precharge_array(self): def create_precharge_array(self):
""" Creating Precharge """ """ Creating Precharge """
@ -253,8 +261,8 @@ class port_data(design.design):
self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port), self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port),
mod=self.precharge_array) mod=self.precharge_array)
bl_name = self.precharge_array.get_bl_name(self.port) bl_name = self.get_bl_name(self.port)
br_name = self.precharge_array.get_br_name(self.port) br_name = self.get_br_name(self.port)
temp = [] temp = []
# Use left BLs for RBL # Use left BLs for RBL
@ -272,21 +280,19 @@ class port_data(design.design):
temp.extend(["p_en_bar", "vdd"]) temp.extend(["p_en_bar", "vdd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_precharge_array(self, offset): def place_precharge_array(self, offset):
""" Placing Precharge """ """ Placing Precharge """
self.precharge_array_inst.place(offset=offset, mirror="MX") self.precharge_array_inst.place(offset=offset, mirror="MX")
def create_column_mux_array(self): def create_column_mux_array(self):
""" Creating Column Mux when words_per_row > 1 . """ """ Creating Column Mux when words_per_row > 1 . """
self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port), self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port),
mod=self.column_mux_array) mod=self.column_mux_array)
bl_name = self.column_mux_array.get_bl_name(self.port) bl_name = self.get_bl_name(self.port)
br_name = self.column_mux_array.get_br_name(self.port) br_name = self.get_br_name(self.port)
temp = [] temp = []
for col in range(self.num_cols): for col in range(self.num_cols):
temp.append("{0}_{1}".format(bl_name, col)) temp.append("{0}_{1}".format(bl_name, col))
@ -301,7 +307,6 @@ class port_data(design.design):
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_column_mux_array(self, offset): def place_column_mux_array(self, offset):
""" Placing Column Mux when words_per_row > 1 . """ """ Placing Column Mux when words_per_row > 1 . """
if self.col_addr_size == 0: 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") self.column_mux_array_inst.place(offset=offset, mirror="MX")
def create_sense_amp_array(self): def create_sense_amp_array(self):
""" Creating Sense amp """ """ Creating Sense amp """
self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port), self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port),
mod=self.sense_amp_array) mod=self.sense_amp_array)
bl_name = self.sense_amp_array.get_bl_name(self.port) bl_name = self.get_bl_name(self.port)
br_name = self.sense_amp_array.get_br_name(self.port) br_name = self.get_br_name(self.port)
temp = [] temp = []
for bit in range(self.word_size): for bit in range(self.word_size):
temp.append("dout_{}".format(bit)) 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(bl_name, bit))
temp.append("{0}_out_{1}".format(br_name, bit)) temp.append("{0}_out_{1}".format(br_name, bit))
temp.extend(["s_en", "vdd", "gnd"]) temp.extend(["s_en", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_sense_amp_array(self, offset): def place_sense_amp_array(self, offset):
""" Placing Sense amp """ """ Placing Sense amp """
self.sense_amp_array_inst.place(offset=offset, mirror="MX") self.sense_amp_array_inst.place(offset=offset, mirror="MX")
def create_write_driver_array(self): def create_write_driver_array(self):
""" Creating Write Driver """ """ Creating Write Driver """
self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port), self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port),
mod=self.write_driver_array) mod=self.write_driver_array)
bl_name = self.write_driver_array.get_bl_name(self.port) bl_name = self.get_bl_name(self.port)
br_name = self.write_driver_array.get_br_name(self.port) br_name = self.get_br_name(self.port)
temp = [] temp = []
for bit in range(self.word_size): for bit in range(self.word_size):
@ -365,12 +366,10 @@ class port_data(design.design):
self.connect_inst(temp) self.connect_inst(temp)
def place_write_driver_array(self, offset): def place_write_driver_array(self, offset):
""" Placing Write Driver """ """ Placing Write Driver """
self.write_driver_array_inst.place(offset=offset, mirror="MX") self.write_driver_array_inst.place(offset=offset, mirror="MX")
def create_write_mask_and_array(self): def create_write_mask_and_array(self):
""" Creating Write Mask AND Array """ """ Creating Write Mask AND Array """
self.write_mask_and_array_inst = self.add_inst(name="write_mask_and_array{}".format(self.port), 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"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_write_mask_and_array(self, offset): def place_write_mask_and_array(self, offset):
""" Placing Write Mask AND array """ """ Placing Write Mask AND array """
self.write_mask_and_array_inst.place(offset=offset, mirror="MX") self.write_mask_and_array_inst.place(offset=offset, mirror="MX")
def compute_instance_offsets(self): def compute_instance_offsets(self):
""" """
Compute the empty instance offsets for port0 and port1 (if needed) Compute the empty instance offsets for port0 and port1 (if needed)
@ -445,7 +442,6 @@ class port_data(design.design):
if self.column_mux_offset: if self.column_mux_offset:
self.place_column_mux_array(self.column_mux_offset) self.place_column_mux_array(self.column_mux_offset)
def route_sense_amp_out(self, port): def route_sense_amp_out(self, port):
""" Add pins for the sense amp output """ """ Add pins for the sense amp output """
@ -457,7 +453,6 @@ class port_data(design.design):
height=data_pin.height(), height=data_pin.height(),
width=data_pin.width()) width=data_pin.width())
def route_write_driver_in(self, port): def route_write_driver_in(self, port):
""" Connecting write driver """ """ Connecting write driver """
@ -466,7 +461,6 @@ class port_data(design.design):
din_name = "din_{}".format(row) din_name = "din_{}".format(row)
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
def route_write_mask_and_array_in(self, port): def route_write_mask_and_array_in(self, port):
""" Add pins for the write mask and array input """ """ 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) bank_wmask_name = "bank_wmask_{}".format(bit)
self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name) self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name)
def route_write_mask_and_array_to_write_driver(self,port): def route_write_mask_and_array_to_write_driver(self,port):
""" Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write """ 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 """ 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()): while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()):
loc += 1 loc += 1
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch 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: else:
# Stride by the write size rather than finding the next pin to the right # Stride by the write size rather than finding the next pin to the right
loc += self.write_size 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() 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()) end_pos = vector(length, wdriver_en_pin.cy())
# Add via for the write driver array's enable input # Add via for the write driver array's enable input
@ -516,7 +509,6 @@ class port_data(design.design):
# Route between write mask AND array and write driver array # Route between write mask AND array and write driver array
self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos]) self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos])
def route_column_mux_to_precharge_array(self, port): def route_column_mux_to_precharge_array(self, port):
""" Routing of BL and BR between col mux and precharge array """ """ Routing of BL and BR between col mux and precharge array """
@ -526,11 +518,13 @@ class port_data(design.design):
inst1 = self.column_mux_array_inst inst1 = self.column_mux_array_inst
inst2 = self.precharge_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): def route_sense_amp_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """ """ Routing of BL and BR between sense_amp and column mux or precharge array """
@ -539,23 +533,23 @@ class port_data(design.design):
if self.col_addr_size>0: if self.col_addr_size>0:
# Sense amp is connected to the col mux # Sense amp is connected to the col mux
inst1 = self.column_mux_array_inst inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}" inst1_bls_templ = "{inst}_out_{bit}"
inst1_br_name = "br_out_{}"
start_bit = 0 start_bit = 0
else: else:
# Sense amp is directly connected to the precharge array # Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}" inst1_bls_templ="{inst}_{bit}"
inst1_br_name = "br_{}"
if self.port==0: if self.port==0:
start_bit=1 start_bit=1
else: else:
start_bit=0 start_bit=0
self.channel_route_bitlines(inst1=inst1,
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, inst1_bls_template=inst1_bls_templ,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit) inst2=inst2,
num_bits=self.word_size,
inst1_start_bit=start_bit)
def route_write_driver_to_column_mux_or_precharge_array(self, port): def route_write_driver_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """ """ Routing of BL and BR between sense_amp and column mux or precharge array """
@ -564,22 +558,21 @@ class port_data(design.design):
if self.col_addr_size>0: if self.col_addr_size>0:
# Write driver is connected to the col mux # Write driver is connected to the col mux
inst1 = self.column_mux_array_inst inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}" inst1_bls_templ = "{inst}_out_{bit}"
inst1_br_name = "br_out_{}"
start_bit = 0 start_bit = 0
else: else:
# Sense amp is directly connected to the precharge array # Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}" inst1_bls_templ="{inst}_{bit}"
inst1_br_name = "br_{}"
if self.port==0: if self.port==0:
start_bit=1 start_bit=1
else: else:
start_bit=0 start_bit=0
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, self.channel_route_bitlines(inst1=inst1, inst2=inst2,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit) 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): def route_write_driver_to_sense_amp(self, port):
""" Routing of BL and BR between write driver and sense amp """ """ Routing of BL and BR between write driver and sense amp """
@ -589,8 +582,9 @@ class port_data(design.design):
# These should be pitch matched in the cell library, # These should be pitch matched in the cell library,
# but just in case, do a channel route. # 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): def route_bitline_pins(self):
""" Add the bitline pins for the given port """ """ Add the bitline pins for the given port """
@ -609,12 +603,15 @@ class port_data(design.design):
for bit in range(self.num_cols): for bit in range(self.num_cols):
if self.precharge_array_inst: 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,
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit)) "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: else:
debug.error("Didn't find precharge array.") debug.error("Didn't find precharge array.")
def route_control_pins(self): def route_control_pins(self):
""" Add the control pins: s_en, p_en_bar, w_en """ """ Add the control pins: s_en, p_en_bar, w_en """
if self.precharge_array_inst: if self.precharge_array_inst:
@ -635,65 +632,107 @@ class port_data(design.design):
if self.write_mask_and_array_inst: if self.write_mask_and_array_inst:
self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en") self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en")
def _group_bitline_instances(self, inst1, inst2, num_bits,
inst1_bls_template,
inst1_start_bit,
inst2_bls_template,
inst2_start_bit):
"""
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():
bot_inst_group = inst1_group
top_inst_group = inst2_group
else:
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, def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0, inst1_bls_template="{inst}_{bit}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0): 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. Route the bl and br of two modules using the channel router.
""" """
# determine top and bottom automatically. bot_inst_group, top_inst_group = self._group_bitline_instances(
# since they don't overlap, we can just check the bottom y coordinate. inst1, inst2, num_bits,
if inst1.by() < inst2.by(): inst1_bls_template, inst1_start_bit,
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) inst2_bls_template, inst2_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)
# Channel route each mux separately since we don't minimize the number # Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once! # of tracks in teh channel router yet. If we did, we could route all the bits at once!
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): 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))] bottom_names = self._get_bitline_pins(bot_inst_group, 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))] top_names = self._get_bitline_pins(top_inst_group, bit)
route_map = list(zip(bottom_names, top_names)) route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack) self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitlines(self, inst1, inst2, num_bits, def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0, inst1_bls_template="{inst}_{bit}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0): inst1_start_bit=0,
inst2_bls_template="{inst}_{bit}",
inst2_start_bit=0):
""" """
Connect the bl and br of two modules. Connect the bl and br of two modules.
This assumes that they have sufficient space to create a jog This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed). in the middle between the two modules (if needed).
""" """
# determine top and bottom automatically. bot_inst_group, top_inst_group = self._group_bitline_instances(
# since they don't overlap, we can just check the bottom y coordinate. inst1, inst2, num_bits,
if inst1.by() < inst2.by(): inst1_bls_template, inst1_start_bit,
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) inst2_bls_template, inst2_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)
for col in range(num_bits): for col in range(num_bits):
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col+bottom_start_bit)).uc() bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col)
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col+bottom_start_bit)).uc() top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col)
top_bl = top_inst.get_pin(top_bl_name.format(col+top_start_bit)).bc() bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc()
top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc() top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc()
yoffset = 0.5*(top_bl.y+bottom_bl.y) yoffset = 0.5 * (top_bl.y + bot_bl.y)
self.add_path("m2",[bottom_bl, vector(bottom_bl.x,yoffset), self.add_path("m2", [bot_bl,
vector(top_bl.x,yoffset), top_bl]) vector(bot_bl.x, yoffset),
self.add_path("m2",[bottom_br, vector(bottom_br.x,yoffset), vector(top_bl.x, yoffset),
vector(top_br.x,yoffset), top_br]) top_bl])
self.add_path("m2", [bot_br,
vector(bot_br.x, yoffset),
vector(top_br.x, yoffset),
top_br])
def graph_exclude_precharge(self): def graph_exclude_precharge(self):
"""Precharge adds a loop between bitlines, can be excluded to reduce complexity""" """Precharge adds a loop between bitlines, can be excluded to reduce complexity"""

View File

@ -12,6 +12,7 @@ from vector import vector
from sram_factory import factory from sram_factory import factory
from globals import OPTS from globals import OPTS
class precharge_array(design.design): class precharge_array(design.design):
""" """
Dynamically generated precharge array of all bitlines. Cols is number Dynamically generated precharge array of all bitlines. Cols is number
@ -32,20 +33,13 @@ class precharge_array(design.design):
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
def get_bl_name(self, port=0): def get_bl_name(self):
bl_name = self.pc_cell.get_bl_names() bl_name = self.pc_cell.get_bl_names()
if len(self.all_ports) == 1: return bl_name
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self, port=0): def get_br_name(self):
br_name = self.pc_cell.get_br_names() br_name = self.pc_cell.get_br_names()
if len(self.all_ports) == 1: return br_name
return br_name
else:
return br_name + "{}".format(port)
def add_pins(self): def add_pins(self):
"""Adds pins for spice file""" """Adds pins for spice file"""
@ -75,11 +69,8 @@ class precharge_array(design.design):
size=self.size, size=self.size,
bitcell_bl=self.bitcell_bl, bitcell_bl=self.bitcell_bl,
bitcell_br=self.bitcell_br) bitcell_br=self.bitcell_br)
self.add_mod(self.pc_cell) self.add_mod(self.pc_cell)
def add_layout_pins(self): def add_layout_pins(self):
self.add_layout_pin(text="en_bar", self.add_layout_pin(text="en_bar",
@ -93,19 +84,8 @@ class precharge_array(design.design):
for i in range(len(self.local_insts)): for i in range(len(self.local_insts)):
inst = self.local_insts[i] inst = self.local_insts[i]
bl_pin = inst.get_pin("bl") self.copy_layout_pin(inst, "bl", "bl_{0}".format(i))
self.add_layout_pin(text="bl_{0}".format(i), self.copy_layout_pin(inst, "br", "br_{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())
def create_insts(self): def create_insts(self):
"""Creates a precharge array by horizontally tiling the precharge cell""" """Creates a precharge array by horizontally tiling the precharge cell"""
@ -119,7 +99,6 @@ class precharge_array(design.design):
self.local_insts.append(inst) self.local_insts.append(inst)
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"]) self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"])
def place_insts(self): def place_insts(self):
""" Places precharge array by horizontally tiling the precharge cell""" """ Places precharge array by horizontally tiling the precharge cell"""
from tech import cell_properties from tech import cell_properties
@ -137,8 +116,11 @@ class precharge_array(design.design):
xoffset = xoffset + self.pc_cell.width xoffset = xoffset + self.pc_cell.width
def get_en_cin(self): 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() precharge_en_cin = self.pc_cell.get_en_cin()
return precharge_en_cin*self.columns return precharge_en_cin * self.columns

View File

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

View File

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

View File

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

View File

@ -75,8 +75,7 @@ class wordline_driver(design.design):
""" """
# Find the x offsets for where the vias/pins should be placed # Find the x offsets for where the vias/pins should be placed
a_xoffset = self.nand_inst[0].rx() a_xoffset = self.nand_inst[0].lx()
b_xoffset = self.inv2_inst[0].lx()
for num in range(self.rows): for num in range(self.rows):
# this will result in duplicate polygons for rails, but who cares # 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) supply_pin = self.inv2_inst[num].get_pin(name)
# Add pins in two locations # Add pins in two locations
for xoffset in [a_xoffset, b_xoffset]: for xoffset in [a_xoffset]:
pin_pos = vector(xoffset, supply_pin.cy()) pin_pos = vector(xoffset, supply_pin.cy())
self.add_power_pin(name, pin_pos) self.add_power_pin(name, pin_pos)
@ -116,7 +115,7 @@ class wordline_driver(design.design):
"vdd", "gnd"]) "vdd", "gnd"])
def place_drivers(self): 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 inv2_xoffset = nand2_xoffset + self.nand2.width
self.width = inv2_xoffset + self.inv.width self.width = inv2_xoffset + self.inv.width
@ -124,10 +123,10 @@ class wordline_driver(design.design):
for row in range(self.rows): for row in range(self.rows):
if (row % 2): if (row % 2):
y_offset = self.inv.height*(row + 1) y_offset = self.inv.height * (row + 1)
inst_mirror = "MX" inst_mirror = "MX"
else: else:
y_offset = self.inv.height*row y_offset = self.inv.height * row
inst_mirror = "R0" inst_mirror = "R0"
nand2_offset = [nand2_xoffset, y_offset] nand2_offset = [nand2_xoffset, y_offset]
@ -169,8 +168,8 @@ class wordline_driver(design.design):
zr_pos = nand_inst.get_pin("Z").rc() zr_pos = nand_inst.get_pin("Z").rc()
al_pos = inv2_inst.get_pin("A").lc() al_pos = inv2_inst.get_pin("A").lc()
# ensure the bend is in the middle # ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y) 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) 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]) self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
# connect the decoder input pin to nand2 B # connect the decoder input pin to nand2 B

View File

@ -10,6 +10,7 @@ import design
import utils import utils
from globals import OPTS from globals import OPTS
from tech import GDS,layer from tech import GDS,layer
from tech import cell_properties as props
class write_driver(design.design): class write_driver(design.design):
""" """
@ -19,7 +20,13 @@ class write_driver(design.design):
the technology library. 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"] type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only: if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) (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) self.add_pin_types(self.type_list)
def get_bl_names(self): def get_bl_names(self):
return "bl" return props.write_driver.pin.bl
def get_br_names(self): 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): def get_w_en_cin(self):
"""Get the relative capacitance of a single input""" """Get the relative capacitance of a single input"""

View File

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

View File

@ -26,8 +26,8 @@ class pgate(design.design):
if height: if height:
self.height = height self.height = height
elif not height: elif not height:
# By default, we make it 8 M1 pitch tall # By default, we make it 10 M1 pitch tall
self.height = 8*self.m1_pitch self.height = 10*self.m1_pitch
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -103,7 +103,7 @@ class pgate(design.design):
- vector(0.5 * contact_width - 0.5 * self.poly_width, 0) - vector(0.5 * contact_width - 0.5 * self.poly_width, 0)
elif position == "right": elif position == "right":
contact_offset = left_gate_offset \ 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: else:
debug.error("Invalid contact placement option.", -1) debug.error("Invalid contact placement option.", -1)
@ -128,16 +128,16 @@ class pgate(design.design):
""" Extend the n/p wells to cover whole cell """ """ Extend the n/p wells to cover whole cell """
# This should match the cells in the cell library # This should match the cells in the cell library
nwell_y_offset = 0.48 * self.height self.nwell_y_offset = 0.48 * self.height
full_height = self.height + 0.5*self.m1_width full_height = self.height + 0.5* self.m1_width
# FIXME: float rounding problem # FIXME: float rounding problem
if "nwell" in layer: if "nwell" in layer:
# Add a rail width to extend the well to the top of the rail # 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, nwell_max_offset = max(self.find_highest_layer_coords("nwell").y,
full_height) full_height)
nwell_position = vector(0, nwell_y_offset) - vector(self.well_extend_active, 0) nwell_position = vector(0, self.nwell_y_offset) - vector(self.well_extend_active, 0)
nwell_height = nwell_max_offset - nwell_y_offset nwell_height = nwell_max_offset - self.nwell_y_offset
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=nwell_position, offset=nwell_position,
width=self.well_width, width=self.well_width,
@ -153,7 +153,7 @@ class pgate(design.design):
pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y, pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y,
-0.5 * self.m1_width) -0.5 * self.m1_width)
pwell_position = vector(-self.well_extend_active, pwell_min_offset) 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", self.add_rect(layer="pwell",
offset=pwell_position, offset=pwell_position,
width=self.well_width, width=self.well_width,
@ -264,3 +264,12 @@ class pgate(design.design):
# offset=implant_offset, # offset=implant_offset,
# width=implant_width, # width=implant_width,
# height=implant_height) # 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.

View File

@ -51,9 +51,9 @@ class pinv(pgate.pgate):
def create_layout(self): def create_layout(self):
""" Calls all functions related to the generation of the layout """ """ Calls all functions related to the generation of the layout """
self.setup_layout_constants()
self.place_ptx() self.place_ptx()
self.add_well_contacts() self.add_well_contacts()
self.determine_width()
self.extend_wells() self.extend_wells()
self.route_supply_rails() self.route_supply_rails()
self.connect_rails() self.connect_rails()
@ -147,21 +147,6 @@ class pinv(pgate.pgate):
debug.check(self.pmos_width >= drc("minwidth_tx"), debug.check(self.pmos_width >= drc("minwidth_tx"),
"Cannot finger PMOS transistors to fit cell height.") "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): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """

View File

@ -49,10 +49,11 @@ class pnand2(pgate.pgate):
""" Calls all functions related to the generation of the layout """ """ Calls all functions related to the generation of the layout """
self.setup_layout_constants() self.setup_layout_constants()
self.route_supply_rails()
self.place_ptx() self.place_ptx()
self.connect_rails()
self.add_well_contacts() self.add_well_contacts()
self.determine_width()
self.route_supply_rails()
self.connect_rails()
self.extend_wells() self.extend_wells()
self.route_inputs() self.route_inputs()
self.route_output() self.route_output()
@ -96,15 +97,6 @@ class pnand2(pgate.pgate):
# source and drain pins # source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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 # This is the extra space needed to ensure DRC rules
# to the active contacts # to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0) extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
@ -190,16 +182,16 @@ class pnand2(pgate.pgate):
def route_inputs(self): def route_inputs(self):
""" Route the A and B inputs """ """ Route the A and B inputs """
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \ inputB_yoffset = self.nmos2_inst.uy() + 0.5 * contact.poly_contact.height
+ self.m2_space + 0.5 * self.m2_width
self.route_input_gate(self.pmos2_inst, self.route_input_gate(self.pmos2_inst,
self.nmos2_inst, self.nmos2_inst,
inputB_yoffset, inputB_yoffset,
"B", "B",
position="center") position="right")
# This will help with the wells and the input/output placement # 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.route_input_gate(self.pmos1_inst,
self.nmos1_inst, self.nmos1_inst,
self.inputA_yoffset, self.inputA_yoffset,
@ -242,8 +234,8 @@ class pnand2(pgate.pgate):
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="m1", layer="m1",
offset=out_offset, offset=out_offset,
width=contact.m1_via.first_layer_height, width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_width) height=contact.m1_via.first_layer_height)
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""

View File

@ -36,7 +36,8 @@ class pnand3(pgate.pgate):
self.pmos_width = self.pmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized # 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 self.tx_mults = 1
# Creates the netlist and layout # Creates the netlist and layout
@ -57,10 +58,11 @@ class pnand3(pgate.pgate):
""" Calls all functions related to the generation of the layout """ """ Calls all functions related to the generation of the layout """
self.setup_layout_constants() self.setup_layout_constants()
self.route_supply_rails()
self.place_ptx() self.place_ptx()
self.connect_rails()
self.add_well_contacts() self.add_well_contacts()
self.determine_width()
self.route_supply_rails()
self.connect_rails()
self.extend_wells() self.extend_wells()
self.route_inputs() self.route_inputs()
self.route_output() self.route_output()
@ -87,15 +89,8 @@ class pnand3(pgate.pgate):
""" Pre-compute some handy layout parameters. """ """ Pre-compute some handy layout parameters. """
# Compute the overlap of the source and drain pins # Compute the overlap of the source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() overlap_xoffset = self.pmos.get_pin("D").ll().x - self.pmos.get_pin("S").ll().x
self.ptx_offset = vector(overlap_xoffset, 0)
# 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.
# This is the extra space needed to ensure DRC rules # This is the extra space needed to ensure DRC rules
# to the active contacts # to the active contacts
@ -153,30 +148,25 @@ class pnand3(pgate.pgate):
""" """
pmos1_pos = vector(self.pmos.active_offset.x, pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height \ self.height - self.pmos.active_height - self.top_bottom_space)
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos) 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.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) self.pmos3_inst.place(self.pmos3_pos)
nmos1_pos = vector(self.pmos.active_offset.x, nmos1_pos = vector(self.pmos.active_offset.x,
self.top_bottom_space) self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos) 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.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) 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): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
@ -193,29 +183,25 @@ class pnand3(pgate.pgate):
self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd") self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd")
def route_inputs(self): def route_inputs(self):
""" Route the A and B inputs """ """ Route the A and B and C 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)
active_spacing = max(self.m1_space, m1_pitch = self.m1_space + contact.m1_via.first_layer_height
0.5 * contact.poly_contact.first_layer_width + self.poly_to_active) # Put B right on the well line
inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing self.inputB_yoffset = self.nwell_y_offset
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
inputC_yoffset,
"C",
position="center")
inputB_yoffset = inputC_yoffset + metal_spacing
self.route_input_gate(self.pmos2_inst, self.route_input_gate(self.pmos2_inst,
self.nmos2_inst, self.nmos2_inst,
inputB_yoffset, self.inputB_yoffset,
"B", "B",
position="center") position="center")
self.inputA_yoffset = inputB_yoffset + metal_spacing self.inputC_yoffset = self.inputB_yoffset - m1_pitch
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
self.inputC_yoffset,
"C",
position="center")
self.inputA_yoffset = self.inputB_yoffset + m1_pitch
self.route_input_gate(self.pmos1_inst, self.route_input_gate(self.pmos1_inst,
self.nmos1_inst, self.nmos1_inst,
self.inputA_yoffset, self.inputA_yoffset,
@ -243,7 +229,7 @@ class pnand3(pgate.pgate):
directions=("V", "V")) directions=("V", "V"))
# PMOS3 and NMOS3 are drain aligned # 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) # Route in the A input track (top track)
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
@ -273,7 +259,7 @@ class pnand3(pgate.pgate):
# In fF # In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1094 transition_prob = 0.1094
return transition_prob *(c_load + c_para) return transition_prob * (c_load + c_para)
def input_load(self): def input_load(self):
"""Return the relative input capacitance of a single input""" """Return the relative input capacitance of a single input"""

View File

@ -48,10 +48,11 @@ class pnor2(pgate.pgate):
""" Calls all functions related to the generation of the layout """ """ Calls all functions related to the generation of the layout """
self.setup_layout_constants() self.setup_layout_constants()
self.route_supply_rails()
self.place_ptx() self.place_ptx()
self.connect_rails()
self.add_well_contacts() self.add_well_contacts()
self.determine_width()
self.route_supply_rails()
self.connect_rails()
self.extend_wells() self.extend_wells()
self.route_inputs() self.route_inputs()
self.route_output() self.route_output()

View File

@ -12,6 +12,7 @@ from tech import parameter
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
from tech import drc
class precharge(design.design): class precharge(design.design):
@ -30,6 +31,15 @@ class precharge(design.design):
self.width = self.bitcell.width self.width = self.bitcell.width
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br 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 # Creates the netlist and layout
# Since it has variable height, it is not a pgate. # Since it has variable height, it is not a pgate.
@ -50,6 +60,7 @@ class precharge(design.design):
self.create_ptx() self.create_ptx()
def create_layout(self): def create_layout(self):
self.place_ptx() self.place_ptx()
self.connect_poly() self.connect_poly()
self.route_en() self.route_en()
@ -78,18 +89,24 @@ class precharge(design.design):
# Adds the rail across the width of the cell # Adds the rail across the width of the cell
vdd_position = vector(0.5 * self.width, self.height) 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, offset=vdd_position,
width=self.width, width=self.width,
height=self.m1_width) height=layer_width)
pmos_pin = self.upper_pmos2_inst.get_pin("S") pmos_pin = self.upper_pmos2_inst.get_pin("S")
# center of vdd rail # center of vdd rail
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos]) self.add_path("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 # 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): def create_ptx(self):
""" """
@ -113,6 +130,8 @@ class precharge(design.design):
Place both the upper_pmos and lower_pmos to the module 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, # Compute the other pmos2 location,
# but determining offset to overlap the source and drain pins # but determining offset to overlap the source and drain pins
overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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() contact_xdiff = self.pmos.get_pin("S").lx()
# adds the lower pmos to layout # 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.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
self.nwell_enclose_active), self.nwell_enclose_active),
self.pmos.active_offset.y) self.initial_yoffset)
self.lower_pmos_inst.place(self.lower_pmos_position) self.lower_pmos_inst.place(self.lower_pmos_position)
# adds the upper pmos(s) to layout # adds the upper pmos(s) to layout with 2 M2 tracks
ydiff = self.pmos.height + 2 * self.m1_space + contact.poly_contact.width ydiff = self.pmos.height + self.m2_pitch
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos) 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 upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset
self.upper_pmos2_inst.place(upper_pmos2_pos) self.upper_pmos2_inst.place(upper_pmos2_pos)
@ -162,15 +182,20 @@ class precharge(design.design):
Adds the en input rail, en contact/vias, and connects to the pmos 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() \ 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, self.add_via_center(layers=self.poly_stack,
offset=offset) offset=offset)
if self.en_layer == "m2":
self.add_via_center(layers=self.m1_stack,
offset=offset)
# adds the en rail on metal1 # adds the en rail on metal1
self.add_layout_pin_segment_center(text="en_bar", self.add_layout_pin_segment_center(text="en_bar",
layer="m1", layer=self.en_layer,
start=offset.scale(0, 1), start=offset.scale(0, 1),
end=offset.scale(0, 1) + vector(self.width, 0)) end=offset.scale(0, 1) + vector(self.width, 0))
@ -180,16 +205,15 @@ class precharge(design.design):
""" """
# adds the contact from active to metal1 # adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \ + vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \
+ self.nwell_extend_active) + self.nwell_extend_active)
self.add_via_center(layers=self.active_stack, self.add_via_center(layers=self.active_stack,
offset=well_contact_pos, offset=self.well_contact_pos,
implant_type="n", implant_type="n",
well_type="n") well_type="n")
# leave an extra pitch for the height self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space
self.height = well_contact_pos.y + contact.active_contact.height + self.m1_pitch
# nwell should span the whole design since it is pmos only # nwell should span the whole design since it is pmos only
self.add_rect(layer="nwell", 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 both bit-line and bit-line-bar to the module
""" """
layer_width = drc("minwidth_" + self.bitline_layer)
layer_space = drc("{0}_to_{0}".format(self.bitline_layer))
# adds the BL on metal 2 # adds the BL
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \ self.bl_xoffset = layer_space + 0.5 * layer_width
- vector(0.5 * self.m2_width, 0) top_pos = vector(self.bl_xoffset, self.height)
self.bl_pin = self.add_layout_pin(text="bl", pin_pos = vector(self.bl_xoffset, 0)
layer="m2", self.add_path(self.bitline_layer, [top_pos, pin_pos])
offset=offset, self.bl_pin = self.add_layout_pin_segment_center(text="bl",
height=self.height) layer=self.bitline_layer,
start=pin_pos,
end=top_pos)
# adds the BR on metal 2 # adds the BR
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \ self.br_xoffset = self.width - layer_space - 0.5 * layer_width
- vector(0.5 * self.m2_width, 0) top_pos = vector(self.br_xoffset, self.height)
self.br_pin = self.add_layout_pin(text="br", pin_pos = vector(self.br_xoffset, 0)
layer="m2", self.add_path(self.bitline_layer, [top_pos, pin_pos])
offset=offset, self.br_pin = self.add_layout_pin_segment_center(text="br",
height=self.height) layer=self.bitline_layer,
start=pin_pos,
end=top_pos)
def connect_to_bitlines(self): def connect_to_bitlines(self):
""" """
Connect the bitlines to the devices Connect the bitlines to the devices
""" """
self.add_bitline_contacts() self.add_bitline_contacts()
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"), self.connect_pmos(self.lower_pmos_inst.get_pin("S"),
self.get_pin("bl")) self.bl_xoffset)
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"), self.connect_pmos(self.lower_pmos_inst.get_pin("D"),
self.get_pin("bl")) self.br_xoffset)
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),
self.get_pin("br")) self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"), self.bl_xoffset)
self.get_pin("br")) self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),
self.br_xoffset)
def add_bitline_contacts(self): def add_bitline_contacts(self):
""" """
Adds contacts/via from metal1 to metal2 for bit-lines Adds contacts/via from metal1 to metal2 for bit-lines
""" """
upper_pin = self.upper_pmos1_inst.get_pin("S") # No contacts needed if M1
if self.bitline_layer == "m1":
return
# BL
lower_pin = self.lower_pmos_inst.get_pin("S") lower_pin = self.lower_pmos_inst.get_pin("S")
self.lower_via = self.add_via_center(layers=self.m1_stack,
# BL goes up to M2 at the transistor offset=lower_pin.center(),
self.bl_contact =self.add_via_center(layers=self.m1_stack,
offset=upper_pin.center(),
directions=("V", "V")) directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=lower_pin.center(),
directions=("V", "V"))
# BR routes over on M1 first lower_pin = self.lower_pmos_inst.get_pin("D")
self.add_via_center(layers=self.m1_stack, self.lower_via = self.add_via_center(layers=self.m1_stack,
offset=vector(self.br_pin.cx(), upper_pin.cy()), offset=lower_pin.center(),
directions=("V", "V")) directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V", "V"))
def connect_pmos_m1(self, pmos_pin, bit_pin): # 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"))
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 Connect a pmos pin to bitline pin
""" """
left_pos = vector(min(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_pin.cx()), pmos_pin.cy()) right_pos = vector(max(pmos_pin.cx(), bit_xoffset), pmos_pin.cy())
self.add_path("m1", [left_pos, right_pos] ) self.add_path(self.bitline_layer,
[left_pos, right_pos],
def connect_pmos_m2(self, pmos_pin, bit_pin): width=pmos_pin.height())
"""
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)
def get_en_cin(self): def get_en_cin(self):
"""Get the relative capacitance of the enable in the precharge cell""" """Get the relative capacitance of the enable in the precharge cell"""

View File

@ -10,6 +10,7 @@ import debug
from tech import layer, drc, spice from tech import layer, drc, spice
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
import contact
class ptx(design.design): class ptx(design.design):
@ -28,6 +29,7 @@ class ptx(design.design):
tx_type="nmos", tx_type="nmos",
connect_active=False, connect_active=False,
connect_poly=False, connect_poly=False,
series_devices=False,
num_contacts=None): num_contacts=None):
# We need to keep unique names because outputting to GDSII # We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will # 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.tx_width = width
self.connect_active = connect_active self.connect_active = connect_active
self.connect_poly = connect_poly self.connect_poly = connect_poly
self.series_devices = series_devices
self.num_contacts = num_contacts self.num_contacts = num_contacts
# Since it has variable height, it is not a pgate. # Since it has variable height, it is not a pgate.
@ -127,27 +130,25 @@ class ptx(design.design):
directions=("V", "V"), directions=("V", "V"),
dimensions=(1, self.num_contacts)) dimensions=(1, self.num_contacts))
# The contacted poly pitch # This is the extra poly spacing due to the poly contact to poly contact pitch
self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width, # of contacted gates
self.poly_space) extra_poly_contact_width = contact.poly_contact.width - self.poly_width
# The contacted poly pitch # This is the spacing between S/D contacts
self.contact_spacing = 2 * self.contact_to_gate + \ # This is the spacing between the poly gates
self.contact_width + self.poly_width 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)
# This is measured because of asymmetric enclosure rules self.end_to_contact = 0.5 * self.active_contact.width
active_enclose_contact = 0.5*(self.active_contact.width - self.contact_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
# Active width is determined by enclosure on both ends and contacted pitch, # Active width is determined by enclosure on both ends and contacted pitch,
# at least one poly and n-1 poly pitches # at least one poly and n-1 poly pitches
self.active_width = 2 * self.end_to_poly + self.poly_width + \ self.active_width = 2 * self.end_to_contact + self.active_contact.width \
(self.mults - 1) * self.poly_pitch + 2 * self.contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch
# Active height is just the transistor width # Active height is just the transistor width
self.active_height = self.tx_width self.active_height = self.tx_width
@ -287,12 +288,12 @@ class ptx(design.design):
Add the poly gates(s) and (optionally) connect them. Add the poly gates(s) and (optionally) connect them.
""" """
# poly is one contacted spacing from the end and down an extension # poly is one contacted spacing from the end and down an extension
poly_offset = self.active_offset \ poly_offset = self.contact_offset \
+ vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \ + vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.contact_to_gate, 0)
+ vector(self.end_to_poly, -self.poly_extend_active)
# poly_positions are the bottom center of the poly gates # 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, # It is important that these are from left to right,
# so that the pins are in the right # so that the pins are in the right
@ -304,16 +305,18 @@ class ptx(design.design):
offset=poly_offset, offset=poly_offset,
height=self.poly_height, height=self.poly_height,
width=self.poly_width) width=self.poly_width)
self.add_layout_pin_rect_center(text="G", gate = self.add_layout_pin_rect_center(text="G",
layer="poly", layer="poly",
offset=poly_offset, offset=poly_offset,
height=self.poly_height, height=self.poly_height,
width=self.poly_width) width=self.poly_width)
poly_positions.append(poly_offset) self.poly_positions.append(poly_offset)
poly_offset = poly_offset + vector(self.poly_pitch,0) self.poly_gates.append(gate)
poly_offset = poly_offset + vector(self.poly_pitch, 0)
if self.connect_poly: if self.connect_poly:
self.connect_fingered_poly(poly_positions) self.connect_fingered_poly(self.poly_positions)
def add_active(self): def add_active(self):
""" """
@ -362,59 +365,88 @@ class ptx(design.design):
""" """
return 1 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): def add_active_contacts(self):
""" """
Add the active contacts to the transistor. Add the active contacts to the transistor.
""" """
drain_positions = []
source_positions = []
[source_positions,drain_positions] = self.get_contact_positions() # Keep a list of the source/drain contacts
self.source_contacts = []
self.drain_contacts = []
for pos in source_positions: # First one is always a SOURCE
contact=self.add_via_center(layers=self.active_stack, 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, offset=pos,
size=(1, self.num_contacts), width=contact.mod.second_layer_width,
directions=("V","V"), height=contact.mod.second_layer_height)
implant_type=self.implant_type, self.source_contacts.append(contact)
well_type=self.well_type) source_positions.append(pos)
self.add_layout_pin_rect_center(text="S",
layer="m1", # 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, offset=pos,
width=contact.mod.second_layer_width, size=(1, self.num_contacts),
height=contact.mod.second_layer_height) 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)
if label == "S":
self.source_contacts.append(contact)
else:
self.drain_contacts.append(contact)
for pos in drain_positions: pos = vector(self.active_offset.x + self.active_width - 0.5 * self.active_contact.width,
contact=self.add_via_center(layers=self.active_stack, 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, offset=pos,
size=(1, self.num_contacts), width=contact.mod.second_layer_width,
directions=("V","V"), height=contact.mod.second_layer_height)
implant_type=self.implant_type, if label == "S":
well_type=self.well_type) self.source_contacts.append(contact)
self.add_layout_pin_rect_center(text="D", else:
layer="m1", self.drain_contacts.append(contact)
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
if self.connect_active: if self.connect_active:
self.connect_fingered_active(drain_positions, source_positions) self.connect_fingered_active(drain_positions, source_positions)

View File

@ -5,12 +5,10 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import sys
import datetime import datetime
import getpass
import debug import debug
from globals import OPTS, print_time from globals import OPTS, print_time
from sram_config import sram_config
class sram(): class sram():
""" """
@ -40,7 +38,7 @@ class sram():
elif self.num_banks == 2: elif self.num_banks == 2:
from sram_2bank import sram_2bank as sram from sram_2bank import sram_2bank as sram
else: 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() self.s.create_netlist()
@ -50,20 +48,18 @@ class sram():
if not OPTS.is_unit_test: if not OPTS.is_unit_test:
print_time("SRAM creation", datetime.datetime.now(), start_time) print_time("SRAM creation", datetime.datetime.now(), start_time)
def sp_write(self, name):
def sp_write(self,name):
self.s.sp_write(name) self.s.sp_write(name)
def lef_write(self,name): def lef_write(self, name):
self.s.lef_write(name) self.s.lef_write(name)
def gds_write(self,name): def gds_write(self, name):
self.s.gds_write(name) self.s.gds_write(name)
def verilog_write(self,name): def verilog_write(self, name):
self.s.verilog_write(name) self.s.verilog_write(name)
def save(self): def save(self):
""" Save all the output files while reporting time to do it as well. """ """ Save all the output files while reporting time to do it as well. """
@ -108,7 +104,6 @@ class sram():
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file)
print_time("Characterization", datetime.datetime.now(), start_time) print_time("Characterization", datetime.datetime.now(), start_time)
# Write the config file # Write the config file
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
from shutil import copyfile from shutil import copyfile

View File

@ -53,19 +53,19 @@ class sram_1bank(sram_base):
# the sense amps/column mux and cell array) # the sense amps/column mux and cell array)
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
# up to the row address DFFs. # up to the row address DFFs.
control_pos = [None]*len(self.all_ports) control_pos = [None] * len(self.all_ports)
row_addr_pos = [None]*len(self.all_ports) row_addr_pos = [None] * len(self.all_ports)
col_addr_pos = [None]*len(self.all_ports) col_addr_pos = [None] * len(self.all_ports)
wmask_pos = [None]*len(self.all_ports) wmask_pos = [None] * len(self.all_ports)
data_pos = [None]*len(self.all_ports) data_pos = [None] * len(self.all_ports)
if self.write_size: if self.write_size:
max_gap_size = self.m3_pitch*self.word_size + 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 max_gap_size_wmask = self.m2_pitch * max(self.num_wmasks + 1, self.col_addr_size + 1) + 2 * self.m1_pitch
else: else:
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk # 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 # 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
port = 0 port = 0
@ -79,7 +79,7 @@ class sram_1bank(sram_base):
# Add the data flops below the write mask flops. # Add the data flops below the write mask flops.
data_pos[port] = vector(self.bank.bank_array_ll.x, 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]) self.data_dff_insts[port].place(data_pos[port])
else: else:
# Add the data flops below the bank to the right of the lower-left of bank array # 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]) self.data_dff_insts[port].place(data_pos[port])
else: else:
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0) 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 # Add the col address flops below the bank to the left of the lower-left of bank array
if self.col_addr_dff: if self.col_addr_dff:
@ -106,11 +105,11 @@ class sram_1bank(sram_base):
-max_gap_size - self.col_addr_dff_insts[port].height) -max_gap_size - self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port]) self.col_addr_dff_insts[port].place(col_addr_pos[port])
else: 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. # 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, 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.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]) self.control_logic_insts[port].place(control_pos[port])
# The row address bits are placed above the control logic aligned on the right. # 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 # 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, 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") self.data_dff_insts[port].place(data_pos[port], mirror="MX")
else: else:
# Add the data flops above the bank to the left of the upper-right of bank array # 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() col_addr_pos[port] = self.bank_inst.ur()
# This includes 2 M2 pitches for the row addr clock line # 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, 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.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) (self.control_logic_insts[port].height - self.control_logic_insts[port].mod.control_logic_center.y)
+ 2*self.bank.m2_gap) + 2 * self.bank.m2_gap)
#import pdb; pdb.set_trace()
self.control_logic_insts[port].place(control_pos[port], mirror="XY") 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. # 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) row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY") self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY")
def add_layout_pins(self): def add_layout_pins(self):
""" """
Add the top-level pins for a single bank SRAM with control. Add the top-level pins for a single bank SRAM with control.
@ -180,27 +177,38 @@ class sram_1bank(sram_base):
for port in self.all_ports: for port in self.all_ports:
# Connect the control pins as inputs # Connect the control pins as inputs
for signal in self.control_logic_inputs[port] + ["clk"]: 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: if port in self.read_ports:
for bit in range(self.word_size): 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 # Lower address bits
for bit in range(self.col_addr_size): 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 # Upper address bits
for bit in range(self.row_addr_size): 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: if port in self.write_ports:
for bit in range(self.word_size): 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: if self.write_size:
for bit in range(self.num_wmasks): 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): def route_layout(self):
""" Route a single bank SRAM """ """ Route a single bank SRAM """
@ -255,13 +263,15 @@ class sram_1bank(sram_base):
offset=clk_steiner_pos) offset=clk_steiner_pos)
# Note, the via to the control logic is taken care of above # 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: if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center() dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) 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: if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk") 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) 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 # In some designs, the steiner via will be too close to the mid_pos via
# so make the wire as wide as the contacts # 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_path("m2",
self.add_wire(("m3","via2","m2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos]) [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: if self.write_size:
wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk") wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk")
@ -281,7 +294,6 @@ class sram_1bank(sram_base):
self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height)) 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]) self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
def route_control_logic(self): def route_control_logic(self):
""" Route the control logic pins that are not inputs """ """ Route the control logic pins that are not inputs """
@ -291,28 +303,29 @@ class sram_1bank(sram_base):
if "clk" in signal: if "clk" in signal:
continue continue
src_pin = self.control_logic_insts[port].get_pin(signal) 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) self.connect_vbus_m2m3(src_pin, dest_pin)
for port in self.all_ports: for port in self.all_ports:
# Only input (besides pins) is the replica bitline # Only input (besides pins) is the replica bitline
src_pin = self.control_logic_insts[port].get_pin("rbl_bl") src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
self.connect_vbus_m2m3(src_pin, dest_pin)
self.connect_hbus_m2m3(src_pin, dest_pin)
def route_row_addr_dff(self): def route_row_addr_dff(self):
""" Connect the output of the row flops to the bank pins """ """ Connect the output of the row flops to the bank pins """
for port in self.all_ports: for port in self.all_ports:
for bit in range(self.row_addr_size): for bit in range(self.row_addr_size):
flop_name = "dout_{}".format(bit) 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) flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name)
bank_pin = self.bank_inst.get_pin(bank_name) bank_pin = self.bank_inst.get_pin(bank_name)
flop_pos = flop_pin.center() flop_pos = flop_pin.center()
bank_pos = bank_pin.center() bank_pos = bank_pin.center()
mid_pos = vector(bank_pos.x,flop_pos.y) mid_pos = vector(bank_pos.x, flop_pos.y)
self.add_wire(("m3","via2","m2"),[flop_pos, mid_pos,bank_pos]) self.add_wire(("m3", "via2", "m2"),
[flop_pos, mid_pos, bank_pos])
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=flop_pos) offset=flop_pos)
@ -320,9 +333,9 @@ class sram_1bank(sram_base):
""" Connect the output of the col flops to the bank pins """ """ Connect the output of the col flops to the bank pins """
for port in self.all_ports: for port in self.all_ports:
if port%2: 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: 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", col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
@ -335,25 +348,24 @@ class sram_1bank(sram_base):
data_dff_map = zip(dff_names, bus_names) 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) 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) data_bank_map = zip(bank_names, bus_names)
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets) self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets)
def route_data_dff(self): def route_data_dff(self):
""" Connect the output of the data flops to the write driver """ """ Connect the output of the data flops to the write driver """
# This is where the channel will start (y-dimension at least) # This is where the channel will start (y-dimension at least)
for port in self.write_ports: for port in self.write_ports:
if self.write_size: if self.write_size:
if port % 2: 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: else:
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch) offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch)
else: else:
if port%2: if port % 2:
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m1_pitch)
else: 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_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] 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() pin_offset = self.data_dff_insts[port].get_pin(x).center()
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=pin_offset, offset=pin_offset,
directions = ("V", "V")) directions=("V", "V"))
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=pin_offset) offset=pin_offset)
self.add_via_center(layers=self.m3_stack, self.add_via_center(layers=self.m3_stack,
offset=pin_offset) 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] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
if self.write_size: if self.write_size:
for x in bank_names: 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) # This is where the channel will start (y-dimension at least)
for port in self.write_ports: for port in self.write_ports:
if port % 2: 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: else:
offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch) offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
@ -416,13 +428,11 @@ class sram_1bank(sram_base):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=offset_pin) offset=offset_pin)
route_map = list(zip(bank_pins, dff_pins)) route_map = list(zip(bank_pins, dff_pins))
self.create_horizontal_channel_route(netlist=route_map, self.create_horizontal_channel_route(netlist=route_map,
offset=offset, offset=offset,
layer_stack=self.m1_stack) layer_stack=self.m1_stack)
def add_lvs_correspondence_points(self): 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.
@ -438,7 +448,7 @@ class sram_1bank(sram_base):
def graph_exclude_data_dff(self): def graph_exclude_data_dff(self):
"""Removes data dff and wmask dff (if applicable) from search graph. """ """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: for inst in self.data_dff_insts:
self.graph_inst_exclude.add(inst) self.graph_inst_exclude.add(inst)
if self.write_size: if self.write_size:
@ -447,7 +457,7 @@ class sram_1bank(sram_base):
def graph_exclude_addr_dff(self): def graph_exclude_addr_dff(self):
"""Removes data dff from search graph. """ """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: for inst in self.row_addr_dff_insts:
self.graph_inst_exclude.add(inst) self.graph_inst_exclude.add(inst)
@ -457,27 +467,27 @@ class sram_1bank(sram_base):
def graph_exclude_ctrl_dffs(self): def graph_exclude_ctrl_dffs(self):
"""Exclude dffs for CSB, WEB, etc from graph""" """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: for inst in self.control_logic_insts:
inst.mod.graph_exclude_dffs() inst.mod.graph_exclude_dffs()
def get_sen_name(self, sram_name, port=0): def get_sen_name(self, sram_name, port=0):
"""Returns the s_en spice name.""" """Returns the s_en spice name."""
#Naming scheme is hardcoded using this function, should be built into the # Naming scheme is hardcoded using this function, should be built into the
#graph in someway. # graph in someway.
sen_name = "s_en{}".format(port) sen_name = "s_en{}".format(port)
control_conns = self.get_conns(self.control_logic_insts[port]) control_conns = self.get_conns(self.control_logic_insts[port])
#Sanity checks # Sanity checks
if sen_name not in control_conns: if sen_name not in control_conns:
debug.error("Signal={} not contained in control logic connections={}"\ debug.error("Signal={} not contained in control logic connections={}".format(sen_name,
.format(sen_name, control_conns)) control_conns))
if sen_name in self.pins: 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) return "X{}.{}".format(sram_name, sen_name)
def get_cell_name(self, inst_name, row, col): def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell.""" """Gets the spice name of the target bitcell."""
#Sanity check in case it was forgotten # Sanity check in case it was forgotten
if inst_name.find('x') != 0: if inst_name.find('x') != 0:
inst_name = 'x'+inst_name inst_name = 'x' + inst_name
return self.bank_inst.mod.get_cell_name(inst_name+'.x'+self.bank_inst.name, row, col) return self.bank_inst.mod.get_cell_name(inst_name + '.x' + self.bank_inst.name, row, col)

View File

@ -5,20 +5,16 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import sys
import datetime import datetime
import getpass
import debug import debug
from datetime import datetime
from importlib import reload from importlib import reload
from vector import vector from vector import vector
from globals import OPTS, print_time from globals import OPTS, print_time
import logical_effort
from design import design from design import design
from verilog import verilog from verilog import verilog
from lef import lef from lef import lef
from sram_factory import factory from sram_factory import factory
import logical_effort
class sram_base(design, verilog, lef): class sram_base(design, verilog, lef):
""" """
@ -36,11 +32,11 @@ class sram_base(design, verilog, lef):
self.bank_insts = [] self.bank_insts = []
if self.write_size: 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: else:
self.num_wmasks = 0 self.num_wmasks = 0
#For logical effort delay calculations. # For logical effort delay calculations.
self.all_mods_except_control_done = False self.all_mods_except_control_done = False
def add_pins(self): def add_pins(self):
@ -48,11 +44,11 @@ class sram_base(design, verilog, lef):
for port in self.write_ports: for port in self.write_ports:
for bit in range(self.word_size): 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 port in self.all_ports:
for bit in range(self.addr_size): 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 # These are used to create the physical pins
self.control_logic_inputs = [] 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()) self.control_logic_outputs.append(self.control_logic_r.get_outputs())
for port in self.all_ports: 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: 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: 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 # add the optional write mask pins
for port in self.write_ports: for port in self.write_ports:
for bit in range(self.num_wmasks): 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 port in self.read_ports:
for bit in range(self.word_size): 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): def create_netlist(self):
""" Netlist creation """ """ Netlist creation """
start_time = datetime.now() start_time = datetime.datetime.now()
# Must create the control logic before pins to get the pins # Must create the control logic before pins to get the pins
self.add_modules() self.add_modules()
@ -100,23 +95,21 @@ class sram_base(design, verilog, lef):
self.width=0 self.width=0
self.height=0 self.height=0
if not OPTS.is_unit_test: 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): def create_layout(self):
""" Layout creation """ """ Layout creation """
start_time = datetime.now() start_time = datetime.datetime.now()
self.place_instances() self.place_instances()
if not OPTS.is_unit_test: 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_layout()
self.route_supplies() self.route_supplies()
if not OPTS.is_unit_test: 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() self.add_lvs_correspondence_points()
@ -126,14 +119,14 @@ class sram_base(design, verilog, lef):
self.width = highest_coord[0] self.width = highest_coord[0]
self.height = highest_coord[1] 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 # We only enable final verification if we have routed the design
self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True) self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True)
if not OPTS.is_unit_test: 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): def create_modules(self):
debug.error("Must override pure virtual function.",-1) debug.error("Must override pure virtual function.", -1)
def route_supplies(self): def route_supplies(self):
""" Route the supply grid and connect the pins to them. """ """ 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 # Copy the pins to the top level
# This will either be used to route or left unconnected. # This will either be used to route or left unconnected.
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst,"gnd") self.copy_power_pins(inst, "gnd")
import tech import tech
if not OPTS.route_supplies: if not OPTS.route_supplies:
@ -165,32 +158,30 @@ class sram_base(design, verilog, lef):
rtr=router(grid_stack, self) rtr=router(grid_stack, self)
rtr.route() rtr.route()
def compute_bus_sizes(self): def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """ """ Compute the independent bus widths shared between two and four bank SRAMs """
# address size + control signals + one-hot bank select signals # 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 # data bus size
self.num_horizontal_line = self.word_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 # vertical bus height depends on 2 or 4 banks
self.data_bus_height = self.m3_pitch*self.num_horizontal_line 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_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.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 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) # 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.") "Bank is too small compared to control logic.")
def add_busses(self): def add_busses(self):
""" Add the horizontal and vertical busses """ """ Add the horizontal and vertical busses """
# Vertical bus # Vertical bus
@ -213,24 +204,22 @@ class sram_base(design, verilog, lef):
names=self.control_bus_names[port], names=self.control_bus_names[port],
length=self.vertical_bus_height) 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", self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=self.addr_bus_offset, offset=self.addr_bus_offset,
names=self.addr_bus_names, names=self.addr_bus_names,
length=self.addr_bus_height)) 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", self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=self.bank_sel_bus_offset, offset=self.bank_sel_bus_offset,
names=self.bank_sel_bus_names, names=self.bank_sel_bus_names,
length=self.vertical_bus_height)) length=self.vertical_bus_height))
# Horizontal data bus # 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", self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3",
pitch=self.m3_pitch, pitch=self.m3_pitch,
offset=self.data_bus_offset, offset=self.data_bus_offset,
@ -249,7 +238,7 @@ class sram_base(design, verilog, lef):
# the decoder in 4-bank SRAMs # the decoder in 4-bank SRAMs
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1", self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch, 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"], names=["gnd"],
length=self.supply_bus_width)) length=self.supply_bus_width))
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1", self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
@ -258,21 +247,18 @@ class sram_base(design, verilog, lef):
names=self.control_bus_names[port], names=self.control_bus_names[port],
length=self.control_bus_width)) length=self.control_bus_width))
def add_multi_bank_modules(self): def add_multi_bank_modules(self):
""" Create the multibank address flops and bank decoder """ """ Create the multibank address flops and bank decoder """
from dff_buf_array import dff_buf_array from dff_buf_array import dff_buf_array
self.msb_address = dff_buf_array(name="msb_address", self.msb_address = dff_buf_array(name="msb_address",
rows=1, rows=1,
columns=self.num_banks/2) columns=self.num_banks / 2)
self.add_mod(self.msb_address) self.add_mod(self.msb_address)
if self.num_banks>2: if self.num_banks>2:
self.msb_decoder = self.bank.decoder.pre2_4 self.msb_decoder = self.bank.decoder.pre2_4
self.add_mod(self.msb_decoder) self.add_mod(self.msb_decoder)
def add_modules(self): def add_modules(self):
self.bitcell = factory.create(module_type=OPTS.bitcell) self.bitcell = factory.create(module_type=OPTS.bitcell)
self.dff = factory.create(module_type="dff") self.dff = factory.create(module_type="dff")
@ -294,7 +280,6 @@ class sram_base(design, verilog, lef):
self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks) self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks)
self.add_mod(self.wmask_dff) self.add_mod(self.wmask_dff)
# Create the bank module (up to four are instantiated) # Create the bank module (up to four are instantiated)
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
self.add_mod(self.bank) self.add_mod(self.bank)
@ -305,7 +290,8 @@ class sram_base(design, verilog, lef):
self.bank_count = 0 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 self.all_mods_except_control_done = True
c = reload(__import__(OPTS.control_logic)) c = reload(__import__(OPTS.control_logic))
@ -334,7 +320,7 @@ class sram_base(design, verilog, lef):
port_type="r") port_type="r")
self.add_mod(self.control_logic_r) self.add_mod(self.control_logic_r)
def create_bank(self,bank_num): def create_bank(self, bank_num):
""" Create a bank """ """ Create a bank """
self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num),
mod=self.bank)) mod=self.bank))
@ -342,18 +328,18 @@ class sram_base(design, verilog, lef):
temp = [] temp = []
for port in self.read_ports: for port in self.read_ports:
for bit in range(self.word_size): 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: for port in self.all_ports:
temp.append("rbl_bl{0}".format(port)) temp.append("rbl_bl{0}".format(port))
for port in self.write_ports: for port in self.write_ports:
for bit in range(self.word_size): 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 port in self.all_ports:
for bit in range(self.bank_addr_size): 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): if(self.num_banks > 1):
for port in self.all_ports: 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: for port in self.read_ports:
temp.append("s_en{0}".format(port)) temp.append("s_en{0}".format(port))
for port in self.all_ports: for port in self.all_ports:
@ -369,7 +355,6 @@ class sram_base(design, verilog, lef):
return self.bank_insts[-1] return self.bank_insts[-1]
def place_bank(self, bank_inst, position, x_flip, y_flip): def place_bank(self, bank_inst, position, x_flip, y_flip):
""" Place a bank at the given position with orientations """ """ Place a bank at the given position with orientations """
@ -400,7 +385,6 @@ class sram_base(design, verilog, lef):
return bank_inst return bank_inst
def create_row_addr_dff(self): def create_row_addr_dff(self):
""" Add all address flops for the main decoder """ """ Add all address flops for the main decoder """
insts = [] insts = []
@ -412,14 +396,13 @@ class sram_base(design, verilog, lef):
inputs = [] inputs = []
outputs = [] outputs = []
for bit in range(self.row_addr_size): for bit in range(self.row_addr_size):
inputs.append("addr{}[{}]".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)) outputs.append("a{}[{}]".format(port, bit + self.col_addr_size))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
return insts return insts
def create_col_addr_dff(self): def create_col_addr_dff(self):
""" Add and place all address flops for the column decoder """ """ Add and place all address flops for the column decoder """
insts = [] insts = []
@ -431,14 +414,13 @@ class sram_base(design, verilog, lef):
inputs = [] inputs = []
outputs = [] outputs = []
for bit in range(self.col_addr_size): for bit in range(self.col_addr_size):
inputs.append("addr{}[{}]".format(port,bit)) inputs.append("addr{}[{}]".format(port, bit))
outputs.append("a{}[{}]".format(port,bit)) outputs.append("a{}[{}]".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
return insts return insts
def create_data_dff(self): def create_data_dff(self):
""" Add and place all data flops """ """ Add and place all data flops """
insts = [] insts = []
@ -454,8 +436,8 @@ class sram_base(design, verilog, lef):
inputs = [] inputs = []
outputs = [] outputs = []
for bit in range(self.word_size): for bit in range(self.word_size):
inputs.append("din{}[{}]".format(port,bit)) inputs.append("din{}[{}]".format(port, bit))
outputs.append("bank_din{}[{}]".format(port,bit)) outputs.append("bank_din{}[{}]".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"]) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
@ -483,7 +465,6 @@ class sram_base(design, verilog, lef):
return insts return insts
def create_control_logic(self): def create_control_logic(self):
""" Add control logic instances """ """ Add control logic instances """
@ -516,12 +497,13 @@ class sram_base(design, verilog, lef):
return insts return insts
def connect_vbus_m2m3(self, src_pin, dest_pin): 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. Routes horizontal then vertical L shape.
Dest pin is assumed to be on M2. 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(): if src_pin.cx()<dest_pin.cx():
in_pos = src_pin.rc() in_pos = src_pin.rc()
@ -533,15 +515,51 @@ class sram_base(design, verilog, lef):
out_pos = dest_pin.uc() out_pos = dest_pin.uc()
# move horizontal first # 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": if src_pin.layer=="m1":
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=in_pos) 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, self.add_via_center(layers=self.m2_stack,
offset=in_pos) 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): def sp_write(self, sp_name):
# Write the entire spice of the object to the file # 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""" """Get the all the stage efforts for each stage in the path from clk_buf to a wordline"""
stage_effort_list = [] stage_effort_list = []
#Clk_buf originates from the control logic so only the bank is related to the wordline path # 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. # 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) stage_effort_list += self.bank.determine_wordline_stage_efforts(external_wordline_cout, inp_is_rise)
return stage_effort_list return stage_effort_list
def get_wl_en_cin(self): def get_wl_en_cin(self):
"""Gets the capacitive load the of clock (clk_buf) for the sram""" """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() return self.bank.get_wl_en_cin()
def get_w_en_cin(self): def get_w_en_cin(self):
"""Gets the capacitive load the of write enable (w_en) for the sram""" """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() return self.bank.get_w_en_cin()
def get_p_en_bar_cin(self): def get_p_en_bar_cin(self):
"""Gets the capacitive load the of precharge enable (p_en_bar) for the sram""" """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() return self.bank.get_p_en_bar_cin()
def get_clk_bar_cin(self): def get_clk_bar_cin(self):
"""Gets the capacitive load the of clock (clk_buf_bar) for the sram""" """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. # 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) # Only the precharge cells use this signal (other than the control logic)
return self.bank.get_clk_bar_cin() return self.bank.get_clk_bar_cin()
def get_sen_cin(self): def get_sen_cin(self):
"""Gets the capacitive load the of sense amp enable for the sram""" """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() return self.bank.get_sen_cin()
def get_dff_clk_buf_cin(self): def get_dff_clk_buf_cin(self):
"""Get the relative capacitance of the clk_buf signal. """Get the relative capacitance of the clk_buf signal.
Does not get the control logic loading but everything else""" Does not get the control logic loading but everything else"""

View File

@ -15,6 +15,7 @@ from globals import OPTS
from sram_factory import factory from sram_factory import factory
import debug 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): class psram_1bank_4mux_1rw_1r_test(openram_test):
def runTest(self): def runTest(self):