standalone char and func

This commit is contained in:
Bugra Onal 2022-12-13 07:53:58 -08:00
parent c6485277f3
commit db85e8ecd6
6 changed files with 84 additions and 54 deletions

View File

@ -59,6 +59,7 @@ class delay(simulation):
self.set_corner(corner)
self.create_signal_names()
self.add_graph_exclusions()
self.meas_id = 0
def create_measurement_objects(self):
""" Create the measurements used for read and write ports """
@ -167,7 +168,9 @@ class delay(simulation):
write_measures = []
write_measures.append(self.write_lib_meas)
if OPTS.top_process != "memchar":
write_measures.append(self.create_write_bit_measures())
return write_measures
def create_debug_measurement_objects(self):
@ -265,8 +268,8 @@ class delay(simulation):
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)
self.sen_path_meas = self.create_delay_path_measures(sen_path, "sen")
self.bl_path_meas = self.create_delay_path_measures(bitline_path, "bl")
all_meas = self.sen_path_meas + self.bl_path_meas
# Paths could have duplicate measurements, remove them before they go to the stim file
@ -288,7 +291,7 @@ class delay(simulation):
return unique_measures
def create_delay_path_measures(self, path):
def create_delay_path_measures(self, path, process):
"""Creates measurements for each net along given path."""
# Determine the directions (RISE/FALL) of signals
@ -300,6 +303,8 @@ class delay(simulation):
cur_net, next_net = path[i], path[i + 1]
cur_dir, next_dir = path_dirs[i], path_dirs[i + 1]
meas_name = "delay_{0}_to_{1}".format(cur_net, next_net)
meas_name += "_" + process + "_id" + str(self.meas_id)
self.meas_id += 1
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
@ -1163,15 +1168,17 @@ class delay(simulation):
loc = 0
port_name = ''
for port in port_iter:
port_measure_lines.append((port_name, measure_txt[loc:port.end(0)]))
port_measure_lines.append((port_name, measure_text[loc:port.end(0)]))
loc = port.start(0)
port_name = port.group(1) + port.group(2)
mf.close()
# Cycle comments, not sure if i need this
cycle_lines = port_measure_lines.pop(0)[1]
# cycle_lines = port_measure_lines.pop(0)[1]
# For now just recover the bit_measures and sen_and_bitline_path_measures
self.read_meas_lists.append([])
self.read_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []}
self.write_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []}
bit_measure_rule = re.compile(r"\.meas tran (v_q_a\d+_b\d+_(read|write)_(zero|one)\d+) FIND v\((.*)\) AT=(\d+(\.\d+)?)n")
for measures in port_measure_lines:
port_name = measures[0]
@ -1180,43 +1187,52 @@ class delay(simulation):
for bit_measure in bit_measure_iter:
meas_name = bit_measure.group(1)
read = bit_measure.group(2) == "read"
cycle = bit_measure.group(3)
probe = bit_measure.group(4)
polarity = bit_polarity.NONINVERTING
if "q_bar" in meas_name:
polarity = bit_polarity.INVERTING
meas = voltage_at_measure(meas_name, probe)
if read:
if cycle == "one":
meas.meta_str = sram_op.READ_ONE
else:
meas.meta_str = sram_op.READ_ZERO
self.read_bit_meas[polarity].append(meas)
self.read_meas_lists[-1].append(meas)
else:
if cycle == "one":
meas.meta_str = sram_op.WRITE_ONE
else:
meas.meta_str = sram_op.WRITE_ZERO
self.write_bit_meas[polarity].append(meas)
self.write_meas_lists[-1].append(meas)
delay_path_rule = re.compile(r"\.meas tran delay_(.*)_to_(.*) TRIG v\((.*)\) VAL=(\d+(\.\d+)?) (RISE|FALL)=(\d+) TD=(\d+(\.\d+)?)n TARG v\((.*)\) VAL=(\d+(\.\d+)?) (RISE|FALL)=(\d+) TD=(\d+(\.\d+)?)n")
delay_path_rule = re.compile(r"\.meas tran delay_(.*)_to_(.*)_(sen|bl)_(id\d*) TRIG v\((.*)\) VAL=(\d+(\.\d+)?) (RISE|FALL)=(\d+) TD=(\d+(\.\d+)?)n TARG v\((.*)\) VAL=(\d+(\.\d+)?) (RISE|FALL)=(\d+) TD=(\d+(\.\d+)?)n")
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
meas_buff = []
self.sen_path_meas = []
self.bl_path_meas = []
for measures in port_measure_lines:
port_name = measures[0]
text = measures[1]
delay_path_iter = delay_path_rule.finditer(text)
for delay_path_measure in delay_path_iter:
from_ = delay_path_measure.group(1)
to_ = delay_path_measure.group(2)
trig_rise = delay_path_measure.group(5)
targ_rise = delay_path_measure.group(10)
meas_name = "delay_{0}_to_{1}".format(from_, to_)
path_ = delay_path_measure.group(3)
id_ = delay_path_measure.group(4)
trig_rise = delay_path_measure.group(8)
targ_rise = delay_path_measure.group(15)
meas_name = "delay_{0}_to_{1}_{2}_{3}".format(from_, to_, path_, id_)
meas = delay_measure(meas_name, from_, to_, trig_rise, targ_rise, measure_scale=1e9, has_port=False)
meas.meta_str = sram_op.READ_ZERO
meas.meta_add_delay = True
meas_buff.append(meas)
# TODO: we are losing duplicate measures here
if from_ == sen_and_port:
if path_ == "sen":
self.sen_path_meas.extend(meas_buff.copy())
meas_buff.clear()
elif from_ == bl_and_port:
self.bl_path_meas.extend(meas_buff)
elif path_ == "bl":
self.bl_path_meas.extend(meas_buff.copy())
meas_buff.clear()
self.read_meas_lists.append(self.sen_path_meas + self.bl_path_meas)
@ -1229,9 +1245,9 @@ class delay(simulation):
self.prepare_netlist()
if OPTS.top_process == "memchar":
# TODO: fix
self.bl_name = "xsram.xbank0.bl_0_{}"
self.br_name = "xsram.xbank0.br_0_{}"
self.sen_name = "xsram.s_en"
self.bl_name = "xsram:xbank0:bl_0_{}"
self.br_name = "xsram:xbank0:br_0_{}"
self.sen_name = "xsram:s_en"
self.create_measurement_objects()
self.recover_measurment_objects()
else:

