Altered indexing of objects in SRAM factory to remove duplications of items using OPTS names. Added smarter bitline name checking.

This commit is contained in:
Hunter Nichols 2019-05-28 16:55:09 -07:00
parent e2d1f7ab0a
commit ad229b1504
9 changed files with 182 additions and 75 deletions

View File

@ -143,38 +143,61 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
name_dict[si_port.lower()] = mod_info
subinst.mod.build_names(name_dict, subinst_name, subinst_ports)
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod):
def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None):
"""Given a list of nets, will compare the internal alias of a mod to determine
if the nets have a connection to this mod's net (but not inst).
"""
#path_nets = ['xsram_0.xbank0.bl0_7']
if exclusion_set == None:
exclusion_set = set()
try:
self.name_dict
except AttributeError:
self.name_dict = {}
self.build_names(self.name_dict, inst_name, port_nets)
#debug.info(1,"names={}".format(list(self.name_dict)))
aliases = []
for net in path_nets:
net = net.lower()
int_net = self.name_dict[net]['int_net']
int_mod = self.name_dict[net]['mod']
if int_mod.is_net_alias(int_net, alias, alias_mod):
# debug.info(1,"int_net={}".format(int_net))
# debug.info(1,"int_mod={}".format(int_mod.name))
# debug.info(1,"alias_net={}".format(alias))
# debug.info(1,"alias_mod={}".format(alias_mod.name))
# debug.info(1,"mod id={}".format(id(alias_mod)))
if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set):
aliases.append(net)
# debug.info(1,"Alias found\n")
# else:
# debug.info(1,"Alias not found\n")
debug.info(1,"Aliases Found={}".format(aliases))
return aliases
def is_net_alias(self, known_net, net_alias, mod):
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
"""Checks if the alias_net in mod is the same as the input net."""
# debug.info(1,"self name={}".format(self.name))
# debug.info(1,"self mod id={}".format(id(self)))
# debug.info(1,"self pins={}".format(self.pins))
# debug.info(1,"known_net={}".format(known_net))
if self in exclusion_set:
return False
#Check ports of this mod
for pin in self.pins:
if self.is_net_alias_name_check(known_net, pin, net_alias, mod):
return True
#Check connections of all other subinsts
#Check connections of all other subinsts
mod_set = set()
for subinst, inst_conns in zip(self.insts, self.conns):
for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins):
if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod):
return True
elif inst_conn.lower() == known_net.lower():
return subinst.mod.is_net_alias(mod_pin, net_alias, mod)
elif inst_conn.lower() == known_net.lower() and subinst.mod not in mod_set:
# debug.info(1,"found matching conn={}".format(inst_conn))
# debug.info(1,"Setting known pin={}".format(mod_pin))
if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set):
return True
mod_set.add(subinst.mod)
return False
def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod):

View File

@ -71,6 +71,14 @@ class bitcell(design.design):
column_pins = ["br"]
return column_pins
def get_bl_name(self):
"""Get bl name"""
return "bl"
def get_br_name(self):
"""Get bl name"""
return "br"
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice

View File

@ -95,6 +95,14 @@ class bitcell_1rw_1r(design.design):
column_pins = ["br0"]
return column_pins
def get_bl_name(self, port=0):
"""Get bl name by port"""
return "bl{}".format(port)
def get_br_name(self, port=0):
"""Get bl name by port"""
return "br{}".format(port)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice

View File

@ -95,6 +95,14 @@ class bitcell_1w_1r(design.design):
column_pins = ["br0"]
return column_pins
def get_bl_name(self, port=0):
"""Get bl name by port"""
return "bl{}".format(port)
def get_br_name(self, port=0):
"""Get bl name by port"""
return "br{}".format(port)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice

View File

@ -873,6 +873,13 @@ class pbitcell(design.design):
"""Returns names of storage nodes in bitcell in [non-inverting, inverting] format."""
return self.storage_nets
def get_bl_name(self, port=0):
"""Get bl name by port"""
return "bl{}".format(port)
def get_br_name(self, port=0):
"""Get bl name by port"""
return "br{}".format(port)
def analytical_delay(self, corner, slew, load=0, swing = 0.5):
parasitic_delay = 1

