Edited lib to support port indexing. Edited tests in reaction to name dict name changes. Cleaned up measurement value generation in delay.

This commit is contained in:
Hunter Nichols 2018-09-16 23:15:22 -07:00
parent ab7d3510b5
commit 4586ed343f
4 changed files with 104 additions and 112 deletions

View File

@ -203,76 +203,64 @@ class delay():
self.sf.close()
def get_delay_meas_values(self, delay_name, port):
"""Get the values needed to generate a Spice measurement statement based on the name of the measurement."""
debug.check('lh' in delay_name or 'hl' in delay_name, "Measure command {0} does not contain direction (lh/hl)")
trig_clk_name = "clk"
meas_name="{0}{1}".format(delay_name, port)
targ_name = "{0}".format("DOUT{0}[{1}]".format(port,self.probe_data))
half_vdd = 0.5 * self.vdd_voltage
trig_slew_low = 0.1 * self.vdd_voltage
targ_slew_high = 0.9 * self.vdd_voltage
if 'delay' in delay_name:
trig_dir="RISE"
trig_val = half_vdd
targ_val = half_vdd
trig_name = trig_clk_name
if 'lh' in delay_name:
targ_dir="RISE"
trig_td = targ_td = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]
else:
targ_dir="FALL"
trig_td = targ_td = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]
elif 'slew' in delay_name:
trig_name = targ_name
if 'lh' in delay_name:
trig_val = trig_slew_low
targ_val = targ_slew_high
targ_dir = trig_dir = "RISE"
trig_td = targ_td = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]
else:
trig_val = targ_slew_high
targ_val = trig_slew_low
targ_dir = trig_dir = "FALL"
trig_td = targ_td = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]
else:
debug.error(1, "Measure command {0} not recognized".format(delay_name))
return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td)
def write_delay_measures_read_port(self, port):
"""
Write the measure statements to quantify the delay and power results for a read port.
"""
# Trigger on the clk of the appropriate cycle
trig_clk_name = trig_name = "clk"
#Target name should be an input to the function or a member variable. That way, the ports can be singled out for testing
targ_name = "{0}".format("DOUT{0}[{1}]".format(port,self.probe_data))
trig_val = targ_val = 0.5 * self.vdd_voltage
trig_delay_val = targ_delay_val = 0.5 * self.vdd_voltage
trig_slew_low = 0.1 * self.vdd_voltage
targ_slew_high = 0.9 * self.vdd_voltage
trig_dir = "FALL"
targ_dir = "RISE"
trig_td = targ_td = 0
parse_error = False
# Delay the target to measure after the negative edge
# add measure statements for delays/slews
for dname in self.delay_meas_names:
if 'delay' in dname:
trig_dir="RISE"
trig_val = trig_delay_val
targ_val = targ_val
trig_name = trig_clk_name
if 'lh' in dname:
targ_dir="RISE"
else:
targ_dir="FALL"
elif 'slew' in dname:
trig_name = targ_name
if 'lh' in dname:
trig_val = trig_slew_low
targ_val = targ_slew_high
targ_dir = trig_dir = "RISE"
else:
trig_val = targ_slew_high
targ_val = trig_slew_low
targ_dir = trig_dir = "FALL"
else:
debug.error(1, "Measure command {0} not recognized".format(dname))
meas_values = self.get_delay_meas_values(dname, port)
self.stim.gen_meas_delay(*meas_values)
if 'lh' in dname:
trig_td = targ_td = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]
elif 'hl' in dname:
trig_td = targ_td = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]
else:
debug.error(1, "Measure command {0} does not contain direction (lh/hl)".format(dname))
self.stim.gen_meas_delay(meas_name="{0}{1}".format(dname, port),
trig_name=trig_name,
targ_name=targ_name,
trig_val=trig_val,
targ_val=targ_val,
trig_dir=trig_dir,
targ_dir=targ_dir,
trig_td=trig_td,
targ_td=targ_td)
# add measure statements for power
for pname in self.power_meas_names:
if "read" not in pname:
continue
t_initial = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]
t_final = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]+1]
#Different naming schemes are used for the measure cycle dict and measurement names.
#TODO: make them the same so they can be indexed the same.
if '1' in pname:
t_initial = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]
t_final = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]+1]
elif '0' in pname:
t_initial = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]
t_final = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]+1]
self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port),
t_initial=t_initial,
t_final=t_final)
@ -429,7 +417,7 @@ class delay():
include leakage of all cells.
"""
#Sanity Check
debug.check(self.period > 0, "Initial starting period not defined")
debug.check(self.period > 0, "Target simulation period non-positive")
result = [{} for i in range(self.total_port_num)]
# Checking from not data_value to data_value
@ -645,7 +633,7 @@ class delay():
Main function to characterize an SRAM for a table. Computes both delay and power characterization.
"""
#Dict to hold all characterization values
char_data = {}
char_sram_data = {}
self.set_probe(probe_address, probe_data)
@ -681,18 +669,17 @@ class delay():
min_period = self.find_min_period(feasible_delays)
debug.check(type(min_period)==float,"Couldn't find minimum period.")
debug.info(1, "Min Period Found: {0}ns".format(min_period))
char_data["min_period"] = round_time(min_period)
char_sram_data["min_period"] = round_time(min_period)
# 3) Find the leakage power of the trimmmed and UNtrimmed arrays.
(full_array_leakage, trim_array_leakage)=self.run_power_simulation()
char_data["leakage_power"]=full_array_leakage
char_sram_data["leakage_power"]=full_array_leakage
leakage_offset = full_array_leakage - trim_array_leakage
# 4) At the minimum period, measure the delay, slew and power for all slew/load pairs.
load_slew_data = self.simulate_loads_and_slews(slews, loads, leakage_offset)
#char_data.update(load_slew_data)
char_port_data = self.simulate_loads_and_slews(slews, loads, leakage_offset)
return (char_data, load_slew_data)
return (char_sram_data, char_port_data)
def simulate_loads_and_slews(self, slews, loads, leakage_offset):
"""Simulate all specified output loads and input slews pairs of all ports"""
@ -979,18 +966,18 @@ class delay():
debug.info(1,"Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1,"Leakage Power: {0} mW".format(power.leakage))
data = {"min_period": 0,
"delay_lh0": delay_lh,
"delay_hl0": delay_hl,
"slew_lh0": slew_lh,
"slew_hl0": slew_hl,
"read0_power0": power.dynamic,
"read1_power0": power.dynamic,
"write0_power0": power.dynamic,
"write1_power0": power.dynamic,
"leakage_power": power.leakage
}
return data
sram_data = { "min_period": 0,
"leakage_power": power.leakage}
port_data = [{"delay_lh": delay_lh,
"delay_hl": delay_hl,
"slew_lh": slew_lh,
"slew_hl": slew_hl,
"read0_power": power.dynamic,
"read1_power": power.dynamic,
"write0_power": power.dynamic,
"write1_power": power.dynamic,
}]
return (sram_data,port_data)
def gen_data(self):
""" Generates the PWL data inputs for a simulation timing test. """

View File

@ -161,7 +161,7 @@ class lib:
# Leakage is included in dynamic when macro is enabled
self.lib.write(" leakage_power () {\n")
self.lib.write(" when : \"{0}\";\n".format(control_str))
self.lib.write(" value : {};\n".format(self.char_results["leakage_power"]))
self.lib.write(" value : {};\n".format(self.char_sram_results["leakage_power"]))
self.lib.write(" }\n")
self.lib.write(" cell_leakage_power : {};\n".format(0))
@ -347,16 +347,16 @@ class lib:
self.lib.write(" related_pin : \"clk\"; \n")
self.lib.write(" timing_type : rising_edge; \n")
self.lib.write(" cell_rise(CELL_TABLE) {\n")
self.write_values(self.char_results["delay_lh{0}".format(read_port)],len(self.loads)," ")
self.write_values(self.char_port_results[read_port]["delay_lh"],len(self.loads)," ")
self.lib.write(" }\n") # rise delay
self.lib.write(" cell_fall(CELL_TABLE) {\n")
self.write_values(self.char_results["delay_hl{0}".format(read_port)],len(self.loads)," ")
self.write_values(self.char_port_results[read_port]["delay_hl"],len(self.loads)," ")
self.lib.write(" }\n") # fall delay
self.lib.write(" rise_transition(CELL_TABLE) {\n")
self.write_values(self.char_results["slew_lh{0}".format(read_port)],len(self.loads)," ")
self.write_values(self.char_port_results[read_port]["slew_lh"],len(self.loads)," ")
self.lib.write(" }\n") # rise trans
self.lib.write(" fall_transition(CELL_TABLE) {\n")
self.write_values(self.char_results["slew_hl{0}".format(read_port)],len(self.loads)," ")
self.write_values(self.char_port_results[read_port]["slew_hl"],len(self.loads)," ")
self.lib.write(" }\n") # fall trans
self.lib.write(" }\n") # timing
self.lib.write(" }\n") # pin
@ -428,8 +428,8 @@ class lib:
for port in range(self.total_port_num):
self.add_clk_control_power(port)
min_pulse_width = round_time(self.char_results["min_period"])/2.0
min_period = round_time(self.char_results["min_period"])
min_pulse_width = round_time(self.char_sram_results["min_period"])/2.0
min_period = round_time(self.char_sram_results["min_period"])
self.lib.write(" timing(){ \n")
self.lib.write(" timing_type :\"min_pulse_width\"; \n")
self.lib.write(" related_pin : clk; \n")
@ -461,7 +461,7 @@ class lib:
if port in self.write_ports:
if port in self.read_ports:
web_name = " & !WEb{0}".format(port)
avg_write_power = np.mean(self.char_results["write1_power{0}".format(port)] + self.char_results["write0_power{0}".format(port)])
avg_write_power = np.mean(self.char_port_results[port]["write1_power"] + self.char_port_results[port]["write0_power"])
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"!CSb{0} & clk{1}\"; \n".format(port, web_name))
self.lib.write(" rise_power(scalar){\n")
@ -475,7 +475,7 @@ class lib:
if port in self.read_ports:
if port in self.write_ports:
web_name = " & WEb{0}".format(port)
avg_read_power = np.mean(self.char_results["read1_power{0}".format(port)] + self.char_results["read0_power{0}".format(port)])
avg_read_power = np.mean(self.char_port_results[port]["read1_power"] + self.char_port_results[port]["read0_power"])
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"!CSb{0} & !clk{1}\"; \n".format(port, web_name))
self.lib.write(" rise_power(scalar){\n")
@ -502,13 +502,14 @@ class lib:
if not hasattr(self,"d"):
self.d = delay(self.sram, self.sp_file, self.corner)
if self.use_model:
self.char_results = self.d.analytical_delay(self.sram,self.slews,self.loads)
char_results = self.d.analytical_delay(self.sram,self.slews,self.loads)
self.char_sram_results, self.char_port_results = char_results
else:
probe_address = "1" * self.sram.addr_size
probe_data = self.sram.word_size - 1
self.char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
self.char_sram_results, self.char_port_results = char_results
def compute_setup_hold(self):
""" Do the analysis if we haven't characterized a FF yet """
# Do the analysis if we haven't characterized a FF yet