View File

@ -14,6 +14,7 @@ from .stimuli import *
from .charutils import *
from globals import OPTS
from .simulation import simulation
from .measurements import voltage_at_measure
class functional(simulation):
@ -78,9 +79,12 @@ class functional(simulation):
self.wordline_row = 0
self.bitline_column = 0
self.create_signal_names()
self.add_graph_exclusions()
self.create_graph()
self.set_internal_spice_names()
#self.add_graph_exclusions()
#self.create_graph()
#self.set_internal_spice_names()
self.bl_name = "xsram:xbank0:bl_0_{}"
self.br_name = "xsram:xbank0:br_0_{}"
self.sen_name = "xsram:s_en"
self.q_name, self.qbar_name = self.get_bit_name()
debug.info(2, "q:\t\t{0}".format(self.q_name))
debug.info(2, "qbar:\t{0}".format(self.qbar_name))
@ -275,7 +279,8 @@ class functional(simulation):
sp_read_value = ""
for bit in range(self.word_size + self.num_spare_cols):
measure_name = "v{0}_{1}ck{2}".format(dout_port.lower(), bit, cycle)
value = parse_spice_list("timing", measure_name)
# value = parse_spice_list("timing", measure_name)
value = self.measures[measure_name].retrieve_measure(port=0)
# FIXME: Ignore the spare columns for now
if bit >= self.word_size:
value = 0
@ -473,6 +478,7 @@ class functional(simulation):
# Generate dout value measurements
self.sf.write("\n * Generation of dout measurements\n")
self.measures = {}
for (word, dout_port, eo_period, cycle) in self.read_check:
t_initial = eo_period
@ -480,7 +486,7 @@ class functional(simulation):
num_bits = self.word_size + self.num_spare_cols
for bit in range(num_bits):
signal_name = "{0}_{1}".format(dout_port, bit)
measure_name = "V{0}ck{1}".format(signal_name, cycle)
measure_name = "v{0}ck{1}".format(signal_name, cycle)
voltage_value = self.stim.get_voltage(word[num_bits - bit - 1])
self.stim.add_comment("* CHECK {0} {1} = {2} time = {3}".format(signal_name,
@ -488,10 +494,13 @@ class functional(simulation):
voltage_value,
eo_period))
# TODO: Convert to measurement statement instead of stimuli
self.stim.gen_meas_value(meas_name=measure_name,
dout=signal_name,
t_initial=t_initial,
t_final=t_final)
meas = voltage_at_measure(measure_name, signal_name)
self.measures[measure_name] = meas
meas.write_measure(self.stim, ((t_initial + t_final) / 2, 0))
# self.stim.gen_meas_value(meas_name=measure_name,
# dout=signal_name,
# t_initial=t_initial,
# t_final=t_final)
self.stim.write_control(self.cycle_times[-1] + self.period)
self.sf.close()
@ -499,10 +508,13 @@ class functional(simulation):
# FIXME: Similar function to delay.py, refactor this
def get_bit_name(self):
""" Get a bit cell name """
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0)
storage_names = cell_inst.mod.get_storage_net_names()
debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
"supported for characterization. Storage nets={0}").format(storage_names))
# TODO: Find a way to get the cell_name and storage_names statically
# (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0)
# storage_names = cell_inst.mod.get_storage_net_names()
# debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes"
# "supported for characterization. Storage nets={0}").format(storage_names))
cell_name = "xsram:xbank0:xbitcell_array:xbitcell_array:xbit_r0_c0"
storage_names = ("Q", "Q_bar")
q_name = cell_name + OPTS.hier_seperator + str(storage_names[0])
qbar_name = cell_name + OPTS.hier_seperator + str(storage_names[1])

