Merging latest changes from multiport with changes made to pbitcell. Changing select code from other modules and tests to reflect changes made to pbitcell.

This commit is contained in:
Michael Timothy Grimes
2018-09-06 19:36:50 -07:00
62 changed files with 413 additions and 363 deletions
+36 -16
View File
@@ -116,6 +116,11 @@ def init_openram(config_file, is_unit_test=True):
import_tech()
init_paths()
# This depends on the tech, so do it after tech is loaded
init_config()
# Reset the static duplicate name checker for unit tests.
import hierarchy_design
hierarchy_design.hierarchy_design.name_map=[]
@@ -229,15 +234,6 @@ def read_config(config_file, is_unit_test=True):
OPTS.num_r_ports,
OPTS.tech_name)
# Don't delete the output dir, it may have other files!
# make the directory if it doesn't exist
try:
os.makedirs(OPTS.output_path, 0o750)
except OSError as e:
if e.errno == 17: # errno.EEXIST
os.chmod(OPTS.output_path, 0o750)
except:
debug.error("Unable to make output directory.",-1)
def end_openram():
@@ -299,12 +295,6 @@ def setup_paths():
cleanup_paths()
# make the directory if it doesn't exist
try:
os.makedirs(OPTS.openram_temp, 0o750)
except OSError as e:
if e.errno == 17: # errno.EEXIST
os.chmod(OPTS.openram_temp, 0o750)
def is_exe(fpath):
@@ -320,7 +310,37 @@ def find_exe(check_exe):
if is_exe(exe):
return exe
return None
def init_paths():
""" Create the temp and output directory if it doesn't exist """
# make the directory if it doesn't exist
try:
os.makedirs(OPTS.openram_temp, 0o750)
except OSError as e:
if e.errno == 17: # errno.EEXIST
os.chmod(OPTS.openram_temp, 0o750)
# Don't delete the output dir, it may have other files!
# make the directory if it doesn't exist
try:
os.makedirs(OPTS.output_path, 0o750)
except OSError as e:
if e.errno == 17: # errno.EEXIST
os.chmod(OPTS.output_path, 0o750)
except:
debug.error("Unable to make output directory.",-1)
def init_config():
""" Initialize the SRAM configurations. """
# Create the SRAM configuration
from sram_config import sram_config
OPTS.sram_config = sram_config(OPTS.word_size,
OPTS.num_words,
OPTS.num_banks)
# imports correct technology directories for testing
def import_tech():
global OPTS
+4
View File
@@ -29,6 +29,10 @@ class bank(design.design):
design.design.__init__(self, name)
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words))
self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports
self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports
self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
# 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
-10
View File
@@ -50,16 +50,6 @@ class bitcell(design.design):
row_pins = ["wl"]
return row_pins
def list_read_wl_names(self):
""" Creates a list of wordline pin names associated with read ports """
row_pins = ["wl"]
return row_pins
def list_write_wl_names(self):
""" Creates a list of wordline pin names associated with write ports """
row_pins = ["wl"]
return row_pins
def list_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = ["bl", "br"]
-5
View File
@@ -53,11 +53,6 @@ class delay_chain(design.design):
self.add_pin("gnd")
def add_modules(self):
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
self.inv = pinv(route_output=False)
self.add_mod(self.inv)
+3 -2
View File
@@ -24,7 +24,8 @@ class hierarchical_decoder(design.design):
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell_height = self.mod_bitcell.height
b = self.mod_bitcell()
self.bitcell_height = b.height
self.NAND_FORMAT = "DEC_NAND[{0}]"
self.INV_FORMAT = "DEC_INV_[{0}]"
@@ -130,7 +131,7 @@ class hierarchical_decoder(design.design):
self.total_number_of_predecoder_outputs = 4*self.no_of_pre2x4 + 8*self.no_of_pre3x8
else:
self.total_number_of_predecoder_outputs = 0
debug.error("Not enough rows for a hierarchical decoder. Non-hierarchical not supported yet.",-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,
if self.no_of_pre3x8 > 0:
+1 -1
View File
@@ -10,7 +10,7 @@ class replica_bitcell(design.design):
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = ["BL", "BR", "WL", "vdd", "gnd"]
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"], layer["boundary"])
+2 -2
View File
@@ -228,7 +228,7 @@ class replica_bitline(design.design):
# 3. Route the contact of previous route to the bitcell WL
# route bend of previous net to bitcell WL
wl_offset = self.rbc_inst.get_pin("WL").lc()
wl_offset = self.rbc_inst.get_pin("wl").lc()
xmid_point= 0.5*(wl_offset.x+contact_offset.x)
wl_mid1 = vector(xmid_point,contact_offset.y)
wl_mid2 = vector(xmid_point,wl_offset.y)
@@ -247,7 +247,7 @@ class replica_bitline(design.design):
# Route the connection of the source route to the RBL bitline (left)
# Via will go halfway down from the bitcell
bl_offset = self.rbc_inst.get_pin("BL").bc()
bl_offset = self.rbc_inst.get_pin("bl").bc()
# Route down a pitch so we can use M2 routing
bl_down_offset = bl_offset - vector(0, self.m2_pitch)
self.add_path("metal2",[source_offset, bl_down_offset, bl_offset])
+2 -1
View File
@@ -57,10 +57,11 @@ class sense_amp_array(design.design):
self.amp = self.mod_sense_amp("sense_amp")
self.add_mod(self.amp)
# This is just used for measurements,
# so don't add the module
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
self.add_mod(self.bitcell)
def create_sense_amp_array(self):
self.local_insts = []
+7 -5
View File
@@ -50,6 +50,13 @@ class wordline_driver(design.design):
def add_modules(self):
# This is just used for measurements,
# so don't add the module
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
self.inv = pinv()
self.add_mod(self.inv)
@@ -59,11 +66,6 @@ class wordline_driver(design.design):
self.nand2 = pnand2()
self.add_mod(self.nand2)
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
self.add_mod(self.bitcell)
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
+3 -2
View File
@@ -58,11 +58,12 @@ class write_driver_array(design.design):
self.mod_write_driver = getattr(c, OPTS.write_driver)
self.driver = self.mod_write_driver("write_driver")
self.add_mod(self.driver)
# This is just used for measurements,
# so don't add the module
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
self.add_mod(self.bitcell)
def create_write_array(self):
self.driver_insts = {}
+4 -5
View File
@@ -52,14 +52,13 @@ print(*output_files,sep="\n")
start_time = datetime.datetime.now()
print_time("Start",start_time)
# Configure the SRAM organization
c = sram_config(word_size=OPTS.word_size,
num_words=OPTS.num_words,
num_rw_ports=OPTS.num_rw_ports,
num_w_ports=OPTS.num_w_ports,
num_r_ports=OPTS.num_r_ports)
num_words=OPTS.num_words)
# import SRAM test generation
s = sram(c, OPTS.output_name)
s = sram(sram_config=c,
name=OPTS.output_name)
# Output the files for the resulting SRAM
s.save()
+7 -1
View File
@@ -55,11 +55,17 @@ class options(optparse.Values):
num_rw_ports = 1
num_r_ports = 0
num_w_ports = 0
# These will get initialized by the the file
supply_voltages = ""
temperatures = ""
process_corners = ""
# These are the main configuration parameters that should be over-ridden
# in a configuration file.
#num_words = 0
#num_banks = 1
#word_size = 0
# These are the default modules that can be over-riden
decoder = "hierarchical_decoder"
+116 -109
View File
@@ -1,5 +1,4 @@
import contact
import pgate
import design
import debug
from tech import drc, parameter, spice
@@ -7,47 +6,42 @@ from vector import vector
from ptx import ptx
from globals import OPTS
class pbitcell(pgate.pgate):
class pbitcell(design.design):
"""
This module implements a parametrically sized multi-port bitcell,
with a variable number of read/write, write, and read ports
"""
width = None
height = None
unique_id = 1
def __init__(self, num_readwrite=OPTS.num_rw_ports, num_write=OPTS.num_w_ports, num_read=OPTS.num_r_ports):
name = "pbitcell_{0}RW_{1}W_{2}R_{3}".format(num_readwrite, num_write, num_read, pbitcell.unique_id)
pbitcell.unique_id += 1
pgate.pgate.__init__(self, name)
debug.info(2, "create a multi-port bitcell with {0} write ports and {1} read ports".format(num_write, num_read))
def __init__(self):
self.num_readwrite = num_readwrite
self.num_write = num_write
self.num_read = num_read
self.total_ports = num_readwrite + num_write + num_read
self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
name = "pbitcell_{0}RW_{1}W_{2}R".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports)
# This is not a pgate because pgates depend on the bitcell height!
design.design.__init__(self, name)
debug.info(2, "create a multi-port bitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports,
self.num_r_ports))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
# We must always create the bitcell layout because
# some transistor sizes in the other netlists depend on it
self.create_layout()
# Since since pbitcell's size is dependent on port choice, class width and height are set after layout creation
# class width and height are necessary for modules that load bitcell attributes
pbitcell.width = self.width
pbitcell.height = self.height
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_storage()
if(self.num_readwrite > 0):
if(self.num_rw_ports > 0):
self.create_readwrite_ports()
if(self.num_write > 0):
if(self.num_w_ports > 0):
self.create_write_ports()
if(self.num_read > 0):
if(self.num_r_ports > 0):
self.create_read_ports()
def create_layout(self):
@@ -58,18 +52,18 @@ class pbitcell(pgate.pgate):
self.route_storage()
self.route_rails()
if(self.num_readwrite > 0):
if(self.num_rw_ports > 0):
self.place_readwrite_ports()
self.route_readwrite_wordlines()
self.route_readwrite_bitlines()
if(self.num_write == 0): # routing for write to storage is the same as read/write to storage
if(self.num_w_ports == 0): # routing for write to storage is the same as read/write to storage
self.route_readwrite_access()
if(self.num_write > 0):
if(self.num_w_ports > 0):
self.place_write_ports()
self.route_write_wordlines()
self.route_write_bitlines()
self.route_write_access()
if(self.num_read > 0):
if(self.num_r_ports > 0):
self.place_read_ports()
self.route_read_wordlines()
self.route_read_bitlines()
@@ -86,22 +80,24 @@ class pbitcell(pgate.pgate):
self.w_br_names = []
self.r_bl_names = []
self.r_br_names = []
self.wl_names = []
self.rw_wl_names = []
self.w_wl_names = []
self.r_wl_names = []
port = 0
for k in range(self.num_readwrite):
for k in range(self.num_rw_ports):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
self.rw_bl_names.append("bl{}".format(port))
self.rw_br_names.append("br{}".format(port))
port += 1
for k in range(self.num_write):
for k in range(self.num_w_ports):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
self.w_bl_names.append("bl{}".format(port))
self.w_br_names.append("br{}".format(port))
port += 1
for k in range(self.num_read):
for k in range(self.num_r_ports):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
self.r_bl_names.append("bl{}".format(port))
@@ -109,20 +105,30 @@ class pbitcell(pgate.pgate):
port += 1
port = 0
for k in range(self.total_ports):
for k in range(self.num_rw_ports):
self.add_pin("wl{}".format(port))
self.wl_names.append("wl{}".format(port))
self.rw_wl_names.append("wl{}".format(port))
port += 1
for k in range(self.num_w_ports):
self.add_pin("wl{}".format(port))
self.w_wl_names.append("wl{}".format(port))
port += 1
for k in range(self.num_r_ports):
self.add_pin("wl{}".format(port))
self.r_wl_names.append("wl{}".format(port))
port += 1
self.add_pin("vdd")
self.add_pin("gnd")
def add_modules(self):
"""
Determine size of transistors and add ptx modules
"""
# if there are any read/write ports, then the inverter nmos is sized based the number of read/write ports
if(self.num_readwrite > 0):
inverter_nmos_width = self.num_readwrite*3*parameter["min_tx_size"]
if(self.num_rw_ports > 0):
inverter_nmos_width = self.num_rw_ports*3*parameter["min_tx_size"]
inverter_pmos_width = parameter["min_tx_size"]
readwrite_nmos_width = 1.5*parameter["min_tx_size"]
write_nmos_width = parameter["min_tx_size"]
@@ -197,7 +203,7 @@ class pbitcell(pgate.pgate):
# write to read transistor spacing (also acts as readwrite to read transistor spacing)
# calculation is dependent on whether the read transistor is adjacent to a write transistor or a readwrite transistor
if(self.num_write > 0):
if(self.num_w_ports > 0):
if(self.write_nmos_contact_extension > self.gate_contact_thres):
write_portion = drc["minwidth_metal2"] + self.write_nmos_contact_extension
else:
@@ -236,23 +242,23 @@ class pbitcell(pgate.pgate):
Calculate positions that describe the edges and dimensions of the cell
"""
# create flags for excluding readwrite, write, or read port calculations if they are not included in the bitcell
if(self.num_readwrite > 0):
if(self.num_rw_ports > 0):
self.readwrite_port_flag = True
else:
self.readwrite_port_flag = False
if(self.num_write > 0):
if(self.num_w_ports > 0):
self.write_port_flag = True
else:
self.write_port_flag = False
if(self.num_read > 0):
if(self.num_r_ports > 0):
self.read_port_flag = True
else:
self.read_port_flag = False
# determine the distance of the leftmost/rightmost transistor gate connection
if (self.num_read > 0):
if (self.num_r_ports > 0):
if(self.read_nmos_contact_extension > self.gate_contact_thres):
end_connection = drc["minwidth_metal2"] + self.read_nmos_contact_extension + contact.m1m2.height
else:
@@ -266,11 +272,11 @@ class pbitcell(pgate.pgate):
# leftmost position = storage width + read/write ports width + write ports width + read ports width + end transistor gate connections + metal spacing necessary for tiling the bitcell
self.leftmost_xpos = -self.inverter_tile_width \
- self.inverter_to_write_spacing \
- self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_readwrite-1)*self.readwrite_tile_width) \
- self.readwrite_port_flag*(self.readwrite_nmos.active_height + (self.num_rw_ports-1)*self.readwrite_tile_width) \
- self.write_port_flag*self.readwrite_port_flag*self.write_to_write_spacing \
- self.write_port_flag*(self.write_nmos.active_height + (self.num_write-1)*self.write_tile_width) \
- self.write_port_flag*(self.write_nmos.active_height + (self.num_w_ports-1)*self.write_tile_width) \
- self.read_port_flag*self.write_to_read_spacing \
- self.read_port_flag*(self.read_nmos.active_height + (self.num_read-1)*self.read_tile_width) \
- self.read_port_flag*(self.read_nmos.active_height + (self.num_r_ports-1)*self.read_tile_width) \
- end_connection \
- 0.5*drc["poly_to_polycontact"]
@@ -279,9 +285,9 @@ class pbitcell(pgate.pgate):
# bottommost position = gnd height + rwwl height + wwl height + rwl height + space needed between tiled bitcells
array_tiling_offset = 0.5*drc["minwidth_metal2"]
self.botmost_ypos = -self.rail_tile_height \
- self.num_readwrite*self.rowline_tile_height \
- self.num_write*self.rowline_tile_height \
- self.num_read*self.rowline_tile_height \
- self.num_rw_ports*self.rowline_tile_height \
- self.num_w_ports*self.rowline_tile_height \
- self.num_r_ports*self.rowline_tile_height \
- array_tiling_offset
# topmost position = height of the inverter + height of vdd
@@ -420,19 +426,19 @@ class pbitcell(pgate.pgate):
"""
# define write transistor variables as empty arrays based on the number of write ports
self.readwrite_nmos_left = [None] * self.num_readwrite
self.readwrite_nmos_right = [None] * self.num_readwrite
self.readwrite_nmos_left = [None] * self.num_rw_ports
self.readwrite_nmos_right = [None] * self.num_rw_ports
# iterate over the number of read/write ports
for k in range(0,self.num_readwrite):
for k in range(0,self.num_rw_ports):
# add read/write transistors
self.readwrite_nmos_left[k] = self.add_inst(name="readwrite_nmos_left{}".format(k),
mod=self.readwrite_nmos)
self.connect_inst(["Q", "rwwl{}".format(k), "rwbl{}".format(k), "gnd"])
self.connect_inst(["Q", self.rw_wl_names[k], self.rw_bl_names[k], "gnd"])
self.readwrite_nmos_right[k] = self.add_inst(name="readwrite_nmos_right{}".format(k),
mod=self.readwrite_nmos)
self.connect_inst(["Q_bar", "rwwl{}".format(k), "rwbl_bar{}".format(k), "gnd"])
self.connect_inst(["Q_bar", self.rw_wl_names[k], self.rw_br_names[k], "gnd"])
def place_readwrite_ports(self):
@@ -441,15 +447,15 @@ class pbitcell(pgate.pgate):
"""
# Define variables relevant to write transistors
self.rwwl_positions = [None] * self.num_readwrite
self.rwbl_positions = [None] * self.num_readwrite
self.rwbl_bar_positions = [None] * self.num_readwrite
self.rwwl_positions = [None] * self.num_rw_ports
self.rwbl_positions = [None] * self.num_rw_ports
self.rwbl_bar_positions = [None] * self.num_rw_ports
# define offset correction due to rotation of the ptx module
readwrite_rotation_correct = self.readwrite_nmos.active_height
# iterate over the number of read/write ports
for k in range(0,self.num_readwrite):
for k in range(0,self.num_rw_ports):
# Add transistors
# calculate read/write transistor offsets
left_readwrite_transistor_xpos = self.left_building_edge \
@@ -504,14 +510,14 @@ class pbitcell(pgate.pgate):
"""
Routes read/write trnasistors to their respective wordlines
"""
for k in range(0,self.num_readwrite):
for k in range(0,self.num_rw_ports):
# Gate/RWWL connections
# add poly-to-meltal2 contacts to connect gate of read/write transistors to RWWL (contact next to gate)
# contact must be placed a metal1 width below the source pin to avoid drc from source pin routings
if(self.readwrite_nmos_contact_extension > self.gate_contact_thres):
contact_xpos = self.readwrite_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width
else:
contact_xpos = left_readwrite_transistor_xpos - self.readwrite_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width
contact_xpos = self.readwrite_nmos_left[k].offset.x - self.readwrite_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width
contact_ypos = self.readwrite_nmos_left[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height
left_gate_contact = vector(contact_xpos, contact_ypos)
@@ -523,7 +529,7 @@ class pbitcell(pgate.pgate):
if(self.readwrite_nmos_contact_extension > self.gate_contact_thres):
contact_xpos = self.readwrite_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width
else:
contact_xpos = right_readwrite_transistor_xpos + drc["poly_to_active"] + 0.5*contact.poly.width
contact_xpos = self.readwrite_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width
contact_ypos = self.readwrite_nmos_right[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height
right_gate_contact = vector(contact_xpos, contact_ypos)
@@ -559,7 +565,7 @@ class pbitcell(pgate.pgate):
"""
Routes read/write transistors to their respective bitlines
"""
for k in range(0,self.num_readwrite):
for k in range(0,self.num_rw_ports):
# Source/RWBL/RWBL_bar connections
# add metal1-to-metal2 contacts on top of read/write transistor source pins for connection to WBL and WBL_bar
offset_left = self.readwrite_nmos_left[k].get_pin("S").center()
@@ -577,7 +583,7 @@ class pbitcell(pgate.pgate):
"""
Routes read/write transistors to the storage component of the bitcell
"""
last_inst = self.num_readwrite - 1
last_inst = self.num_rw_ports - 1
# Drain/Storage connections
# this path only needs to be drawn once on the last iteration of the loop
@@ -624,19 +630,19 @@ class pbitcell(pgate.pgate):
write_rotation_correct = self.write_nmos.active_height
# define write transistor variables as empty arrays based on the number of write ports
self.write_nmos_left = [None] * self.num_write
self.write_nmos_right = [None] * self.num_write
self.write_nmos_left = [None] * self.num_w_ports
self.write_nmos_right = [None] * self.num_w_ports
# iterate over the number of write ports
for k in range(0,self.num_write):
for k in range(0,self.num_w_ports):
# add write transistors
self.write_nmos_left[k] = self.add_inst(name="write_nmos_left{}".format(k),
mod=self.write_nmos)
self.connect_inst(["Q", "wwl{}".format(k), "wbl{}".format(k), "gnd"])
self.connect_inst(["Q", self.w_wl_names[k], self.w_bl_names[k], "gnd"])
self.write_nmos_right[k] = self.add_inst(name="write_nmos_right{}".format(k),
mod=self.write_nmos)
self.connect_inst(["Q_bar", "wwl{}".format(k), "wbl_bar{}".format(k), "gnd"])
self.connect_inst(["Q_bar", self.w_wl_names[k], self.w_br_names[k], "gnd"])
def place_write_ports(self):
@@ -644,15 +650,15 @@ class pbitcell(pgate.pgate):
Places write ports in the bit cell.
"""
# Define variables relevant to write transistors
self.wwl_positions = [None] * self.num_write
self.wbl_positions = [None] * self.num_write
self.wbl_bar_positions = [None] * self.num_write
self.wwl_positions = [None] * self.num_w_ports
self.wbl_positions = [None] * self.num_w_ports
self.wbl_bar_positions = [None] * self.num_w_ports
# define offset correction due to rotation of the ptx module
write_rotation_correct = self.write_nmos.active_height
# iterate over the number of write ports
for k in range(0,self.num_write):
for k in range(0,self.num_w_ports):
# Add transistors
# calculate write transistor offsets
left_write_transistor_xpos = self.left_building_edge \
@@ -677,7 +683,7 @@ class pbitcell(pgate.pgate):
# Add WWL lines
# calculate WWL position
wwl_ypos = self.gnd_position.y \
- self.num_readwrite*self.rowline_tile_height \
- self.num_rw_ports*self.rowline_tile_height \
- (k+1)*self.rowline_tile_height
self.wwl_positions[k] = vector(self.leftmost_xpos, wwl_ypos)
@@ -711,14 +717,14 @@ class pbitcell(pgate.pgate):
"""
Routes write transistors to their respective wordlines
"""
for k in range(0,self.num_write):
for k in range(0,self.num_w_ports):
# Gate/WWL connections
# add poly-to-meltal2 contacts to connect gate of write transistors to WWL (contact next to gate)
# contact must be placed a metal width below the source pin to avoid drc from source pin routings
if(self.write_nmos_contact_extension > self.gate_contact_thres):
contact_xpos = self.write_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width
else:
contact_xpos = left_write_transistor_xpos - self.write_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width
contact_xpos = self.write_nmos_left[k].offset.x - self.write_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width
contact_ypos = self.write_nmos_left[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height
left_gate_contact = vector(contact_xpos, contact_ypos)
@@ -730,7 +736,7 @@ class pbitcell(pgate.pgate):
if(self.write_nmos_contact_extension > self.gate_contact_thres):
contact_xpos = self.write_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width
else:
contact_xpos = right_write_transistor_xpos + drc["poly_to_active"] + 0.5*contact.poly.width
contact_xpos = self.write_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width
contact_ypos = self.write_nmos_right[k].get_pin("D").bc().y - drc["minwidth_metal1"] - 0.5*contact.m1m2.height
right_gate_contact = vector(contact_xpos, contact_ypos)
@@ -765,7 +771,7 @@ class pbitcell(pgate.pgate):
"""
Routes write transistors to their respective bitlines
"""
for k in range(0,self.num_write):
for k in range(0,self.num_w_ports):
# Source/WBL/WBL_bar connections
# add metal1-to-metal2 contacts on top of write transistor source pins for connection to WBL and WBL_bar
offset_left = self.write_nmos_left[k].get_pin("S").center()
@@ -782,7 +788,7 @@ class pbitcell(pgate.pgate):
"""
Routes write transistors to the storage component of the bitcell
"""
last_inst = self.num_write - 1
last_inst = self.num_w_ports - 1
# Drain/Storage connections
# this path only needs to be drawn once on the last iteration of the loop
@@ -829,13 +835,13 @@ class pbitcell(pgate.pgate):
"""
# define read transistor variables as empty arrays based on the number of read ports
self.read_nmos_left = [None] * self.num_read
self.read_nmos_right = [None] * self.num_read
self.read_access_nmos_left = [None] * self.num_read
self.read_access_nmos_right = [None] * self.num_read
self.read_nmos_left = [None] * self.num_r_ports
self.read_nmos_right = [None] * self.num_r_ports
self.read_access_nmos_left = [None] * self.num_r_ports
self.read_access_nmos_right = [None] * self.num_r_ports
# iterate over the number of read ports
for k in range(0,self.num_read):
for k in range(0,self.num_r_ports):
# add read-access transistors
self.read_access_nmos_left[k] = self.add_inst(name="read_access_nmos_left{}".format(k),
mod=self.read_nmos)
@@ -848,20 +854,20 @@ class pbitcell(pgate.pgate):
# add read transistors
self.read_nmos_left[k] = self.add_inst(name="read_nmos_left{}".format(k),
mod=self.read_nmos)
self.connect_inst(["rbl{}".format(k), "rwl{}".format(k), "RA_to_R_left{}".format(k), "gnd"])
self.connect_inst([self.r_bl_names[k], self.r_wl_names[k], "RA_to_R_left{}".format(k), "gnd"])
self.read_nmos_right[k] = self.add_inst(name="read_nmos_right{}".format(k),
mod=self.read_nmos)
self.connect_inst(["rbl_bar{}".format(k), "rwl{}".format(k), "RA_to_R_right{}".format(k), "gnd"])
self.connect_inst([self.r_br_names[k], self.r_wl_names[k], "RA_to_R_right{}".format(k), "gnd"])
def place_read_ports(self):
"""
Places the read ports in the bit cell.
"""
# Define variables relevant to read transistors
self.rwl_positions = [None] * self.num_read
self.rbl_positions = [None] * self.num_read
self.rbl_bar_positions = [None] * self.num_read
self.rwl_positions = [None] * self.num_r_ports
self.rbl_positions = [None] * self.num_r_ports
self.rbl_bar_positions = [None] * self.num_r_ports
# define offset correction due to rotation of the ptx module
read_rotation_correct = self.read_nmos.active_height
@@ -870,7 +876,7 @@ class pbitcell(pgate.pgate):
overlap_offset = self.read_nmos.get_pin("D").ll() - self.read_nmos.get_pin("S").ll()
# iterate over the number of read ports
for k in range(0,self.num_read):
for k in range(0,self.num_r_ports):
# Add transistors
# calculate transistor offsets
left_read_transistor_xpos = self.left_building_edge \
@@ -900,8 +906,8 @@ class pbitcell(pgate.pgate):
# Add RWL lines
# calculate RWL position
rwl_ypos = self.gnd_position.y \
- self.num_readwrite*self.rowline_tile_height \
- self.num_write*self.rowline_tile_height \
- self.num_rw_ports*self.rowline_tile_height \
- self.num_w_ports*self.rowline_tile_height \
- (k+1)*self.rowline_tile_height
self.rwl_positions[k] = vector(self.leftmost_xpos, rwl_ypos)
@@ -931,13 +937,13 @@ class pbitcell(pgate.pgate):
"""
Routes read transistors to their respective worlines
"""
for k in range(0,self.num_read):
for k in range(0,self.num_r_ports):
# Gate of read transistor / RWL connection
# add poly-to-meltal2 contacts to connect gate of read transistors to RWL (contact next to gate)
if(self.read_nmos_contact_extension > self.gate_contact_thres):
contact_xpos = self.read_nmos_left[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width
else:
contact_xpos = left_read_transistor_xpos - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width
contact_xpos = self.read_nmos_left[k].offset.x - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width
contact_ypos = self.read_nmos_left[k].get_pin("G").lc().y
left_gate_contact = vector(contact_xpos, contact_ypos)
@@ -949,7 +955,7 @@ class pbitcell(pgate.pgate):
if(self.read_nmos_contact_extension > self.gate_contact_thres):
contact_xpos = self.read_nmos_right[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width
else:
contact_xpos = right_read_transistor_xpos + drc["poly_to_active"] + 0.5*contact.poly.width
contact_xpos = self.read_nmos_right[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width
contact_ypos = self.read_nmos_right[k].get_pin("G").rc().y
right_gate_contact = vector(contact_xpos, contact_ypos)
@@ -989,7 +995,7 @@ class pbitcell(pgate.pgate):
"""
Routes read transistors to their respective bitlines
"""
for k in range(0,self.num_read):
for k in range(0,self.num_r_ports):
# Drain of read transistor / RBL & RBL_bar connection
# add metal1-to-metal2 contacts on top of read transistor drain pins for connection to RBL and RBL_bar
offset_left = self.read_nmos_left[k].get_pin("D").center()
@@ -1006,13 +1012,13 @@ class pbitcell(pgate.pgate):
"""
Routes read access transistors to the storage component of the bitcell
"""
for k in range(0,self.num_read):
for k in range(0,self.num_r_ports):
# Gate of read-access transistor / storage connection
# add poly-to-metal1 contacts to connect gate of read-access transistors to output of inverters (contact next to gate)
if(self.read_nmos_contact_extension > self.gate_contact_thres):
contact_xpos = self.read_nmos_left[k].get_pin("S").rc().x + drc["minwidth_metal2"] + 0.5*contact.m1m2.width
else:
contact_xpos = left_read_transistor_xpos + drc["poly_to_active"] + 0.5*contact.poly.width
contact_xpos = self.read_nmos_left[k].offset.x + drc["poly_to_active"] + 0.5*contact.poly.width
contact_ypos = self.read_access_nmos_left[k].get_pin("G").rc().y
left_gate_contact = vector(contact_xpos, contact_ypos)
@@ -1022,7 +1028,7 @@ class pbitcell(pgate.pgate):
if(self.read_nmos_contact_extension > self.gate_contact_thres):
contact_xpos = self.read_nmos_right[k].get_pin("S").lc().x - drc["minwidth_metal2"] - 0.5*contact.m1m2.width
else:
contact_xpos = right_read_transistor_xpos - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width
contact_xpos = self.read_nmos_right[k].offset.x - self.read_nmos.active_height - drc["poly_to_active"] - 0.5*contact.poly.width
contact_ypos = self.read_access_nmos_right[k].get_pin("G").lc().y
right_gate_contact = vector(contact_xpos, contact_ypos)
@@ -1073,7 +1079,7 @@ class pbitcell(pgate.pgate):
# extend pwell over read/write and write transistors to the
# height of the write transistor well (read/write and write
# transistors are the same height)
if(self.num_write > 0):
if(self.num_w_ports > 0):
# calculate the edge of the write transistor well closest to the center
left_write_well_xpos = self.write_nmos_left[0].offset.x + drc["well_enclosure_active"]
right_write_well_xpos = self.write_nmos_right[0].offset.x - self.write_nmos.active_height - drc["well_enclosure_active"]
@@ -1099,7 +1105,7 @@ class pbitcell(pgate.pgate):
height=write_well_height)
# extend pwell over the read transistors to the height of the bitcell
if(self.num_read > 0):
if(self.num_r_ports > 0):
# calculate the edge of the read transistor well clostest to the center
left_read_well_xpos = self.read_nmos_left[0].offset.x + drc["well_enclosure_active"]
right_read_well_xpos = self.read_nmos_right[0].offset.x - self.read_nmos.active_height - drc["well_enclosure_active"]
@@ -1168,43 +1174,44 @@ class pbitcell(pgate.pgate):
return bitcell_pins
def list_all_wl_names(self):
""" Creates a list of all wordline pin names """
return self.wl_names
""" Creates a list of all wordline pin names """
wordline_names = self.rw_wl_names + self.w_wl_names + self.r_wl_names
return wordline_names
def list_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
bitline_pins = []
for port in range(self.total_ports):
column_pins.append("bl{0}".format(port))
column_pins.append("br{0}".format(port))
bitline_pins.append("bl{0}".format(port))
bitline_pins.append("br{0}".format(port))
return bitline_pins
def list_all_bl_names(self):
""" Creates a list of all bl pins names """
bl_pins = [self.rw_bl_names, self.w_bl_names, self.r_bl_names]
bl_pins = self.rw_bl_names + self.w_bl_names + self.r_bl_names
return bl_pins
def list_all_br_names(self):
""" Creates a list of all br pins names """
br_pins = [self.rw_br_names, self.w_br_names, self.r_br_names]
br_pins = self.rw_br_names + self.w_br_names + self.r_br_names
return br_pins
def list_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
bl_pins = [self.rw_bl_names, self.r_bl_names]
bl_pins = self.rw_bl_names + self.r_bl_names
return bl_pins
def list_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
br_pins = [self.rw_br_names, self.r_br_names]
br_pins = self.rw_br_names + self.r_br_names
return br_pins
def list_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
bl_pins = [self.rw_bl_names, self.w_bl_names]
bl_pins = self.rw_bl_names + self.w_bl_names
return bl_pins
def list_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
br_pins = [self.rw_br_names, self.w_br_names]
br_pins = self.rw_br_names + self.w_br_names
return br_pins
+10 -1
View File
@@ -11,10 +11,19 @@ class pgate(design.design):
This is a module that implements some shared functions for parameterized gates.
"""
def __init__(self, name):
def __init__(self, name, height=None):
""" Creates a generic cell """
design.design.__init__(self, name)
if height:
self.height = height
elif not height:
from importlib import reload
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
b = bitcell()
self.height = b.height
def connect_pin_to_rail(self,inst,pin,supply):
""" Conencts a ptx pin to a supply rail. """
+2 -6
View File
@@ -17,26 +17,22 @@ class pinv(pgate.pgate):
from center of rail to rail.. The route_output will route the
output to the right side of the cell for easier access.
"""
from importlib import reload
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
unique_id = 1
def __init__(self, size=1, beta=parameter["beta"], height=bitcell.height, route_output=True):
def __init__(self, size=1, beta=parameter["beta"], height=None, route_output=True):
# We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will
# over-write a design in GDS if one has and the other doesn't
# have poly connected, for example.
name = "pinv_{}".format(pinv.unique_id)
pinv.unique_id += 1
pgate.pgate.__init__(self, name)
pgate.pgate.__init__(self, name, height)
debug.info(2, "create pinv structure {0} with size of {1}".format(name, size))
self.nmos_size = size
self.pmos_size = beta*size
self.beta = beta
self.height = height # Maybe minimize height if not defined in future?
self.route_output = False
+2 -6
View File
@@ -11,13 +11,9 @@ class pinvbuf(design.design):
This is a simple inverter/buffer used for driving loads. It is
used in the column decoder for 1:2 decoding and as the clock buffer.
"""
from importlib import reload
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
unique_id = 1
def __init__(self, driver_size=4, height=bitcell.height, name=""):
def __init__(self, driver_size=4, height=None, name=""):
self.stage_effort = 4
self.row_height = height
@@ -32,7 +28,7 @@ class pinvbuf(design.design):
name = "pinvbuf_{0}_{1}_{2}".format(self.predriver_size, self.driver_size, pinvbuf.unique_id)
pinvbuf.unique_id += 1
design.design.__init__(self, name)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
self.create_netlist()
+2 -7
View File
@@ -12,24 +12,19 @@ class pnand2(pgate.pgate):
This model use ptx to generate a 2-input nand within a cetrain height.
"""
from importlib import reload
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
unique_id = 1
def __init__(self, size=1, height=bitcell.height):
def __init__(self, size=1, height=None):
""" Creates a cell for a simple 2 input nand """
name = "pnand2_{0}".format(pnand2.unique_id)
pnand2.unique_id += 1
pgate.pgate.__init__(self, name)
pgate.pgate.__init__(self, name, height)
debug.info(2, "create pnand2 structure {0} with size of {1}".format(name, size))
self.nmos_size = 2*size
self.pmos_size = parameter["beta"]*size
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
self.pmos_width = self.pmos_size*drc["minwidth_tx"]
self.height = height
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnand2 is only supported now.")
+2 -8
View File
@@ -12,18 +12,13 @@ class pnand3(pgate.pgate):
This model use ptx to generate a 2-input nand within a cetrain height.
"""
from importlib import reload
c = reload(__import__(OPTS.bitcell))
mod_bitcell = getattr(c, OPTS.bitcell)
bitcell = mod_bitcell()
unique_id = 1
def __init__(self, size=1, height=bitcell.height):
def __init__(self, size=1, height=None):
""" Creates a cell for a simple 3 input nand """
name = "pnand3_{0}".format(pnand3.unique_id)
pnand3.unique_id += 1
pgate.pgate.__init__(self, name)
pgate.pgate.__init__(self, name, height)
debug.info(2, "create pnand3 structure {0} with size of {1}".format(name, size))
# We have trouble pitch matching a 3x sizes to the bitcell...
@@ -32,7 +27,6 @@ class pnand3(pgate.pgate):
self.pmos_size = parameter["beta"]*size
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
self.pmos_width = self.pmos_size*drc["minwidth_tx"]
self.height = height
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnand3 is only supported now.")
+2 -7
View File
@@ -12,17 +12,13 @@ class pnor2(pgate.pgate):
This model use ptx to generate a 2-input nor within a cetrain height.
"""
from importlib import reload
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
unique_id = 1
def __init__(self, size=1, height=bitcell.height):
def __init__(self, size=1, height=None):
""" Creates a cell for a simple 2 input nor """
name = "pnor2_{0}".format(pnor2.unique_id)
pnor2.unique_id += 1
pgate.pgate.__init__(self, name)
pgate.pgate.__init__(self, name, height)
debug.info(2, "create pnor2 structure {0} with size of {1}".format(name, size))
self.nmos_size = size
@@ -30,7 +26,6 @@ class pnor2(pgate.pgate):
self.pmos_size = 1.5*parameter["beta"]*size
self.nmos_width = self.nmos_size*drc["minwidth_tx"]
self.pmos_width = self.pmos_size*drc["minwidth_tx"]
self.height = height
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnor2 is only supported now.")
@@ -39,6 +39,8 @@ class single_level_column_mux(design.design):
self.add_wells()
def add_modules(self):
# This is just used for measurements,
# so don't add the module
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
+6 -3
View File
@@ -12,8 +12,9 @@ class sram():
results.
We can later add visualizer and other high-level functions as needed.
"""
def __init__(self, sram_config, name="sram"):
def __init__(self, sram_config, name):
sram_config.compute_sizes()
sram_config.set_local_config(self)
# reset the static duplicate name checker for unit tests
@@ -27,6 +28,8 @@ class sram():
start_time = datetime.datetime.now()
self.name = name
if self.num_banks == 1:
from sram_1bank import sram_1bank as sram
elif self.num_banks == 2:
@@ -35,8 +38,8 @@ class sram():
from sram_4bank import sram_4bank as sram
else:
debug.error("Invalid number of banks.",-1)
self.s = sram(sram_config, name)
self.s = sram(name, sram_config)
self.s.create_netlist()
if not OPTS.netlist_only:
self.s.create_layout()
+2 -3
View File
@@ -18,12 +18,11 @@ class sram_1bank(sram_base):
"""
Procedures specific to a one bank SRAM.
"""
def __init__(self, sram_config, name):
sram_base.__init__(self, sram_config, name)
def __init__(self, name, sram_config):
sram_base.__init__(self, name, sram_config)
def create_netlist(self):
self.compute_sizes()
sram_base.create_netlist(self)
self.create_modules()
+2 -2
View File
@@ -16,8 +16,8 @@ class sram_2bank(sram_base):
"""
Procedures specific to a two bank SRAM.
"""
def __init__(self, sram_config, name):
sram_base.__init__(self, sram_config, name)
def __init__(self, name, sram_config):
sram_base.__init__(self, name, sram_config)
def compute_bank_offsets(self):
""" Compute the overall offsets for a two bank SRAM """
+2 -2
View File
@@ -16,8 +16,8 @@ class sram_4bank(sram_base):
"""
Procedures specific to a four bank SRAM.
"""
def __init__(self, sram_config, name):
sram_base.__init__(self, sram_config, name)
def __init__(self, name, sram_config):
sram_base.__init__(self, name, sram_config)
def compute_bank_offsets(self):
""" Compute the overall offsets for a four bank SRAM """
+11 -76
View File
@@ -3,7 +3,6 @@ import datetime
import getpass
import debug
from importlib import reload
from math import log,sqrt,ceil
from vector import vector
from globals import OPTS, print_time
@@ -14,85 +13,18 @@ class sram_base(design):
Dynamically generated SRAM by connecting banks to control logic. The
number of banks should be 1 , 2 or 4
"""
def __init__(self, sram_config, name):
def __init__(self, name, sram_config):
design.__init__(self, name)
# This is used to compute the sizes of the SRAM
# and must be loaded before netlist creation
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
self.sram_config = sram_config
self.sram_config.set_local_config(self)
self.sram_config = sram_config
sram_config.set_local_config(self)
self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports
self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports
self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
self.bank_insts = []
def compute_sizes(self):
""" Computes the organization of the memory using bitcell size by trying to make it square."""
debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.")
self.num_words_per_bank = self.num_words/self.num_banks
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
# If this was hard coded, don't dynamically compute it!
if self.sram_config.words_per_row:
self.words_per_row = self.sram_config.words_per_row
else:
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
self.bank_side_length = sqrt(self.bank_area)
# Estimate the words per row given the height of the bitcell and the square side length
self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
# Estimate the number of rows given the tentative words per row
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
# Fix the number of columns and rows
self.num_cols = int(self.words_per_row*self.word_size)
self.num_rows = int(self.num_words_per_bank/self.words_per_row)
# Compute the address and bank sizes
self.row_addr_size = int(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size + self.row_addr_size
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
self.sram_config.words_per_row = self.words_per_row
debug.info(1,"Words per row: {}".format(self.words_per_row))
def estimate_words_per_row(self,tentative_num_cols, word_size):
"""
This provides a heuristic rounded estimate for the number of words
per row.
"""
if tentative_num_cols < 1.5*word_size:
return 1
elif tentative_num_cols > 3*word_size:
return 4
else:
return 2
def amend_words_per_row(self,tentative_num_rows, words_per_row):
"""
This picks the number of words per row more accurately by limiting
it to a minimum and maximum.
"""
# Recompute the words per row given a hard max
if(tentative_num_rows > 512):
debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
return int(words_per_row*tentative_num_rows/512)
# Recompute the words per row given a hard min
if(tentative_num_rows < 16):
debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
return int(words_per_row*tentative_num_rows/16)
return words_per_row
def add_pins(self):
""" Add pins for entire SRAM. """
@@ -274,6 +206,9 @@ class sram_base(design):
def add_modules(self):
""" Create all the modules that will be used """
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
c = reload(__import__(OPTS.control_logic))
self.mod_control_logic = getattr(c, OPTS.control_logic)
+80 -20
View File
@@ -1,36 +1,96 @@
import debug
from math import log,sqrt,ceil
from importlib import reload
from globals import OPTS
class sram_config:
""" This is a structure that is used to hold the SRAM configuration options. """
def __init__(self, word_size, num_words, num_banks=1, num_rw_ports=OPTS.num_rw_ports, num_w_ports=OPTS.num_w_ports, num_r_ports=OPTS.num_r_ports):
def __init__(self, word_size, num_words, num_banks=1):
self.word_size = word_size
self.num_words = num_words
self.num_banks = num_banks
self.num_rw_ports = num_rw_ports
self.num_w_ports = num_w_ports
self.num_r_ports = num_r_ports
# This will get over-written when we determine the organization
self.num_banks = 1
self.words_per_row = None
self.total_write = num_rw_ports + num_w_ports
self.total_read = num_rw_ports + num_r_ports
self.total_ports = num_rw_ports + num_w_ports + num_r_ports
# Move the module names to this?
def set_local_config(self, module):
module.word_size = self.word_size
module.num_words = self.num_words
module.num_banks = self.num_banks
module.num_rw_ports = self.num_rw_ports
module.num_w_ports = self.num_w_ports
module.num_r_ports = self.num_r_ports
""" Copy all of the member variables to the given module for convenience """
module.words_per_row = self.words_per_row
module.total_write = self.total_write
module.total_read = self.total_read
module.total_ports = self.total_ports
members = [attr for attr in dir(self) if not callable(getattr(self, attr)) and not attr.startswith("__")]
# Copy all the variables to the local module
for member in members:
setattr(module,member,getattr(self,member))
def compute_sizes(self):
""" Computes the organization of the memory using bitcell size by trying to make it square."""
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
# pass a copy of myself for the port numbers
self.bitcell = self.mod_bitcell()
debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.")
self.num_words_per_bank = self.num_words/self.num_banks
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
# If this was hard coded, don't dynamically compute it!
if not self.words_per_row:
# Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry)
self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank
self.bank_side_length = sqrt(self.bank_area)
# Estimate the words per row given the height of the bitcell and the square side length
self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width)
self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size)
# Estimate the number of rows given the tentative words per row
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
# Fix the number of columns and rows
self.num_cols = int(self.words_per_row*self.word_size)
self.num_rows = int(self.num_words_per_bank/self.words_per_row)
# Compute the address and bank sizes
self.row_addr_size = int(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size + self.row_addr_size
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
debug.info(1,"Words per row: {}".format(self.words_per_row))
def estimate_words_per_row(self,tentative_num_cols, word_size):
"""
This provides a heuristic rounded estimate for the number of words
per row.
"""
if tentative_num_cols < 1.5*word_size:
return 1
elif tentative_num_cols > 3*word_size:
return 4
else:
return 2
def amend_words_per_row(self,tentative_num_rows, words_per_row):
"""
This picks the number of words per row more accurately by limiting
it to a minimum and maximum.
"""
# Recompute the words per row given a hard max
if(not OPTS.is_unit_test and tentative_num_rows > 512):
debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048")
return int(words_per_row*tentative_num_rows/512)
# Recompute the words per row given a hard min
if(not OPTS.is_unit_test and tentative_num_rows < 16):
debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows))
return int(words_per_row*tentative_num_rows/16)
return words_per_row
+41 -12
View File
@@ -18,47 +18,76 @@ class pbitcell_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
import pbitcell
from pbitcell import pbitcell
import tech
OPTS.num_rw_ports=1
OPTS.num_w_ports=1
OPTS.num_r_ports=1
debug.info(2, "Bitcell with 1 of each port: read/write, write, and read")
tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=1,num_r_ports=1)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=0
OPTS.num_w_ports=1
OPTS.num_r_ports=1
debug.info(2, "Bitcell with 0 read/write ports")
tx = pbitcell.pbitcell(num_rw_ports=0,num_w_ports=1,num_r_ports=1)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=1
OPTS.num_w_ports=0
OPTS.num_r_ports=1
debug.info(2, "Bitcell with 0 write ports")
tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=0,num_r_ports=1)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=1
OPTS.num_w_ports=1
OPTS.num_r_ports=0
debug.info(2, "Bitcell with 0 read ports")
tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=1,num_r_ports=0)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=1
OPTS.num_w_ports=0
OPTS.num_r_ports=0
debug.info(2, "Bitcell with 0 read ports and 0 write ports")
tx = pbitcell.pbitcell(num_rw_ports=1,num_w_ports=0,num_r_ports=0)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=2
OPTS.num_w_ports=2
OPTS.num_r_ports=2
debug.info(2, "Bitcell with 2 of each port: read/write, write, and read")
tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=2,num_r_ports=2)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=0
OPTS.num_w_ports=2
OPTS.num_r_ports=2
debug.info(2, "Bitcell with 0 read/write ports")
tx = pbitcell.pbitcell(num_rw_ports=0,num_w_ports=2,num_r_ports=2)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=2
OPTS.num_w_ports=0
OPTS.num_r_ports=2
debug.info(2, "Bitcell with 0 write ports")
tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=0,num_r_ports=2)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=2
OPTS.num_w_ports=2
OPTS.num_r_ports=0
debug.info(2, "Bitcell with 0 read ports")
tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=2,num_r_ports=0)
tx = pbitcell()
self.local_check(tx)
OPTS.num_rw_ports=2
OPTS.num_w_ports=0
OPTS.num_r_ports=0
debug.info(2, "Bitcell with 0 read ports and 0 write ports")
tx = pbitcell.pbitcell(num_rw_ports=2,num_w_ports=0,num_r_ports=0)
tx = pbitcell()
self.local_check(tx)
globals.end_openram()
+3 -3
View File
@@ -27,13 +27,13 @@ class precharge_test(openram_test):
OPTS.num_rw_ports = 2
OPTS.num_r_ports = 2
OPTS.num_w_ports = 2
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0")
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx)
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="wbl0", bitcell_br="wbl_bar0")
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx)
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="rbl0", bitcell_br="rbl_bar0")
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl4", bitcell_br="br4")
self.local_check(tx)
globals.end_openram()
+3 -3
View File
@@ -28,13 +28,13 @@ class precharge_test(openram_test):
OPTS.num_r_ports = 2
OPTS.num_w_ports = 2
pc = precharge_array.precharge_array(columns=3, bitcell_bl="rwbl0", bitcell_br="rwbl_bar0")
pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(pc)
pc = precharge_array.precharge_array(columns=3, bitcell_bl="wbl0", bitcell_br="wbl_bar0")
pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(pc)
pc = precharge_array.precharge_array(columns=3, bitcell_bl="rbl0", bitcell_br="rbl_bar0")
pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl4", bitcell_br="br4")
self.local_check(pc)
globals.end_openram()
View File
View File
View File
+1 -1
View File
@@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 19_psingle_bank_test")
#@unittest.skip("SKIPPING 19_psingle_bank_test")
class psingle_bank_test(openram_test):
def runTest(self):
+5 -4
View File
@@ -21,27 +21,28 @@ class sram_1bank_test(openram_test):
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Single bank, no column mux with control logic")
a = sram(c, name="sram1")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
c.num_words=32
c.words_per_row=2
debug.info(1, "Single bank two way column mux with control logic")
a = sram(c, name="sram2")
a = sram(c, "sram2")
self.local_check(a, final_verification=True)
c.num_words=64
c.words_per_row=4
debug.info(1, "Single bank, four way column mux with control logic")
a = sram(c, name="sram3")
a = sram(c, "sram3")
self.local_check(a, final_verification=True)
c.word_size=2
c.num_words=128
c.words_per_row=8
debug.info(1, "Single bank, eight way column mux with control logic")
a = sram(c, name="sram4")
a = sram(c, "sram4")
self.local_check(a, final_verification=True)
globals.end_openram()
+5 -4
View File
@@ -22,27 +22,28 @@ class sram_2bank_test(openram_test):
num_words=32,
num_banks=2)
c.words_per_row=1
debug.info(1, "Two bank, no column mux with control logic")
a = sram(c, name="sram1")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
c.num_words=64
c.words_per_row=2
debug.info(1, "Two bank two way column mux with control logic")
a = sram(c, name="sram2")
a = sram(c, "sram2")
self.local_check(a, final_verification=True)
c.num_words=128
c.words_per_row=4
debug.info(1, "Two bank, four way column mux with control logic")
a = sram(c, name="sram3")
a = sram(c, "sram3")
self.local_check(a, final_verification=True)
c.word_size=2
c.num_words=256
c.words_per_row=8
debug.info(1, "Two bank, eight way column mux with control logic")
a = sram(c, name="sram4")
a = sram(c, "sram4")
self.local_check(a, final_verification=True)
globals.end_openram()
+4 -4
View File
@@ -23,26 +23,26 @@ class sram_4bank_test(openram_test):
num_banks=4)
debug.info(1, "Four bank, no column mux with control logic")
a = sram(c, name="sram1")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
c.num_words=128
c.words_per_row=2
debug.info(1, "Four bank two way column mux with control logic")
a = sram(c, name="sram2")
a = sram(c, "sram2")
self.local_check(a, final_verification=True)
c.num_words=256
c.words_per_row=4
debug.info(1, "Four bank, four way column mux with control logic")
a = sram(word_size=16, num_words=256, num_banks=4, name="sram3")
a = sram(c, "sram3")
self.local_check(a, final_verification=True)
c.word_size=2
c.num_words=256
c.words_per_row=8
debug.info(1, "Four bank, eight way column mux with control logic")
a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4")
a = sram.sram(c, "sram4")
self.local_check(a, final_verification=True)
globals.end_openram()
+1 -1
View File
@@ -32,7 +32,7 @@ class timing_sram_test(openram_test):
c = sram_config(word_size=1,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram(c, name="sram1")
+1 -1
View File
@@ -32,7 +32,7 @@ class timing_sram_test(openram_test):
c = sram_config(word_size=1,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram(c, name="sram1")
+1 -2
View File
@@ -22,10 +22,9 @@ class lib_test(openram_test):
c = sram_config(word_size=2,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank")
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)
+1 -1
View File
@@ -31,7 +31,7 @@ class lib_test(openram_test):
c = sram_config(word_size=2,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank")
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
+1 -1
View File
@@ -31,7 +31,7 @@ class lib_test(openram_test):
c = sram_config(word_size=2,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank")
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
+1 -1
View File
@@ -22,7 +22,7 @@ class lef_test(openram_test):
c = sram_config(word_size=2,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank")
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
+1 -1
View File
@@ -21,7 +21,7 @@ class verilog_test(openram_test):
c = sram_config(word_size=2,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank")
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5 -5
View File
@@ -1,6 +1,6 @@
magic
tech scmos
timestamp 1521677056
timestamp 1536091415
<< nwell >>
rect -8 29 42 51
<< pwell >>
@@ -105,13 +105,13 @@ rect 6 2 10 48
rect 24 -2 28 48
rect 32 33 36 48
rect 32 -2 36 29
<< m3p >>
<< bb >>
rect 0 0 34 46
<< labels >>
rlabel metal2 0 0 0 0 1 gnd
rlabel metal2 34 0 34 0 1 gnd
rlabel m2contact 17 46 17 46 5 vdd
rlabel metal1 4 7 4 7 1 WL
rlabel metal2 8 43 8 43 1 BL
rlabel metal2 26 43 26 43 1 BR
rlabel metal2 8 43 8 43 1 bl
rlabel metal2 26 43 26 43 1 br
rlabel metal1 4 7 4 7 1 wl
<< end >>
+2 -2
View File
@@ -1,6 +1,6 @@
magic
tech scmos
timestamp 1518823399
timestamp 1536089597
<< nwell >>
rect 0 48 109 103
<< pwell >>
@@ -266,7 +266,7 @@ rect 6 30 10 50
rect 22 20 26 57
rect 70 44 74 70
rect 70 20 74 40
<< m3p >>
<< bb >>
rect 0 0 109 100
<< labels >>
rlabel m2contact 15 34 15 34 4 clk
+2 -2
View File
@@ -1,6 +1,6 @@
magic
tech scmos
timestamp 1523479368
timestamp 1536089622
<< nwell >>
rect -2 0 18 200
<< pwell >>
@@ -280,7 +280,7 @@ rect 14 8 20 9
rect 14 4 15 8
rect 19 4 20 8
rect 14 3 20 4
<< m3p >>
<< bb >>
rect 0 0 34 200
<< labels >>
rlabel metal1 0 8 0 8 2 clk
@@ -1,6 +1,6 @@
magic
tech scmos
timestamp 1521677136
timestamp 1536091380
<< nwell >>
rect -8 29 42 51
<< pwell >>
@@ -106,13 +106,13 @@ rect 6 2 10 48
rect 24 -2 28 48
rect 32 33 36 48
rect 32 -2 36 29
<< m3p >>
<< bb >>
rect 0 0 34 46
<< labels >>
rlabel metal2 0 0 0 0 1 gnd
rlabel metal2 34 0 34 0 1 gnd
rlabel m2contact 17 46 17 46 5 vdd
rlabel metal1 4 7 4 7 1 WL
rlabel metal2 8 43 8 43 1 BL
rlabel metal2 26 43 26 43 1 BR
rlabel metal2 8 43 8 43 1 bl
rlabel metal2 26 43 26 43 1 br
rlabel metal1 4 7 4 7 1 wl
<< end >>
+2 -2
View File
@@ -1,6 +1,6 @@
magic
tech scmos
timestamp 1524065550
timestamp 1536089670
<< nwell >>
rect 0 0 40 102
<< pwell >>
@@ -122,7 +122,7 @@ rect 20 44 22 48
rect 3 0 7 11
rect 10 0 14 44
rect 20 0 24 44
<< m3p >>
<< bb >>
rect 0 0 34 163
<< labels >>
flabel metal1 0 149 0 149 4 FreeSans 26 0 0 0 en
+2 -2
View File
@@ -1,6 +1,6 @@
magic
tech scmos
timestamp 1524499924
timestamp 1536089695
<< nwell >>
rect -2 45 38 73
<< pwell >>
@@ -86,7 +86,7 @@ rect 24 19 28 23
<< metal2 >>
rect 15 34 25 38
rect 15 0 19 34
<< m3p >>
<< bb >>
rect 0 0 34 73
<< labels >>
rlabel metal1 0 12 0 12 3 en
@@ -1,6 +1,6 @@
magic
tech scmos
timestamp 1524499497
timestamp 1536089714
<< nwell >>
rect -3 101 37 138
rect -3 0 37 51
@@ -209,7 +209,7 @@ rect 10 196 14 202
rect 20 193 24 202
rect 20 177 24 189
rect 15 0 19 6
<< m3p >>
<< bb >>
rect 0 0 34 202
<< labels >>
rlabel metal2 15 1 15 1 1 din
@@ -276,6 +276,11 @@ cifoutput
style lambda=0.30(p)
scalefactor 30 15
# This is a custom section to add bounding boxes in OpenRAM
layer BB bb
labels bb
calma 63 0
layer CWN nwell,rnw
bloat-or pdiff,rpd,pdc/a,pfet * 180
bloat-or nsd,nsc/a * 90
@@ -1506,7 +1511,12 @@ cifinput
style lambda=0.30(p)
scalefactor 30
layer nwell CWN
# This is a custom section to add bounding boxes in OpenRAM
layer bb BB
labels BB
calma 63 0
layer nwell CWN
and-not CWNR
and-not CTA
labels CWN
+2 -2
View File
@@ -39,8 +39,8 @@ layer["via1"] = 50
layer["metal2"] = 51
layer["via2"] = 61
layer["metal3"] = 62
layer["text"] = 83
layer["boundary"] = 83
layer["text"] = 63
layer["boundary"] = 63
###################################################
##END GDS Layer Map
+1 -1
View File
@@ -13,4 +13,4 @@ Metal2 drawing 51 0
Via2 drawing 61 0
Metal3 drawing 62 0
Glass drawing 52 0
text drawing 83 0
comment drawing 63 0