Mostly formatting. Added write measurements.

This commit is contained in:
Matt Guthaus 2019-07-24 10:57:33 -07:00
parent 3df8abd38c
commit 9cb96bda7d
1 changed files with 191 additions and 82 deletions

View File

@ -52,7 +52,7 @@ class delay(simulation):
self.add_graph_exclusions() self.add_graph_exclusions()
def create_measurement_names(self): def create_measurement_names(self):
"""Create measurement names. The names themselves currently define the type of measurement""" """ Create measurement names. The names themselves currently define the type of measurement """
self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"]
@ -60,14 +60,17 @@ class delay(simulation):
# self.bitline_delay_names = ["delay_bl", "delay_br"] # self.bitline_delay_names = ["delay_bl", "delay_br"]
def create_measurement_objects(self): def create_measurement_objects(self):
"""Create the measurements used for read and write ports""" """ Create the measurements used for read and write ports """
self.read_meas_lists = self.create_read_port_measurement_objects() self.read_meas_lists = self.create_read_port_measurement_objects()
self.write_meas_lists = self.create_write_port_measurement_objects() self.write_meas_lists = self.create_write_port_measurement_objects()
self.check_meas_names(self.read_meas_lists+self.write_meas_lists) self.check_meas_names(self.read_meas_lists+self.write_meas_lists)
def check_meas_names(self, measures_lists): def check_meas_names(self, measures_lists):
"""Given measurements (in 2d list), checks that their names are unique. """
Spice sim will fail otherwise.""" Given measurements (in 2d list), checks that their names are unique.
Spice sim will fail otherwise.
"""
name_set = set() name_set = set()
for meas_list in measures_lists: for meas_list in measures_lists:
for meas in meas_list: for meas in meas_list:
@ -121,6 +124,7 @@ class delay(simulation):
unique error checking, these are separated from other measurements. unique error checking, these are separated from other measurements.
These measurements are only associated with read values. These measurements are only associated with read values.
""" """
self.bitline_volt_meas = [] self.bitline_volt_meas = []
self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO", self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO",
@ -140,6 +144,7 @@ class delay(simulation):
def create_write_port_measurement_objects(self): def create_write_port_measurement_objects(self):
"""Create the measurements used for read ports: delays, slews, powers""" """Create the measurements used for read ports: delays, slews, powers"""
self.write_lib_meas = [] self.write_lib_meas = []
self.write_lib_meas.append(power_measure("write1_power", "RISE", measure_scale=1e3)) self.write_lib_meas.append(power_measure("write1_power", "RISE", measure_scale=1e3))
@ -149,10 +154,12 @@ class delay(simulation):
write_measures = [] write_measures = []
write_measures.append(self.write_lib_meas) write_measures.append(self.write_lib_meas)
write_measures.append(self.create_write_bit_measures())
return write_measures return write_measures
def create_debug_measurement_objects(self): def create_debug_measurement_objects(self):
"""Create debug measurement to help identify failures.""" """Create debug measurement to help identify failures."""
self.debug_volt_meas = [] self.debug_volt_meas = []
for meas in self.delay_meas: for meas in self.delay_meas:
# Output voltage measures # Output voltage measures
@ -167,7 +174,8 @@ class delay(simulation):
return self.debug_volt_meas+[self.sen_meas] return self.debug_volt_meas+[self.sen_meas]
def create_read_bit_measures(self): def create_read_bit_measures(self):
"""Adds bit measurements for read0 and read1 cycles""" """ Adds bit measurements for read0 and read1 cycles """
self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE) meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE)
for cycle in meas_cycles: for cycle in meas_cycles:
@ -178,10 +186,27 @@ class delay(simulation):
self.bit_meas[polarity].append(meas) self.bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements # Dictionary values are lists, reduce to a single list of measurements
return [meas for meas_list in self.bit_meas.values() for meas in meas_list] return [meas for meas_list in self.bit_meas.values() for meas in meas_list]
def create_write_bit_measures(self):
""" Adds bit measurements for write0 and write1 cycles """
self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE)
for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items():
meas.meta_str = cycle
self.bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements
return [meas for meas_list in self.bit_meas.values() for meas in meas_list]
def get_bit_measures(self, meas_tag, probe_address, probe_data): def get_bit_measures(self, meas_tag, probe_address, probe_data):
"""Creates measurements for the q/qbar of input bit position. """
meas_tag is a unique identifier for the measurement.""" Creates measurements for the q/qbar of input bit position.
meas_tag is a unique identifier for the measurement.
"""
bit_col = self.get_data_bit_column_number(probe_address, probe_data) bit_col = self.get_data_bit_column_number(probe_address, probe_data)
bit_row = self.get_address_row_number(probe_address) bit_row = self.get_address_row_number(probe_address)
(cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, bit_row, bit_col) (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, bit_row, bit_col)
@ -190,6 +215,7 @@ class delay(simulation):
"supported for characterization. Storage nets={}").format(storage_names)) "supported for characterization. Storage nets={}").format(storage_names))
q_name = cell_name+'.'+str(storage_names[0]) q_name = cell_name+'.'+str(storage_names[0])
qbar_name = cell_name+'.'+str(storage_names[1]) qbar_name = cell_name+'.'+str(storage_names[1])
# Bit measures, measurements times to be defined later. The measurement names must be unique # Bit measures, measurements times to be defined later. The measurement names must be unique
# but they is enforced externally # but they is enforced externally
q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name, has_port=False) q_meas = voltage_at_measure("v_q_{}".format(meas_tag), q_name, has_port=False)
@ -199,11 +225,13 @@ class delay(simulation):
def set_load_slew(self,load,slew): def set_load_slew(self,load,slew):
""" Set the load and slew """ """ Set the load and slew """
self.load = load self.load = load
self.slew = slew self.slew = slew
def add_graph_exclusions(self): def add_graph_exclusions(self):
"""Exclude portions of SRAM from timing graph which are not relevant""" """Exclude portions of SRAM from timing graph which are not relevant"""
# other initializations can only be done during analysis when a bit has been selected # other initializations can only be done during analysis when a bit has been selected
# for testing. # for testing.
self.sram.bank.graph_exclude_precharge() self.sram.bank.graph_exclude_precharge()
@ -214,6 +242,7 @@ class delay(simulation):
def create_graph(self): def create_graph(self):
"""Creates timing graph to generate the timing paths for the SRAM output.""" """Creates timing graph to generate the timing paths for the SRAM output."""
self.sram.bank.bitcell_array.init_graph_params() # Removes previous bit exclusions self.sram.bank.bitcell_array.init_graph_params() # Removes previous bit exclusions
self.sram.bank.bitcell_array.graph_exclude_bits(self.wordline_row, self.bitline_column) self.sram.bank.bitcell_array.graph_exclude_bits(self.wordline_row, self.bitline_column)
@ -224,6 +253,7 @@ class delay(simulation):
def set_internal_spice_names(self): def set_internal_spice_names(self):
"""Sets important names for characterization such as Sense amp enable and internal bit nets.""" """Sets important names for characterization such as Sense amp enable and internal bit nets."""
port = 0 port = 0
self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port), \ self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port), \
'{}{}_{}'.format(self.dout_name, port, self.probe_data)) '{}{}_{}'.format(self.dout_name, port, self.probe_data))
@ -233,10 +263,13 @@ class delay(simulation):
self.bl_name,self.br_name = self.get_bl_name(self.graph.all_paths) self.bl_name,self.br_name = self.get_bl_name(self.graph.all_paths)
debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name)) debug.info(2,"bl name={}, br name={}".format(self.bl_name,self.br_name))
def get_sen_name(self, paths): def get_sen_name(self, paths):
"""Gets the signal name associated with the sense amp enable from input paths. """
Only expects a single path to contain the sen signal name.""" Gets the signal name associated with the sense amp enable from input paths.
Only expects a single path to contain the sen signal name.
"""
sa_mods = factory.get_mods(OPTS.sense_amp) sa_mods = factory.get_mods(OPTS.sense_amp)
# Any sense amp instantiated should be identical, any change to that # Any sense amp instantiated should be identical, any change to that
# will require some identification to determine the mod desired. # will require some identification to determine the mod desired.
@ -247,6 +280,7 @@ class delay(simulation):
def get_bl_name(self, paths): def get_bl_name(self, paths):
"""Gets the signal name associated with the bitlines in the bank.""" """Gets the signal name associated with the bitlines in the bank."""
cell_mods = factory.get_mods(OPTS.bitcell) cell_mods = factory.get_mods(OPTS.bitcell)
if len(cell_mods)>=1: if len(cell_mods)>=1:
cell_mod = self.get_primary_cell_mod(cell_mods) cell_mod = self.get_primary_cell_mod(cell_mods)
@ -264,16 +298,20 @@ class delay(simulation):
bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set)) bl_names.append(self.get_alias_in_path(paths, int_net, cell_mod, exclude_set))
return bl_names[0], bl_names[1] return bl_names[0], bl_names[1]
def get_bl_name_search_exclusions(self): def get_bl_name_search_exclusions(self):
"""Gets the mods as a set which should be excluded while searching for name.""" """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 # Exclude the RBL as it contains bitcells which are not in the main bitcell array
# so it makes the search awkward # so it makes the search awkward
return set(factory.get_mods(OPTS.replica_bitline)) return set(factory.get_mods(OPTS.replica_bitline))
def get_primary_cell_mod(self, cell_mods): 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.""" Distinguish bitcell array mod from replica bitline array.
Assume there are no replica bitcells in the primary array.
"""
if len(cell_mods) == 1: if len(cell_mods) == 1:
return cell_mods[0] return cell_mods[0]
rbc_mods = factory.get_mods(OPTS.replica_bitcell) rbc_mods = factory.get_mods(OPTS.replica_bitcell)
@ -290,6 +328,7 @@ class delay(simulation):
def are_mod_pins_equal(self, mods): def are_mod_pins_equal(self, mods):
"""Determines if there are pins differences in the input mods""" """Determines if there are pins differences in the input mods"""
if len(mods) == 0: if len(mods) == 0:
return True return True
pins = mods[0].pins pins = mods[0].pins
@ -299,8 +338,11 @@ class delay(simulation):
return True return True
def get_alias_in_path(self, paths, int_net, mod, exclusion_set=None): 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""" Finds a single alias for the int_net in given paths.
More or less hits cause an error
"""
net_found = False net_found = False
for path in paths: for path in paths:
aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set) aliases = self.sram.find_aliases(self.sram_spc_name, self.pins, path, int_net, mod, exclusion_set)
@ -318,6 +360,7 @@ class delay(simulation):
def check_arguments(self): def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements""" """Checks if arguments given for write_stimulus() meets requirements"""
try: try:
int(self.probe_address, 2) int(self.probe_address, 2)
except ValueError: except ValueError:
@ -353,10 +396,12 @@ class delay(simulation):
def write_delay_stimulus(self): def write_delay_stimulus(self):
""" Creates a stimulus file for simulations to probe a bitcell at a given clock period. """
Creates a stimulus file for simulations to probe a bitcell at a given clock period.
Address and bit were previously set with set_probe(). Address and bit were previously set with set_probe().
Input slew (in ns) and output capacitive load (in fF) are required for charaterization. Input slew (in ns) and output capacitive load (in fF) are required for charaterization.
""" """
self.check_arguments() self.check_arguments()
# obtains list of time-points for each rising clk edge # obtains list of time-points for each rising clk edge
@ -452,7 +497,11 @@ class delay(simulation):
self.sf.close() self.sf.close()
def get_read_measure_variants(self, port, measure_obj): def get_read_measure_variants(self, port, measure_obj):
"""Checks the measurement object and calls respective function for related measurement inputs.""" """
Checks the measurement object and calls respective function for
related measurement inputs.
"""
meas_type = type(measure_obj) meas_type = type(measure_obj)
if meas_type is delay_measure or meas_type is slew_measure: if meas_type is delay_measure or meas_type is slew_measure:
variant_tuple = self.get_delay_measure_variants(port, measure_obj) variant_tuple = self.get_delay_measure_variants(port, measure_obj)
@ -472,7 +521,11 @@ class delay(simulation):
return variant_tuple return variant_tuple
def get_delay_measure_variants(self, port, delay_obj): def get_delay_measure_variants(self, port, delay_obj):
"""Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" """
Get the measurement values that can either vary from simulation to
simulation (vdd, address) or port to port (time delays)
"""
# Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port # Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port
# vdd is arguably constant as that is true for a single lib file. # vdd is arguably constant as that is true for a single lib file.
if delay_obj.meta_str == sram_op.READ_ZERO: if delay_obj.meta_str == sram_op.READ_ZERO:
@ -491,6 +544,7 @@ class delay(simulation):
def get_power_measure_variants(self, port, power_obj, operation): def get_power_measure_variants(self, port, power_obj, operation):
"""Get the measurement values that can either vary port to port (time delays)""" """Get the measurement values that can either vary port to port (time delays)"""
# Return value is intended to match the power measure format: t_initial, t_final, port # Return value is intended to match the power measure format: t_initial, t_final, port
t_initial = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]] t_initial = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]]
t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]+1] t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]+1]
@ -498,21 +552,21 @@ class delay(simulation):
return (t_initial, t_final, port) return (t_initial, t_final, port)
def get_volt_at_measure_variants(self, port, volt_meas): 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. Get the measurement values that can either vary port to port (time delays)
if volt_meas.meta_str == sram_op.READ_ZERO: """
# 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]] meas_cycle = self.cycle_times[self.measure_cycles[port][volt_meas.meta_str]]
elif volt_meas.meta_str == sram_op.READ_ONE:
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 # Measurement occurs at the end of the period -> current period start + period
at_time = meas_cycle+self.period at_time = meas_cycle+self.period
return (at_time, port) return (at_time, port)
def get_volt_when_measure_variants(self, port, volt_meas): def get_volt_when_measure_variants(self, port, volt_meas):
"""Get the measurement values that can either vary port to port (time delays)""" """
Get the measurement values that can either vary port to port (time delays)
"""
# Only checking 0 value reads for now. # Only checking 0 value reads for now.
t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]] t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]]
@ -522,6 +576,7 @@ class delay(simulation):
""" """
Write the measure statements to quantify the delay and power results for a read port. Write the measure statements to quantify the delay and power results for a read port.
""" """
# add measure statements for delays/slews # add measure statements for delays/slews
for meas_list in self.read_meas_lists: for meas_list in self.read_meas_lists:
for measure in meas_list: for measure in meas_list:
@ -529,17 +584,31 @@ class delay(simulation):
measure.write_measure(self.stim, measure_variant_inp_tuple) measure.write_measure(self.stim, measure_variant_inp_tuple)
def get_write_measure_variants(self, port, measure_obj): def get_write_measure_variants(self, port, measure_obj):
"""Checks the measurement object and calls respective function for related measurement inputs.""" """
Checks the measurement object and calls respective function for related measurement inputs.
"""
meas_type = type(measure_obj) meas_type = type(measure_obj)
if meas_type is power_measure: if meas_type is power_measure:
return self.get_power_measure_variants(port, measure_obj, "write") return self.get_power_measure_variants(port, measure_obj, "write")
elif meas_type is voltage_at_measure:
variant_tuple = self.get_volt_at_measure_variants(port, measure_obj)
else: else:
debug.error("Input function not defined for measurement type={}".format(meas_type)) debug.error("Input function not defined for measurement type={}".format(meas_type))
# Removes port input from any object which does not use it. This shorthand only works if
# the measurement has port as the last input. Could be implemented by measurement type or
# remove entirely from measurement classes.
if not measure_obj.has_port:
variant_tuple = variant_tuple[:-1]
return variant_tuple
def write_delay_measures_write_port(self, port): def write_delay_measures_write_port(self, port):
""" """
Write the measure statements to quantify the power results for a write port. Write the measure statements to quantify the power results for a write port.
""" """
# add measure statements for power # add measure statements for power
for meas_list in self.write_meas_lists: for meas_list in self.write_meas_lists:
for measure in meas_list: for measure in meas_list:
@ -550,17 +619,22 @@ class delay(simulation):
""" """
Write the measure statements to quantify the delay and power results for all targeted ports. Write the measure statements to quantify the delay and power results for all targeted ports.
""" """
self.sf.write("\n* Measure statements for delay and power\n") self.sf.write("\n* Measure statements for delay and power\n")
# Output some comments to aid where cycles start and # Output some comments to aid where cycles start and
# what is happening # what is happening
for comment in self.cycle_comments: for comment in self.cycle_comments:
self.sf.write("* {}\n".format(comment)) self.sf.write("* {}\n".format(comment))
self.sf.write("\n")
for read_port in self.targ_read_ports: for read_port in self.targ_read_ports:
self.write_delay_measures_read_port(read_port) self.sf.write("* Read ports {}\n".format(read_port))
self.write_delay_measures_read_port(read_port)
for write_port in self.targ_write_ports: for write_port in self.targ_write_ports:
self.write_delay_measures_write_port(write_port) self.sf.write("* Write ports {}\n".format(write_port))
self.write_delay_measures_write_port(write_port)
def write_power_measures(self): def write_power_measures(self):
@ -595,14 +669,16 @@ class delay(simulation):
debug.error("Timed out, could not find a feasible period.",2) debug.error("Timed out, could not find a feasible period.",2)
# Clear any write target ports and set read port # Clear any write target ports and set read port
self.targ_write_ports = [] self.targ_write_ports = [port]
self.targ_read_ports = [port] self.targ_read_ports = [port]
success = False success = False
debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port)) debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port))
self.period = feasible_period self.period = feasible_period
(success, results)=self.run_delay_simulation() (success, results)=self.run_delay_simulation()
# Clear these target ports after simulation # Clear these target ports after simulation
self.targ_write_ports = []
self.targ_read_ports = [] self.targ_read_ports = []
if not success: if not success:
@ -615,9 +691,9 @@ class delay(simulation):
delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(*feasible_delays) delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(*feasible_delays)
slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(*feasible_slews) slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(*feasible_slews)
debug.info(2, "feasible_period passed for Port {3}: {0}ns {1} {2} ".format(feasible_period, debug.info(2, "feasible_period passed for Port {3}: {0}ns {1} {2} ".format(feasible_period,
delay_str, delay_str,
slew_str, slew_str,
port)) port))
if success: if success:
debug.info(2, "Found feasible_period for port {0}: {1}ns".format(port, feasible_period)) debug.info(2, "Found feasible_period for port {0}: {1}ns".format(port, feasible_period))
@ -660,39 +736,23 @@ class delay(simulation):
works on the trimmed netlist by default, so powers do not works on the trimmed netlist by default, so powers do not
include leakage of all cells. include leakage of all cells.
""" """
# Sanity Check
debug.check(self.period > 0, "Target simulation period non-positive") 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() self.write_delay_stimulus()
self.stim.run_sim() self.stim.run_sim()
self.check_measurements()
def check_measurements(self):
""" Check the write and read measurements """
# Loop through all targeted ports and collect delays and powers. # Loop through all targeted ports and collect delays and powers.
# Too much duplicate code here. Try reducing result = [{} for i in self.all_ports]
for port in self.targ_read_ports:
debug.info(2, "Checking delay values for port {}".format(port))
read_port_dict = {}
# 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)
success = success and self.check_bit_measures()
# Check timing for read ports. Power is only checked if it was read correctly
if not self.check_valid_delays(read_port_dict) or not success:
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.
result[port].update(read_port_dict)
for port in self.targ_write_ports: for port in self.targ_write_ports:
debug.info(2, "Checking write values for port {}".format(port))
write_port_dict = {} write_port_dict = {}
for measure in self.write_lib_meas: for measure in self.write_lib_meas:
write_port_dict[measure.name] = measure.retrieve_measure(port=port) write_port_dict[measure.name] = measure.retrieve_measure(port=port)
@ -701,21 +761,45 @@ class delay(simulation):
debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict),1) # Printing the entire dict looks bad. debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict),1) # Printing the entire dict looks bad.
result[port].update(write_port_dict) result[port].update(write_port_dict)
# The delay is from the negative edge for our SRAM
return (sim_passed,result) for port in self.targ_read_ports:
debug.info(2, "Checking read delay values for port {}".format(port))
read_port_dict = {}
# 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_read_debug_measures(port)
success = success and self.check_bit_measures()
# Check timing for read ports. Power is only checked if it was read correctly
if not self.check_valid_delays(read_port_dict) or not success:
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.
result[port].update(read_port_dict)
return (True,result)
def check_sen_measure(self, port): def check_sen_measure(self, port):
"""Checks that the sen occurred within a half-period""" """Checks that the sen occurred within a half-period"""
sen_val = self.sen_meas.retrieve_measure(port=port) sen_val = self.sen_meas.retrieve_measure(port=port)
debug.info(2,"S_EN delay={} ns".format(sen_val)) debug.info(2,"s_en delay={}ns".format(sen_val))
if self.sen_meas.meta_add_delay: if self.sen_meas.meta_add_delay:
max_delay = self.period/2 max_delay = self.period/2
else: else:
max_delay = self.period max_delay = self.period
return not (type(sen_val) != float or sen_val > max_delay) return not (type(sen_val) != float or sen_val > max_delay)
def check_debug_measures(self, port):
def check_read_debug_measures(self, port):
"""Debug measures that indicate special conditions.""" """Debug measures that indicate special conditions."""
# Currently, only check if the opposite than intended value was read during # Currently, only check if the opposite than intended value was read during
# the read cycles i.e. neither of these measurements should pass. # the read cycles i.e. neither of these measurements should pass.
success = True success = True
@ -756,8 +840,10 @@ class delay(simulation):
def check_bit_measures(self): def check_bit_measures(self):
"""Checks the measurements which represent the internal storage voltages """
at the end of the read cycle.""" Checks the measurements which represent the internal storage voltages
at the end of the read cycle.
"""
success = True success = True
for polarity, meas_list in self.bit_meas.items(): for polarity, meas_list in self.bit_meas.items():
for meas in meas_list: for meas in meas_list:
@ -780,8 +866,10 @@ class delay(simulation):
return success return success
def check_bitline_meas(self, v_discharged_bl, v_charged_bl): def check_bitline_meas(self, v_discharged_bl, v_charged_bl):
"""Checks the value of the discharging bitline. Confirms s_en timing errors. """
Returns true if the bitlines are at there expected value.""" Checks the value of the discharging bitline. Confirms s_en timing errors.
Returns true if the bitlines are at there expected value.
"""
# The inputs looks at discharge/charged bitline rather than left or right (bl/br) # The inputs looks at discharge/charged bitline rather than left or right (bl/br)
# Performs two checks, discharging bitline is at least 10% away from vdd and there is a # Performs two checks, discharging bitline is at least 10% away from vdd and there is a
# 10% vdd difference between the bitlines. Both need to fail to be considered a s_en error. # 10% vdd difference between the bitlines. Both need to fail to be considered a s_en error.
@ -794,8 +882,8 @@ class delay(simulation):
def run_power_simulation(self): def run_power_simulation(self):
""" """
This simulates a disabled SRAM to get the leakage power when it is off. This simulates a disabled SRAM to get the leakage power when it is off.
""" """
debug.info(1, "Performing leakage power simulations.") debug.info(1, "Performing leakage power simulations.")
self.write_power_stimulus(trim=False) self.write_power_stimulus(trim=False)
self.stim.run_sim() self.stim.run_sim()
@ -817,6 +905,7 @@ class delay(simulation):
def check_valid_delays(self, result_dict): def check_valid_delays(self, result_dict):
""" Check if the measurements are defined and if they are valid. """ """ Check if the measurements are defined and if they are valid. """
# Hard coded names currently # Hard coded names currently
delay_hl = result_dict["delay_hl"] delay_hl = result_dict["delay_hl"]
delay_lh = result_dict["delay_lh"] delay_lh = result_dict["delay_lh"]
@ -884,6 +973,7 @@ class delay(simulation):
# Binary search algorithm to find the min period (max frequency) of input port # Binary search algorithm to find the min period (max frequency) of input port
time_out = 25 time_out = 25
self.targ_write_ports = [port]
self.targ_read_ports = [port] self.targ_read_ports = [port]
while True: while True:
time_out -= 1 time_out -= 1
@ -892,9 +982,9 @@ class delay(simulation):
self.period = target_period self.period = target_period
debug.info(1, "MinPeriod Search Port {3}: {0}ns (ub: {1} lb: {2})".format(target_period, debug.info(1, "MinPeriod Search Port {3}: {0}ns (ub: {1} lb: {2})".format(target_period,
ub_period, ub_period,
lb_period, lb_period,
port)) port))
if self.try_period(feasible_delays): if self.try_period(feasible_delays):
ub_period = target_period ub_period = target_period
@ -915,14 +1005,16 @@ class delay(simulation):
This tries to simulate a period and checks if the result This tries to simulate a period and checks if the result
works. If it does and the delay is within 5% still, it returns True. works. If it does and the delay is within 5% still, it returns True.
""" """
# Run Delay simulation but Power results not used. # Run Delay simulation but Power results not used.
(success, results) = self.run_delay_simulation() (success, results) = self.run_delay_simulation()
if not success: if not success:
return False return False
# Check the values of target readwrite and read ports. Write ports do not produce delays in this current version # Check the values of target readwrite and read ports. Write ports do not produce delays in this current version
for port in self.targ_read_ports: for port in self.targ_read_ports:
for dname in self.delay_meas_names: # check that the delays and slews do not degrade with tested period. # check that the delays and slews do not degrade with tested period.
for dname in self.delay_meas_names:
# FIXME: This is a hack solution to fix the min period search. The slew will always be based on the period when there # FIXME: This is a hack solution to fix the min period search. The slew will always be based on the period when there
# is a column mux. Therefore, the checks are skipped for this condition. This is hard to solve without changing the netlist. # is a column mux. Therefore, the checks are skipped for this condition. This is hard to solve without changing the netlist.
@ -936,7 +1028,6 @@ class delay(simulation):
# key=raw_input("press return to continue") # key=raw_input("press return to continue")
# Dynamic way to build string. A bit messy though.
delay_str = ', '.join("{0}={1}ns".format(mname, results[port][mname]) for mname in self.delay_meas_names) delay_str = ', '.join("{0}={1}ns".format(mname, results[port][mname]) for mname in self.delay_meas_names)
debug.info(2,"Successful period {0}, Port {2}, {1}".format(self.period, debug.info(2,"Successful period {0}, Port {2}, {1}".format(self.period,
delay_str, delay_str,
@ -944,8 +1035,11 @@ class delay(simulation):
return True return True
def set_probe(self,probe_address, probe_data): def set_probe(self,probe_address, probe_data):
""" Probe address and data can be set separately to utilize other """
functions in this characterizer besides analyze.""" Probe address and data can be set separately to utilize other
functions in this characterizer besides analyze.
"""
self.probe_address = probe_address self.probe_address = probe_address
self.probe_data = probe_data self.probe_data = probe_data
self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data)
@ -954,6 +1048,7 @@ class delay(simulation):
def get_data_bit_column_number(self, probe_address, probe_data): def get_data_bit_column_number(self, probe_address, probe_data):
"""Calculates bitline column number of data bit under test using bit position and mux size""" """Calculates bitline column number of data bit under test using bit position and mux size"""
if self.sram.col_addr_size>0: if self.sram.col_addr_size>0:
col_address = int(probe_address[0:self.sram.col_addr_size],2) col_address = int(probe_address[0:self.sram.col_addr_size],2)
else: else:
@ -963,6 +1058,7 @@ class delay(simulation):
def get_address_row_number(self, probe_address): def get_address_row_number(self, probe_address):
"""Calculates wordline row number of data bit under test using address and column mux size""" """Calculates wordline row number of data bit under test using address and column mux size"""
return int(probe_address[self.sram.col_addr_size:],2) return int(probe_address[self.sram.col_addr_size:],2)
def prepare_netlist(self): def prepare_netlist(self):
@ -988,6 +1084,7 @@ class delay(simulation):
def analysis_init(self, probe_address, probe_data): def analysis_init(self, probe_address, probe_data):
"""Sets values which are dependent on the data address/bit being tested.""" """Sets values which are dependent on the data address/bit being tested."""
self.set_probe(probe_address, probe_data) self.set_probe(probe_address, probe_data)
self.create_graph() self.create_graph()
self.set_internal_spice_names() self.set_internal_spice_names()
@ -998,6 +1095,7 @@ class delay(simulation):
""" """
Main function to characterize an SRAM for a table. Computes both delay and power characterization. Main function to characterize an SRAM for a table. Computes both delay and power characterization.
""" """
# Dict to hold all characterization values # Dict to hold all characterization values
char_sram_data = {} char_sram_data = {}
self.analysis_init(probe_address, probe_data) self.analysis_init(probe_address, probe_data)
@ -1030,6 +1128,7 @@ class delay(simulation):
def alter_lh_char_data(self, char_port_data): def alter_lh_char_data(self, char_port_data):
"""Copies high-to-low data to low-to-high data to make them consistent on the same clock edge.""" """Copies high-to-low data to low-to-high data to make them consistent on the same clock edge."""
# This is basically a hack solution which should be removed/fixed later. # This is basically a hack solution which should be removed/fixed later.
for port in self.all_ports: for port in self.all_ports:
char_port_data[port]['delay_lh'] = char_port_data[port]['delay_hl'] char_port_data[port]['delay_lh'] = char_port_data[port]['delay_hl']
@ -1037,6 +1136,7 @@ class delay(simulation):
def simulate_loads_and_slews(self, slews, loads, leakage_offset): def simulate_loads_and_slews(self, slews, loads, leakage_offset):
"""Simulate all specified output loads and input slews pairs of all ports""" """Simulate all specified output loads and input slews pairs of all ports"""
measure_data = self.get_empty_measure_data_dict() measure_data = self.get_empty_measure_data_dict()
# Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways. # Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways.
self.targ_read_ports = self.read_ports self.targ_read_ports = self.read_ports
@ -1060,6 +1160,7 @@ class delay(simulation):
def calculate_inverse_address(self): def calculate_inverse_address(self):
"""Determine dummy test address based on probe address and column mux size.""" """Determine dummy test address based on probe address and column mux size."""
# The inverse address needs to share the same bitlines as the probe address as the trimming will remove all other bitlines # 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. # 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.probe_address[:self.sram.col_addr_size] # do not invert this part
@ -1125,6 +1226,7 @@ class delay(simulation):
self.probe_address,data_zeros) self.probe_address,data_zeros)
def get_available_port(self,get_read_port): def get_available_port(self,get_read_port):
"""Returns the first accessible read or write port. """ """Returns the first accessible read or write port. """
if get_read_port and len(self.read_ports) > 0: if get_read_port and len(self.read_ports) > 0:
return self.read_ports[0] return self.read_ports[0]
@ -1142,6 +1244,7 @@ class delay(simulation):
of the cycles to do a timing evaluation. The last time is the end of the simulation of the cycles to do a timing evaluation. The last time is the end of the simulation
and does not need a rising edge. and does not need a rising edge.
""" """
# Using this requires setting at least one port to target for simulation. # Using this requires setting at least one port to target for simulation.
if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0: if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0:
debug.error("No port selected for characterization.",1) debug.error("No port selected for characterization.",1)
@ -1173,7 +1276,8 @@ class delay(simulation):
self.gen_test_cycles_one_port(cur_read_port, cur_write_port) self.gen_test_cycles_one_port(cur_read_port, cur_write_port)
def analytical_delay(self, slews, loads): def analytical_delay(self, slews, loads):
""" Return the analytical model results for the SRAM. """
Return the analytical model results for the SRAM.
""" """
if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0:
debug.warning("Analytical characterization results are not supported for multiport.") debug.warning("Analytical characterization results are not supported for multiport.")
@ -1206,6 +1310,7 @@ class delay(simulation):
def analytical_power(self, slews, loads): def analytical_power(self, slews, loads):
"""Get the dynamic and leakage power from the SRAM""" """Get the dynamic and leakage power from the SRAM"""
# slews unused, only last load is used # slews unused, only last load is used
load = loads[-1] load = loads[-1]
power = self.sram.analytical_power(self.corner, load) power = self.sram.analytical_power(self.corner, load)
@ -1218,6 +1323,7 @@ class delay(simulation):
def gen_data(self): def gen_data(self):
""" Generates the PWL data inputs for a simulation timing test. """ """ Generates the PWL data inputs for a simulation timing test. """
for write_port in self.write_ports: for write_port in self.write_ports:
for i in range(self.word_size): for i in range(self.word_size):
sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i) sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i)
@ -1228,6 +1334,7 @@ class delay(simulation):
Generates the address inputs for a simulation timing test. Generates the address inputs for a simulation timing test.
This alternates between all 1's and all 0's for the address. This alternates between all 1's and all 0's for the address.
""" """
for port in self.all_ports: for port in self.all_ports:
for i in range(self.addr_size): for i in range(self.addr_size):
sig_name = "{0}{1}_{2}".format(self.addr_name,port,i) sig_name = "{0}{1}_{2}".format(self.addr_name,port,i)
@ -1235,6 +1342,7 @@ class delay(simulation):
def gen_control(self): def gen_control(self):
""" Generates the control signals """ """ Generates the control signals """
for port in self.all_ports: for port in self.all_ports:
self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
if port in self.readwrite_ports: if port in self.readwrite_ports:
@ -1243,6 +1351,7 @@ class delay(simulation):
def get_empty_measure_data_dict(self): def get_empty_measure_data_dict(self):
"""Make a dict of lists for each type of delay and power measurement to append results to""" """Make a dict of lists for each type of delay and power measurement to append results to"""
measure_names = self.delay_meas_names + self.power_meas_names measure_names = self.delay_meas_names + self.power_meas_names
# Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. # Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists.
measure_data = [{mname:[] for mname in measure_names} for i in self.all_ports] measure_data = [{mname:[] for mname in measure_names} for i in self.all_ports]