Merge branch 'dev' of github.com:VLSIDA/PrivateRAM into dev

This commit is contained in:
mrg 2020-11-20 16:57:14 -08:00
commit e134e07522
15 changed files with 181 additions and 14 deletions

View File

@ -117,6 +117,14 @@ class timing_graph():
cur_slew = delays[-1].slew
return delays
def get_edge_mods(self, path):
"""Return all edge mods associated with path"""
if len(path) == 0:
return []
return [self.edge_mods[(path[i], path[i+1])] for i in range(len(path)-1)]
def __str__(self):
""" override print function output """

View File

@ -60,3 +60,8 @@ class bitcell_1port(bitcell_base.bitcell_base):
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False

View File

@ -48,3 +48,8 @@ class replica_bitcell_1port(bitcell_base.bitcell_base):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False

View File

@ -128,6 +128,7 @@ class delay(simulation):
read_measures.append(self.create_bitline_measurement_objects())
read_measures.append(self.create_debug_measurement_objects())
read_measures.append(self.create_read_bit_measures())
read_measures.append(self.create_sen_and_bitline_path_measures())
return read_measures
@ -249,6 +250,98 @@ class delay(simulation):
qbar_meas = voltage_at_measure("v_qbar_{}".format(meas_tag), qbar_name)
return {bit_polarity.NONINVERTING: q_meas, bit_polarity.INVERTING: qbar_meas}
def create_sen_and_bitline_path_measures(self):
"""Create measurements for the s_en and bitline paths for individual delays per stage."""
# FIXME: There should be a default_read_port variable in this case, pathing is done with this
# but is never mentioned otherwise
port = self.read_ports[0]
sen_and_port = self.sen_name+str(port)
bl_and_port = self.bl_name.format(port) # bl_name contains a '{}' for the port
# Isolate the s_en and bitline paths
debug.info(1, "self.bl_name = {}".format(self.bl_name))
debug.info(1, "self.graph.all_paths = {}".format(self.graph.all_paths))
sen_paths = [path for path in self.graph.all_paths if sen_and_port in path]
bl_paths = [path for path in self.graph.all_paths if bl_and_port in path]
debug.check(len(sen_paths)==1, 'Found {} paths which contain the s_en net.'.format(len(sen_paths)))
debug.check(len(bl_paths)==1, 'Found {} paths which contain the bitline net.'.format(len(bl_paths)))
sen_path = sen_paths[0]
bitline_path = bl_paths[0]
# Get the measures
self.sen_path_meas = self.create_delay_path_measures(sen_path)
self.bl_path_meas = self.create_delay_path_measures(bitline_path)
all_meas = self.sen_path_meas + self.bl_path_meas
# Paths could have duplicate measurements, remove them before they go to the stim file
all_meas = self.remove_duplicate_meas_names(all_meas)
# FIXME: duplicate measurements still exist in the member variables, since they have the same
# name it will still work, but this could cause an issue in the future.
return all_meas
def remove_duplicate_meas_names(self, measures):
"""Returns new list of measurements without duplicate names"""
name_set = set()
unique_measures = []
for meas in measures:
if meas.name not in name_set:
name_set.add(meas.name)
unique_measures.append(meas)
return unique_measures
def create_delay_path_measures(self, path):
"""Creates measurements for each net along given path."""
# Determine the directions (RISE/FALL) of signals
path_dirs = self.get_meas_directions(path)
# Create the measurements
path_meas = []
for i in range(len(path)-1):
cur_net, next_net = path[i], path[i+1]
cur_dir, next_dir = path_dirs[i], path_dirs[i+1]
meas_name = "delay_{}_to_{}".format(cur_net, next_net)
if i+1 != len(path)-1:
path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, next_dir, measure_scale=1e9, has_port=False))
else: # Make the last measurement always measure on FALL because is a read 0
path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, "FALL", measure_scale=1e9, has_port=False))
# Some bitcell logic is hardcoded for only read zeroes, force that here as well.
path_meas[-1].meta_str = sram_op.READ_ZERO
path_meas[-1].meta_add_delay = True
return path_meas
def get_meas_directions(self, path):
"""Returns SPICE measurements directions based on path."""
# Get the edges modules which define the path
edge_mods = self.graph.get_edge_mods(path)
# Convert to booleans based on function of modules (inverting/non-inverting)
mod_type_bools = [mod.is_non_inverting() for mod in edge_mods]
#FIXME: obtuse hack to differentiate s_en input from bitline in sense amps
if self.sen_name in path:
# Force the sense amp to be inverting for s_en->DOUT.
# bitline->DOUT is non-inverting, but the module cannot differentiate inputs.
s_en_index = path.index(self.sen_name)
mod_type_bools[s_en_index] = False
debug.info(2,'Forcing sen->dout to be inverting.')
# Use these to determine direction list assuming delay start on neg. edge of clock (FALL)
# Also, use shorthand that 'FALL' == False, 'RISE' == True to simplify logic
bool_dirs = [False]
cur_dir = False # All Paths start on FALL edge of clock
for mod_bool in mod_type_bools:
cur_dir = (cur_dir == mod_bool)
bool_dirs.append(cur_dir)
# Convert from boolean to string
return ['RISE' if dbool else 'FALL' for dbool in bool_dirs]
def set_load_slew(self, load, slew):
""" Set the load and slew """
@ -666,6 +759,8 @@ class delay(simulation):
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict), 1)
result[port].update(read_port_dict)
self.check_path_measures()
return (True, result)
@ -767,6 +862,21 @@ class delay(simulation):
debug.info(1, "min_dicharge={}, min_diff={}".format(min_dicharge, min_diff))
return (min_dicharge and min_diff)
def check_path_measures(self):
"""Get and check all the delays along the sen and bitline paths"""
# Get and set measurement, no error checking done other than prints.
debug.info(2, "Checking measures in Delay Path")
value_dict = {}
for meas in self.sen_path_meas+self.bl_path_meas:
val = meas.retrieve_measure()
debug.info(2, '{}={}'.format(meas.name, val))
if type(val) != float or val > self.period/2:
debug.info(1,'Failed measurement:{}={}'.format(meas.name, val))
value_dict[meas.name] = val
return value_dict
def run_power_simulation(self):
"""
This simulates a disabled SRAM to get the leakage power when it is off.
@ -1053,7 +1163,7 @@ class delay(simulation):
# The inverse address needs to share the same bitlines as the probe address as the trimming will remove all other bitlines
# This is only an issue when there is a column mux and the address maps to different bitlines.
column_addr = self.probe_address[:self.sram.col_addr_size] # do not invert this part
column_addr = self.get_column_addr() # do not invert this part
inverse_address = ""
for c in self.probe_address[self.sram.col_addr_size:]: # invert everything else
if c=="0":

View File

@ -412,7 +412,7 @@ class model_check(delay):
data_dict[self.bl_meas_name] = bl_delays[read_port]
data_dict[self.power_name] = powers[read_port]
if not OPTS.use_tech_delay_chain_size: #Model is not used in this case
if OPTS.auto_delay_chain_sizing: #Model is not used in this case
wl_model_delays, sae_model_delays = self.get_model_delays(read_port)
debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays))
debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays))
@ -439,7 +439,7 @@ class model_check(delay):
name_dict[self.power_name] = self.power_meas_names
#name_dict[self.wl_slew_name] = self.wl_slew_meas_names
if not OPTS.use_tech_delay_chain_size:
if OPTS.auto_delay_chain_sizing:
name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured.
name_dict[self.sae_model_name] = name_dict["sae_measures"]

View File

@ -403,6 +403,10 @@ class simulation():
pin_names.append("{0}".format("gnd"))
return pin_names
def get_column_addr(self):
"""Returns column address of probe bit"""
return self.probe_address[:self.sram.col_addr_size]
def add_graph_exclusions(self):
"""
Exclude portions of SRAM from timing graph which are not relevant
@ -434,11 +438,12 @@ class simulation():
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
debug.info(2, "s_en name = {}".format(self.sen_name))
column_addr = self.get_column_addr()
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
port_pos = -1 - len(str(self.probe_data)) - len(str(port))
port_pos = -1 - len(str(column_addr)) - len(str(port))
if bl_name_port.endswith(str(port) + "_" + str(self.probe_data)):
if bl_name_port.endswith(str(port) + "_" + str(column_addr)):
self.bl_name = bl_name_port[:port_pos] + "{}" + bl_name_port[port_pos + len(str(port)):]
elif not bl_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
self.bl_name = bl_name_port
@ -446,7 +451,7 @@ class simulation():
self.bl_name = bl_name_port
debug.warning("Error occurred while determining bitline names. Can cause faults in simulation.")
if br_name_port.endswith(str(port) + "_" + str(self.probe_data)):
if br_name_port.endswith(str(port) + "_" + str(column_addr)):
self.br_name = br_name_port[:port_pos] + "{}" + br_name_port[port_pos + len(str(port)):]
elif not br_name_port[port_pos].isdigit(): # single port SRAM case, bl will not be numbered eg bl_0
self.br_name = br_name_port