View File

@ -54,6 +54,7 @@ class replica_pbitcell(design.design):
def add_modules(self):
self.prbc = factory.create(module_type="pbitcell",replica_bitcell=True)
debug.info(1,"rbl bitcell name={}".format(self.prbc.name))
self.add_mod(self.prbc)
self.height = self.prbc.height

View File

@ -78,18 +78,21 @@ class delay(simulation):
"""Create the measurements used for read ports: delays, slews, powers"""
self.read_lib_meas = []
trig_delay_name = "clk{0}"
self.clk_frmt = "clk{0}" #Unformatted clock name
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
self.read_lib_meas.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", measure_scale=1e9))
self.read_lib_meas[-1].meta_str = sram_op.READ_ONE #Used to index time delay values when measurements written to spice file.
self.read_lib_meas.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", measure_scale=1e9))
self.read_lib_meas[-1].meta_str = sram_op.READ_ZERO
self.delay_meas = self.read_lib_meas[:] #For debugging, kept separated
self.delay_meas = []
self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9))
self.delay_meas[-1].meta_str = sram_op.READ_ONE #Used to index time delay values when measurements written to spice file.
self.delay_meas.append(delay_measure("delay_hl", self.clk_frmt, targ_name, "FALL", "FALL", measure_scale=1e9))
self.delay_meas[-1].meta_str = sram_op.READ_ZERO
self.read_lib_meas+=self.delay_meas
self.read_lib_meas.append(slew_measure("slew_lh", targ_name, "RISE", measure_scale=1e9))
self.read_lib_meas[-1].meta_str = sram_op.READ_ONE
self.read_lib_meas.append(slew_measure("slew_hl", targ_name, "FALL", measure_scale=1e9))
self.read_lib_meas[-1].meta_str = sram_op.READ_ZERO
self.slew_meas = []
self.slew_meas.append(slew_measure("slew_lh", targ_name, "RISE", measure_scale=1e9))
self.slew_meas[-1].meta_str = sram_op.READ_ONE
self.slew_meas.append(slew_measure("slew_hl", targ_name, "FALL", measure_scale=1e9))
self.slew_meas[-1].meta_str = sram_op.READ_ZERO
self.read_lib_meas+=self.slew_meas
self.read_lib_meas.append(power_measure("read1_power", "RISE", measure_scale=1e3))
self.read_lib_meas[-1].meta_str = sram_op.READ_ONE
@ -162,11 +165,15 @@ class delay(simulation):
self.debug_delay_meas.append(debug_meas)
#Output voltage measures
self.debug_volt_meas.append(voltage_at_measure("v_{}".format(debug_meas.meta_str),
self.debug_volt_meas.append(voltage_at_measure("v_{}".format(debug_meas.name),
debug_meas.targ_name_no_port))
self.debug_volt_meas[-1].meta_str = debug_meas.meta_str
return self.debug_delay_meas+self.debug_volt_meas
self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9)
self.sen_meas.meta_str = sram_op.READ_ZERO
self.sen_meas.meta_add_delay = True
return self.debug_delay_meas+self.debug_volt_meas+[self.sen_meas]
def create_read_bit_measures(self):
"""Adds bit measurements for read0 and read1 cycles"""
@ -227,27 +234,11 @@ class delay(simulation):
self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port), \
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
sen_name = self.get_sen_name(self.graph.all_paths)
debug.info(1,"s_en name = {}".format(sen_name))
self.sen_name = self.get_sen_name(self.graph.all_paths)
debug.info(1,"s_en name = {}".format(self.sen_name))
self.bl_name,self.br_name = self.get_bl_name(self.graph.all_paths)
import sys
sys.exit(1)
preconv_names = []
for path in self.graph.all_paths:
if not path is sen_path:
sen_preconv = self.graph.get_path_preconvergence_point(path, sen_path)
if sen_preconv not in preconv_names:
preconv_names.append(sen_preconv[0]) #only save non-sen_path names
#Not an good way to separate inverting and non-inverting bitlines...
self.bl_name = [bl for bl in preconv_names if 'bl' in bl][0]
self.br_name = [bl for bl in preconv_names if 'br' in bl][0]
debug.info(1,"bl_name={}".format(self.bl_name))
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, self.wordline_row, self.bitline_column)
debug.info(1, "cell_name={}".format(cell_name))
debug.info(1,"bl name={}, br name={}".format(self.bl_name,self.br_name))
def get_sen_name(self, paths):
"""Gets the signal name associated with the sense amp enable from input paths.
@ -257,44 +248,85 @@ class delay(simulation):
#will require some identification to determine the mod desired.
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
enable_name = sa_mods[0].get_enable_name()
sen_found = False
#Only a single path should contain a single s_en name. Anything else is an error.
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, enable_name, sa_mods[0])
if sen_found and len(aliases) >= 1:
debug.error('Found multiple paths with SA enable.',1)
elif len(aliases) > 1:
debug.error('Found multiple S_EN points in single path. Cannot distinguish between them.',1)
elif not sen_found and len(aliases) == 1:
sen_name = aliases[0]
sen_found = True
if not sen_found:
debug.error("Could not find S_EN name.",1)
sen_name = self.get_alias_in_path(paths, enable_name, sa_mods[0])
return sen_name
def get_bl_name(self, paths):
"""Gets the signal name associated with the bitlines in the bank."""
sa_mods = factory.get_mods(OPTS.sense_amp)
cell_mods = factory.get_mods(OPTS.bitcell)
if len(cell_mods)>=1:
cell_mod = self.get_primary_cell_mod(cell_mods)
elif len(cell_mods)==0:
debug.error("No bitcells found. Cannot determine bitline names.", 1)
#Any sense amp instantiated should be identical, any change to that
#will require some identification to determine the mod desired.
debug.check(len(sa_mods) == 1, "Only expected one type of Sense Amp. Cannot perform s_en checks.")
enable_name = sa_mods[0].get_enable_name()
sen_found = False
# debug.check(self.are_mod_pins_equal(cell_mods), "Only expected one type of bitcell. Cannot perform bitline checks")
# debug.info(1,"num pbitcells={}".format(len(cell_mods)))
# debug.info(1,"cell ids={}".format([id(i) for i in cell_mods]))
#cell_mods = cell_mods[1:]
cell_bl = cell_mod.get_bl_name()
cell_br = cell_mod.get_br_name()
bl_found = False
#Only a single path should contain a single s_en name. Anything else is an error.
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, enable_name, sa_mods[0])
if sen_found and len(aliases) >= 1:
debug.error('Found multiple paths with SA enable.',1)
elif len(aliases) > 1:
debug.error('Found multiple S_EN points in single path. Cannot distinguish between them.',1)
elif not sen_found and len(aliases) == 1:
sen_name = aliases[0]
sen_found = True
if not sen_found:
debug.error("Could not find S_EN name.",1)
bl_names = []
exclude_set = self.get_bl_name_search_exclusions()
for int_net in [cell_bl, cell_br]:
bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set))
return sen_name
return bl_names[0], bl_names[1]
def get_bl_name_search_exclusions(self):
"""Gets the mods as a set which should be excluded while searching for name."""
#Exclude the RBL as it contains bitcells which are not in the main bitcell array
#so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline))
def get_primary_cell_mod(self, cell_mods):
"""Distinguish bitcell array mod from replica bitline array.
Assume there are no replica bitcells in the primary array."""
if len(cell_mods) == 1:
return cell_mods[0]
rbc_mods = factory.get_mods(OPTS.replica_bitcell)
non_rbc_mods = []
for bitcell in cell_mods:
has_cell = False
for replica_cell in rbc_mods:
has_cell = has_cell or replica_cell.contains(bitcell, replica_cell.mods)
if not has_cell:
non_rbc_mods.append(bitcell)
if len(non_rbc_mods) != 1:
debug.error('Multiple bitcell mods found. Cannot distinguish for characterization',1)
return non_rbc_mods[0]
def are_mod_pins_equal(self, mods):
"""Determines if there are pins differences in the input mods"""
if len(mods) == 0:
return True
pins = mods[0].pins
for mod in mods[1:]:
if pins != mod.pins:
return False
return True
def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None):
"""Finds a single alias for the int_net in given paths.
More or less hits cause an error"""
net_found = False
for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set)
if net_found and len(aliases) >= 1:
debug.error('Found multiple paths with {} net.'.format(int_net),1)
elif len(aliases) > 1:
debug.error('Found multiple {} nets in single path.'.format(int_net),1)
elif not net_found and len(aliases) == 1:
path_net_name = aliases[0]
net_found = True
if not net_found:
debug.error("Could not find {} net in timing paths.".format(int_net),1)
return path_net_name
def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements"""
@ -658,6 +690,10 @@ class delay(simulation):
#Get measurements from output file
for measure in self.read_lib_meas:
read_port_dict[measure.name] = measure.retrieve_measure(port=port)
#Check sen timing, then bitlines, then general measurements.
if not self.check_sen_measure(port):
return (False,{})
success = self.check_debug_measures(port, read_port_dict)
success = success and self.check_bit_measures()
#Check timing for read ports. Power is only checked if it was read correctly
@ -680,6 +716,17 @@ class delay(simulation):
# The delay is from the negative edge for our SRAM
return (sim_passed,result)
def check_sen_measure(self, port):
"""Checks that the sen occurred within a half-period"""
self.sen_meas
sen_val = self.sen_meas.retrieve_measure(port=port)
debug.info(1,"S_EN delay={} ns".format(sen_val))
if self.sen_meas.meta_add_delay:
max_delay = self.period/2
else:
max_delay = self.period
return not (type(sen_val) != float or sen_val > max_delay)
def check_debug_measures(self, port, read_measures):
"""Debug measures that indicate special conditions."""
#Currently, only check if the opposite than intended value was read during

