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
commit 1a340c9c85
62 changed files with 413 additions and 363 deletions

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

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

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"]

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)

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:

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"])

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])

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 = []

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. """

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 = {}

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()

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"

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

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. """

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

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()

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.")

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.")

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.")

View File

@ -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)

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()

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()

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 """

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 """

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)

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

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()

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()

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()

0
compiler/tests/08_wordline_driver_test.py Normal file → Executable file
View File

0
compiler/tests/09_sense_amp_array_test.py Normal file → Executable file
View File

0
compiler/tests/10_write_driver_array_test.py Normal file → Executable file
View File

2
compiler/tests/19_psingle_bank_test.py Normal file → Executable file
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):

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()

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()

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()

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")

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")

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)

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))

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))

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))

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))

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 >>

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

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

View File

@ -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 >>

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

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

View File

@ -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

View File

@ -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

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

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