View File

@ -73,3 +73,9 @@ class sense_amp(design.design):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
def is_non_inverting(self):
"""Return input to output polarity for module"""
#FIXME: This only applied to bl/br -> dout and not s_en->dout
return True

View File

@ -9,7 +9,6 @@ import optparse
import getpass
import os
class options(optparse.Values):
"""
Class for holding all of the OpenRAM options. All
@ -61,7 +60,7 @@ class options(optparse.Values):
rbl_delay_percentage = 0.5
# Allow manual adjustment of the delay chain over automatic
use_tech_delay_chain_size = False
auto_delay_chain_sizing = False
delay_chain_stages = 9
delay_chain_fanout_per_stage = 4
@ -103,7 +102,7 @@ class options(optparse.Values):
# Run with extracted parasitics
use_pex = False
# Output config with all options
output_extended_config = False
output_extended_config = True
###################
@ -124,7 +123,7 @@ class options(optparse.Values):
pex_exe = None
# For sky130, we need magic for filtering.
magic_exe = None
# Number of threads to use
num_threads = 2
@ -161,7 +160,7 @@ class options(optparse.Values):
inv_dec = "pinv"
nand2_dec = "pnand2"
nand3_dec = "pnand3"
nand4_dec = "pnand4"
nand4_dec = "pnand4" # Not available right now
precharge_array = "precharge_array"
ptx = "ptx"
replica_bitline = "replica_bitline"

View File

@ -146,3 +146,8 @@ class pand2(pgate.pgate):
offset=pin.center(),
width=pin.width(),
height=pin.height())
def is_non_inverting(self):
"""Return input to output polarity for module"""
return True

View File

@ -337,3 +337,8 @@ class pinv(pgate.pgate):
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False

View File

@ -314,3 +314,8 @@ class pnand2(pgate.pgate):
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False

View File

@ -347,3 +347,8 @@ class pnand3(pgate.pgate):
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)
def is_non_inverting(self):
"""Return input to output polarity for module"""
return False

View File

@ -549,3 +549,7 @@ class ptx(design.design):
"""
self.add_graph_edges(graph, port_nets)
def is_non_inverting(self):
"""Return input to output polarity for module"""
return True

View File

@ -23,7 +23,13 @@ class sram():
def __init__(self, sram_config, name):
sram_config.set_local_config(self)
# FIXME: adjust this to not directly change OPTS.
# Word-around to have values relevant to OPTS be displayed if not directly set.
OPTS.words_per_row = self.words_per_row
debug.info(1, "Changed OPTS wpr={}".format(self.words_per_row))
debug.info(1, "OPTS wpr={}".format(OPTS.words_per_row))
# reset the static duplicate name checker for unit tests
# in case we create more than one SRAM
from design import design

View File

@ -38,7 +38,6 @@ class timing_sram_test(openram_test):
# num_words=256,
# num_banks=1)
# c.words_per_row=2
# OPTS.use_tech_delay_chain_size = True
c.recompute_sizes()
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = factory.create(module_type="sram", sram_config=c)