View File

@ -85,6 +85,7 @@ class bitcell_array(design.design):
def add_modules(self):
""" Add the modules used in this design """
self.cell = factory.create(module_type="bitcell")
debug.info(1,"Cell mod created, id={}".format(id(self.cell)))
self.add_mod(self.cell)
def create_instances(self):

View File

@ -46,16 +46,14 @@ class sram_factory:
if hasattr(OPTS, module_type):
# Retrieve the name from OPTS if it exists,
# otherwise just use the name
module_name = getattr(OPTS, module_type)
else:
module_name = module_type
module_type = getattr(OPTS, module_type)
# Either retrieve the already loaded module or load it
try:
mod = self.modules[module_type]
except KeyError:
c = reload(__import__(module_name))
mod = getattr(c, module_name)
c = reload(__import__(module_type))
mod = getattr(c, module_type)
self.modules[module_type] = mod
self.module_indices[module_type] = 0
self.objects[module_type] = []
@ -75,8 +73,10 @@ class sram_factory:
# This is especially for library cells so that the spice and gds files can be found.
if len(kwargs)>0:
# Create a unique name and increment the index
module_name = "{0}_{1}".format(module_name, self.module_indices[module_type])
module_name = "{0}_{1}".format(module_type, self.module_indices[module_type])
self.module_indices[module_type] += 1
else:
module_name = module_type
#debug.info(0, "New module: type={0} name={1} kwargs={2}".format(module_type,module_name,str(kwargs)))
obj = mod(name=module_name,**kwargs)
@ -85,6 +85,10 @@ class sram_factory:
def get_mods(self, module_type):
"""Returns list of all objects of module name's type."""
if hasattr(OPTS, module_type):
# Retrieve the name from OPTS if it exists,
# otherwise just use the input
module_type = getattr(OPTS, module_type)
try:
mod_tuples = self.objects[module_type]
mods = [mod for kwargs,mod in mod_tuples]