mirror of https://github.com/VLSIDA/OpenRAM.git
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:
parent
ab7d3510b5
commit
4586ed343f
|
|
@ -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. """
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue