Added incorrect read checking in characterizer.

This commit is contained in:
Hunter Nichols 2019-05-13 19:38:46 -07:00
parent d54074d68e
commit b4cce65889
4 changed files with 196 additions and 59 deletions

View File

@ -1,4 +1,4 @@
import sys,re,shutil
import sys,re,shutil,copy
import debug
import tech
import math
@ -48,32 +48,33 @@ class delay(simulation):
def create_measurement_objects(self):
"""Create the measurements used for read and write ports"""
self.create_read_port_measurement_objects()
self.create_write_port_measurement_objects()
self.read_meas_lists = self.create_read_port_measurement_objects()
self.write_meas_lists = self.create_write_port_measurement_objects()
def create_read_port_measurement_objects(self):
"""Create the measurements used for read ports: delays, slews, powers"""
self.read_meas_objs = []
self.read_lib_meas = []
trig_delay_name = "clk{0}"
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
self.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", measure_scale=1e9))
self.read_meas_objs[-1].meta_str = "read1" #Used to index time delay values when measurements written to spice file.
self.read_meas_objs.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", measure_scale=1e9))
self.read_meas_objs[-1].meta_str = "read0"
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 = "read1" #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 = "read0"
self.delay_meas = self.read_lib_meas[:] #For debugging, kept separated
self.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", measure_scale=1e9))
self.read_meas_objs[-1].meta_str = "read1"
self.read_meas_objs.append(slew_measure("slew_hl", targ_name, "FALL", measure_scale=1e9))
self.read_meas_objs[-1].meta_str = "read0"
self.read_lib_meas.append(slew_measure("slew_lh", targ_name, "RISE", measure_scale=1e9))
self.read_lib_meas[-1].meta_str = "read1"
self.read_lib_meas.append(slew_measure("slew_hl", targ_name, "FALL", measure_scale=1e9))
self.read_lib_meas[-1].meta_str = "read0"
self.read_meas_objs.append(power_measure("read1_power", "RISE", measure_scale=1e3))
self.read_meas_objs[-1].meta_str = "read1"
self.read_meas_objs.append(power_measure("read0_power", "FALL", measure_scale=1e3))
self.read_meas_objs[-1].meta_str = "read0"
self.read_lib_meas.append(power_measure("read1_power", "RISE", measure_scale=1e3))
self.read_lib_meas[-1].meta_str = "read1"
self.read_lib_meas.append(power_measure("read0_power", "FALL", measure_scale=1e3))
self.read_lib_meas[-1].meta_str = "read0"
#This will later add a half-period to the spice time delay. Only for reading 0.
for obj in self.read_meas_objs:
for obj in self.read_lib_meas:
if obj.meta_str is "read0":
obj.meta_add_delay = True
@ -85,17 +86,22 @@ class delay(simulation):
bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column)
br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column)
# self.read_meas_objs.append(voltage_when_measure(self.voltage_when_names[0], trig_name, bl_name, "RISE", .5))
# self.read_meas_objs.append(voltage_when_measure(self.voltage_when_names[1], trig_name, br_name, "RISE", .5))
# self.read_lib_meas.append(voltage_when_measure(self.voltage_when_names[0], trig_name, bl_name, "RISE", .5))
# self.read_lib_meas.append(voltage_when_measure(self.voltage_when_names[1], trig_name, br_name, "RISE", .5))
#These are read values but need to be separated for unique error checking.
self.create_bitline_delay_measurement_objects()
read_measures = []
read_measures.append(self.read_lib_meas)
#Other measurements associated with the read port not included in the liberty file
read_measures.append(self.create_bitline_delay_measurement_objects())
read_measures.append(self.create_debug_measurement_objects())
return read_measures
def create_bitline_delay_measurement_objects(self):
"""Create the measurements used for bitline delay values. Due to unique error checking, these are separated from other measurements.
These measurements are only associated with read values
"""
self.bitline_delay_objs = []
self.bitline_delay_meas = []
trig_name = "clk{0}"
if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name
port_format = ""
@ -106,23 +112,51 @@ class delay(simulation):
targ_val = (self.vdd_voltage - tech.spice["v_threshold_typical"])/self.vdd_voltage #Calculate as a percentage of vdd
targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit
# self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[0], trig_name, bl_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9))
# self.bitline_delay_objs[-1].meta_str = "read0"
# self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[1], trig_name, br_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9))
# self.bitline_delay_objs[-1].meta_str = "read1"
# self.bitline_delay_meas.append(delay_measure(self.bitline_delay_names[0], trig_name, bl_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9))
# self.bitline_delay_meas[-1].meta_str = "read0"
# self.bitline_delay_meas.append(delay_measure(self.bitline_delay_names[1], trig_name, br_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9))
# self.bitline_delay_meas[-1].meta_str = "read1"
#Enforces the time delay on the bitline measurements for read0 or read1
for obj in self.bitline_delay_objs:
for obj in self.bitline_delay_meas:
obj.meta_add_delay = True
return self.bitline_delay_meas
def create_write_port_measurement_objects(self):
"""Create the measurements used for read ports: delays, slews, powers"""
self.write_meas_objs = []
self.write_lib_meas = []
self.write_meas_objs.append(power_measure("write1_power", "RISE", measure_scale=1e3))
self.write_meas_objs[-1].meta_str = "write1"
self.write_meas_objs.append(power_measure("write0_power", "FALL", measure_scale=1e3))
self.write_meas_objs[-1].meta_str = "write0"
self.write_lib_meas.append(power_measure("write1_power", "RISE", measure_scale=1e3))
self.write_lib_meas[-1].meta_str = "write1"
self.write_lib_meas.append(power_measure("write0_power", "FALL", measure_scale=1e3))
self.write_lib_meas[-1].meta_str = "write0"
write_measures = []
write_measures.append(self.write_lib_meas)
return write_measures
def create_debug_measurement_objects(self):
"""Create debug measurement to help identify failures."""
self.debug_delay_meas = []
self.debug_volt_meas = []
for meas in self.delay_meas:
debug_meas = copy.deepcopy(meas)
debug_meas.name = debug_meas.name+'_debug'
#This particular debugs check if output value is flipped, so TARG_DIR is flipped
if meas.targ_dir_str == 'FALL':
debug_meas.targ_dir_str = 'RISE'
else:
debug_meas.targ_dir_str = 'FALL'
#inserting debug member variable
debug_meas.parent = meas.name
self.debug_delay_meas.append(debug_meas)
self.debug_volt_meas.append(voltage_at_measure("v_{}".format(debug_meas.meta_str),
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
def create_signal_names(self):
self.addr_name = "A"
self.din_name = "DIN"
@ -284,6 +318,8 @@ class delay(simulation):
return self.get_power_measure_variants(port, measure_obj, "read")
elif meas_type is voltage_when_measure:
return self.get_volt_when_measure_variants(port, measure_obj)
elif meas_type is voltage_at_measure:
return self.get_volt_at_measure_variants(port, measure_obj)
else:
debug.error("Input function not defined for measurement type={}".format(meas_type))
@ -299,6 +335,7 @@ class delay(simulation):
else:
debug.error("Unrecognised delay Index={}".format(delay_obj.meta_str),1)
#These measurements have there time further delayed to the neg. edge of the clock.
if delay_obj.meta_add_delay:
meas_cycle_delay += self.period/2
@ -312,7 +349,21 @@ class delay(simulation):
return (t_initial, t_final, port)
def get_volt_when_measure_variants(self, port, power_obj):
def get_volt_at_measure_variants(self, port, volt_meas):
"""Get the measurement values that can either vary port to port (time delays)"""
#Only checking 0 value reads for now.
if volt_meas.meta_str == "read0":
#Falling delay are measured starting from neg. clk edge. Delay adjusted to that.
meas_cycle = self.cycle_times[self.measure_cycles[port][volt_meas.meta_str]]
elif volt_meas.meta_str == "read1":
meas_cycle = self.cycle_times[self.measure_cycles[port][volt_meas.meta_str]]
else:
debug.error("Unrecognised delay Index={}".format(volt_meas.meta_str),1)
#Measurement occurs at the end of the period -> current period start + period
at_time = meas_cycle+self.period
return (at_time, port)
def get_volt_when_measure_variants(self, port, volt_meas):
"""Get the measurement values that can either vary port to port (time delays)"""
#Only checking 0 value reads for now.
t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]]
@ -324,9 +375,10 @@ class delay(simulation):
Write the measure statements to quantify the delay and power results for a read port.
"""
# add measure statements for delays/slews
for measure in self.read_meas_objs+self.bitline_delay_objs:
measure_variant_inp_tuple = self.get_read_measure_variants(port, measure)
measure.write_measure(self.stim, measure_variant_inp_tuple)
for meas_list in self.read_meas_lists:
for measure in meas_list:
measure_variant_inp_tuple = self.get_read_measure_variants(port, measure)
measure.write_measure(self.stim, measure_variant_inp_tuple)
def get_write_measure_variants(self, port, measure_obj):
"""Checks the measurement object and calls respective function for related measurement inputs."""
@ -341,9 +393,10 @@ class delay(simulation):
Write the measure statements to quantify the power results for a write port.
"""
# add measure statements for power
for measure in self.write_meas_objs:
measure_variant_inp_tuple = self.get_write_measure_variants(port, measure)
measure.write_measure(self.stim, measure_variant_inp_tuple)
for meas_list in self.write_meas_lists:
for measure in meas_list:
measure_variant_inp_tuple = self.get_write_measure_variants(port, measure)
measure.write_measure(self.stim, measure_variant_inp_tuple)
def write_delay_measures(self):
"""
@ -462,6 +515,7 @@ class delay(simulation):
#Sanity Check
debug.check(self.period > 0, "Target simulation period non-positive")
sim_passed = True
result = [{} for i in self.all_ports]
# Checking from not data_value to data_value
self.write_delay_stimulus()
@ -471,17 +525,18 @@ class delay(simulation):
#Loop through all targeted ports and collect delays and powers.
#Too much duplicate code here. Try reducing
for port in self.targ_read_ports:
debug.info(2, "Check delay values for port {}".format(port))
debug.info(2, "Checking delay values for port {}".format(port))
read_port_dict = {}
#Get measurements from output file
for measure in self.read_meas_objs:
for measure in self.read_lib_meas:
read_port_dict[measure.name] = measure.retrieve_measure(port=port)
debug_passed = self.check_debug_measures(port, read_port_dict)
#Check timing for read ports. Power is only checked if it was read correctly
if not self.check_valid_delays(read_port_dict):
if not self.check_valid_delays(read_port_dict) or not debug_passed:
return (False,{})
if not check_dict_values_is_float(read_port_dict):
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) #Printing the entire dict looks bad.
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) #Printing the entire dict looks bad.
result[port].update(read_port_dict)
@ -490,7 +545,7 @@ class delay(simulation):
for port in self.targ_write_ports:
write_port_dict = {}
for measure in self.write_meas_objs:
for measure in self.write_lib_meas:
write_port_dict[measure.name] = measure.retrieve_measure(port=port)
if not check_dict_values_is_float(write_port_dict):
@ -498,13 +553,54 @@ class delay(simulation):
result[port].update(write_port_dict)
# The delay is from the negative edge for our SRAM
return (True,result)
return (sim_passed,result)
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
# the read cycles i.e. neither of these measurements should pass.
success = True
for meas in self.debug_delay_meas:
val = meas.retrieve_measure(port=port)
debug.info(2,"{}={}".format(meas.name, val))
if type(val) != float:
continue
if meas.meta_add_delay:
max_delay = self.period/2
else:
max_delay = self.period
#If the debug measurement occurs after the original (and passes other conditions)
#then it fails i.e. the debug value represents the final (but failing) state of the output
parent_compare = type(read_measures[meas.parent]) != float or val > read_measures[meas.parent]
if 0 < val < max_delay and parent_compare:
success = False
debug.info(1, "Debug measurement failed. Incorrect Value found on output.")
break
for meas in self.debug_volt_meas:
val = meas.retrieve_measure(port=port)
debug.info(2,"{}={}".format(meas.name, val))
if type(val) != float:
continue
if meas.meta_str == 'read1' and val < tech.spice["v_threshold_typical"]:
success = False
debug.info(1, "Debug measurement failed. Value {}v was read on read 1 cycle.".format(val))
break
elif meas.meta_str == 'read0' and val > self.vdd_voltage-tech.spice["v_threshold_typical"]:
success = False
debug.info(1, "Debug measurement failed. Value {}v was read on read 0 cycle.".format(val))
break
return success
def evaluate_bitline_delay(self, port):
"""Parse and check the bitline delay. One of the measurements is expected to fail which warrants its own function."""
bl_delay_meas_dict = {}
values_added = 0 #For error checking
for measure in self.bitline_delay_objs:
for measure in self.bitline_delay_meas:
bl_delay_val = measure.retrieve_measure(port=port)
if type(bl_delay_val) != float or 0 > bl_delay_val or bl_delay_val > self.period/2: #Only add if value is valid, do not error.
debug.error("Bitline delay measurement failed: half-period={}, {}={}".format(self.period/2, measure.name, bl_delay_val),1)
@ -556,7 +652,8 @@ class delay(simulation):
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh)
half_period = self.period/2 #high-to-low delays start at neg. clk edge, so they need to be less than half_period
if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period:
if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \
or delay_hl<0 or delay_lh<0 or slew_hl<0 or slew_lh<0:
debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))

View File

@ -156,4 +156,32 @@ class voltage_when_measure(spice_measurement):
trig_voltage = self.trig_val_of_vdd*vdd_voltage
return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td)
return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td)
class voltage_at_measure(spice_measurement):
"""Generates a spice measurement to measure the voltage at a specific time.
The time is considered variant with different periods."""
def __init__(self, measure_name, targ_name, measure_scale=None):
spice_measurement.__init__(self, measure_name, measure_scale)
self.set_meas_constants(targ_name)
def get_measure_function(self):
return stimuli.gen_meas_find_voltage_at_time
def set_meas_constants(self, targ_name):
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
self.targ_name_no_port = targ_name
def get_measure_values(self, time_at, port=None):
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
if port != None:
#For dictionary indexing reasons, the name is formatted differently than the signals
meas_name = "{}{}".format(self.name, port)
targ_name = self.targ_name_no_port.format(port)
else:
meas_name = self.name
targ_name = self.targ_name_no_port
return (meas_name,targ_name,time_at)

View File

@ -226,6 +226,13 @@ class stimuli():
trig_dir,
trig_td))
def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at):
""" Creates the .meas statement for voltage at time"""
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
self.sf.write(measure_string.format(meas_name,
targ_name,
time_at))
def gen_meas_power(self, meas_name, t_initial, t_final):
""" Creates the .meas statement for the measurement of avg power """
# power mea cmd is different in different spice:

View File

@ -26,10 +26,15 @@ class timing_sram_test(openram_test):
reload(characterizer)
from characterizer import delay
from sram_config import sram_config
c = sram_config(word_size=1,
num_words=16,
# c = sram_config(word_size=1,
# num_words=16,
# num_banks=1)
# c.words_per_row=1
c = sram_config(word_size=32,
num_words=256,
num_banks=1)
c.words_per_row=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)
@ -42,13 +47,13 @@ class timing_sram_test(openram_test):
s.s.graph_exclude_data_dff()
s.s.graph_exclude_ctrl_dffs()
debug.info(1,'pins={}'.format(s.s.pins))
import graph_util
graph = graph_util.timing_graph()
pins=['DIN0[0]', 'ADDR0[0]', 'ADDR0[1]', 'ADDR0[2]', 'ADDR0[3]', 'csb0', 'web0', 'clk0', 'DOUT0[0]', 'vdd', 'gnd']
s.s.build_graph(graph,"Xsram",pins)
#debug.info(1,"{}".format(graph))
graph.print_all_paths('clk0', 'DOUT0[0]')
# debug.info(1,'pins={}'.format(s.s.pins))
# import graph_util
# graph = graph_util.timing_graph()
# pins=['DIN0[0]', 'ADDR0[0]', 'ADDR0[1]', 'ADDR0[2]', 'ADDR0[3]', 'csb0', 'web0', 'clk0', 'DOUT0[0]', 'vdd', 'gnd']
# s.s.build_graph(graph,"Xsram",pins)
# #debug.info(1,"{}".format(graph))
# graph.print_all_paths('clk0', 'DOUT0[0]')
# import sys
# sys.exit(1)