View File

@ -173,9 +173,14 @@ class sram():
# Characterize the design
start_time = datetime.datetime.now()
from characterizer import lib
debug.print_raw("LIB: Characterizing... ")
lib(out_dir=OPTS.output_path, sram=self.s, sp_file=self.get_sp_name())
from characterizer import delay
debug.print_raw("LIB: Writing Analysis File... ")
d = delay(self, self.get_sp_name(), ("TT", 5, 25))
if (self.sram.num_spare_rows == 0):
probe_address = "1" * self.sram.addr_size
else:
probe_address = "0" + "1" * (self.sram.addr_size - 1)
d.analysis_init(probe_address, probe_data)
print_time("Characterization", datetime.datetime.now(), start_time)
# Write the config file

View File

@ -39,11 +39,6 @@ class sram_config:
except ImportError:
self.array_col_multiple = 1
if self.write_size:
self.num_wmasks = int(ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
if not self.num_spare_cols:
self.num_spare_cols = 0

View File

@ -44,8 +44,10 @@ init_openram(config_file=config_file, is_unit_test=False)
print_banner()
# Configure the SRAM organization (duplicated from openram.py)
from sram_config import sram_config
c = sram_config(word_size=OPTS.word_size,
#from sram_config import sram_config
from characterizer.fake_sram import fake_sram
s = fake_sram(name=OPTS.output_name,
word_size=OPTS.word_size,
num_words=OPTS.num_words,
write_size=OPTS.write_size,
num_banks=OPTS.num_banks,
@ -53,18 +55,18 @@ c = sram_config(word_size=OPTS.word_size,
num_spare_rows=OPTS.num_spare_rows,
num_spare_cols=OPTS.num_spare_cols)
s.parse_html(OPTS.output_path + "sram.html")
s.generate_pins()
s.setup_multiport_constants()
OPTS.netlist_only = True
OPTS.check_lvsdrc = False
# Initialize and create the sram object
from sram import sram
s = sram(name=OPTS.output_name, sram_config=c)
# Generate stimulus and run functional simulation on the design
start_time = datetime.datetime.now()
from characterizer import functional
debug.print_raw("Functional simulation... ")
f = functional(s.s, cycles=cycles, spfile=s.get_sp_name(), period=period, output_path=OPTS.openram_temp)
f = functional(s, cycles=cycles, spfile=OPTS.output_path + OPTS.output_name + ".sp", period=period, output_path=OPTS.openram_temp)
(fail, error) = f.run()
debug.print_raw(error)
print_time("Functional simulation", datetime.datetime.now(), start_time)