View File

@ -48,12 +48,14 @@ class timing_sram_test(openram_test):
import tech
loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data, slews, loads)
data, port_data = d.analyze(probe_address, probe_data, slews, loads)
#Combine info about port into all data
data.update(port_data[0])
#Assumes single rw port (6t sram)
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl0': [2.5829000000000004],
'delay_lh0': [0.2255964],
golden_data = {'delay_hl': [2.5829000000000004],
'delay_lh': [0.2255964],
'leakage_power': 0.0019498999999999996,
'min_period': 4.844,
'read0_power0': [0.055371399999999994],
@ -63,16 +65,16 @@ class timing_sram_test(openram_test):
'write0_power0': [0.06545659999999999],
'write1_power0': [0.057846299999999996]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl0': [3.452],
'delay_lh0': [1.3792000000000002],
golden_data = {'delay_hl': [3.452],
'delay_lh': [1.3792000000000002],
'leakage_power': 0.0257065,
'min_period': 4.688,
'read0_power0': [15.0755],
'read1_power0': [14.4526],
'slew_hl0': [0.6137363],
'slew_lh0': [0.3381045],
'write0_power0': [16.9203],
'write1_power0': [15.367]}
'read0_power': [15.0755],
'read1_power': [14.4526],
'slew_hl': [0.6137363],
'slew_lh': [0.3381045],
'write0_power': [16.9203],
'write1_power': [15.367]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -48,11 +48,13 @@ class timing_sram_test(openram_test):
import tech
loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data, slews, loads)
data, port_data = d.analyze(probe_address, probe_data, slews, loads)
#Combine info about port into all data
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl0': [2.584251],
'delay_lh0': [0.22870469999999998],
golden_data = {'delay_hl': [2.584251],
'delay_lh': [0.22870469999999998],
'leakage_power': 0.0009567935,
'min_period': 4.844,
'read0_power0': [0.0547588],
@ -62,16 +64,16 @@ class timing_sram_test(openram_test):
'write0_power0': [0.06513271999999999],
'write1_power0': [0.058057000000000004]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl0': [3.644147],
'delay_lh0': [1.629815],
golden_data = {'delay_hl': [3.644147],
'delay_lh': [1.629815],
'leakage_power': 0.0009299118999999999,
'min_period': 4.688,
'read0_power0': [16.28732],
'read1_power0': [15.75155],
'slew_hl0': [0.6722473],
'slew_lh0': [0.3386347],
'write0_power0': [18.545450000000002],
'write1_power0': [16.81084]}
'read0_power': [16.28732],
'read1_power': [15.75155],
'slew_hl': [0.6722473],
'slew_lh': [0.3386347],
'write0_power': [18.545450000000002],
'write1_power': [16.81084]}
else:
self.assertTrue(False) # other techs fail