mirror of https://github.com/VLSIDA/OpenRAM.git
Add read cycle to clear DOUT bus before each read measure.
This commit is contained in:
parent
8f72621f4a
commit
71606e1097
|
|
@ -89,7 +89,7 @@ class delay():
|
||||||
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
|
||||||
self.obtain_cycle_times()
|
self.create_test_cycles()
|
||||||
|
|
||||||
# creates and opens stimulus file for writing
|
# creates and opens stimulus file for writing
|
||||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||||
|
|
@ -137,7 +137,7 @@ class delay():
|
||||||
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
|
||||||
self.obtain_cycle_times()
|
self.create_test_cycles()
|
||||||
|
|
||||||
# creates and opens stimulus file for writing
|
# creates and opens stimulus file for writing
|
||||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||||
|
|
@ -303,11 +303,11 @@ class delay():
|
||||||
feasible_delay_hl = results["delay_hl"]
|
feasible_delay_hl = results["delay_hl"]
|
||||||
feasible_slew_hl = results["slew_hl"]
|
feasible_slew_hl = results["slew_hl"]
|
||||||
|
|
||||||
debug.info(1, "Found feasible_period: {0}ns feasible_delay {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period,
|
delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(feasible_delay_lh, feasible_delay_hl)
|
||||||
feasible_delay_lh,
|
slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(feasible_slew_lh, feasible_slew_hl)
|
||||||
feasible_delay_hl,
|
debug.info(1, "Found feasible_period: {0}ns {1} {2} ".format(feasible_period,
|
||||||
feasible_slew_lh,
|
delay_str,
|
||||||
feasible_slew_hl))
|
slew_str))
|
||||||
self.period = feasible_period
|
self.period = feasible_period
|
||||||
return (feasible_delay_lh, feasible_delay_hl)
|
return (feasible_delay_lh, feasible_delay_hl)
|
||||||
|
|
||||||
|
|
@ -380,39 +380,32 @@ class delay():
|
||||||
""" Check if the measurements are defined and if they are valid. """
|
""" Check if the measurements are defined and if they are valid. """
|
||||||
|
|
||||||
(delay_hl, delay_lh, slew_hl, slew_lh) = delay_tuple
|
(delay_hl, delay_lh, slew_hl, slew_lh) = delay_tuple
|
||||||
|
period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew)
|
||||||
|
|
||||||
# if it failed or the read was longer than a period
|
# if it failed or the read was longer than a period
|
||||||
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
|
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
|
||||||
debug.info(2,"Failed simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
|
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
|
||||||
self.load,
|
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh)
|
||||||
self.slew,
|
debug.info(2,"Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
|
||||||
delay_hl,
|
delays_str,
|
||||||
delay_lh,
|
slews_str))
|
||||||
slew_hl,
|
|
||||||
slew_lh))
|
|
||||||
return False
|
return False
|
||||||
# Scale delays to ns (they previously could have not been floats)
|
# Scale delays to ns (they previously could have not been floats)
|
||||||
delay_hl *= 1e9
|
delay_hl *= 1e9
|
||||||
delay_lh *= 1e9
|
delay_lh *= 1e9
|
||||||
slew_hl *= 1e9
|
slew_hl *= 1e9
|
||||||
slew_lh *= 1e9
|
slew_lh *= 1e9
|
||||||
|
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)
|
||||||
if delay_hl>self.period or delay_lh>self.period or slew_hl>self.period or slew_lh>self.period:
|
if delay_hl>self.period or delay_lh>self.period or slew_hl>self.period or slew_lh>self.period:
|
||||||
debug.info(2,"UNsuccessful simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
|
debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
|
||||||
self.load,
|
delays_str,
|
||||||
self.slew,
|
slews_str))
|
||||||
delay_hl,
|
|
||||||
delay_lh,
|
|
||||||
slew_hl,
|
|
||||||
slew_lh))
|
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
debug.info(2,"Successful simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
|
debug.info(2,"Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
|
||||||
self.load,
|
delays_str,
|
||||||
self.slew,
|
slews_str))
|
||||||
delay_hl,
|
|
||||||
delay_lh,
|
|
||||||
slew_hl,
|
|
||||||
slew_lh))
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -535,6 +528,8 @@ class delay():
|
||||||
"""
|
"""
|
||||||
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.
|
||||||
"""
|
"""
|
||||||
|
# Data structure for all the characterization values
|
||||||
|
char_data = {}
|
||||||
|
|
||||||
self.set_probe(probe_address, probe_data)
|
self.set_probe(probe_address, probe_data)
|
||||||
|
|
||||||
|
|
@ -556,21 +551,27 @@ class delay():
|
||||||
debug.check(feasible_delay_lh>0,"Negative delay may not be possible")
|
debug.check(feasible_delay_lh>0,"Negative delay may not be possible")
|
||||||
debug.check(feasible_delay_hl>0,"Negative delay may not be possible")
|
debug.check(feasible_delay_hl>0,"Negative delay may not be possible")
|
||||||
|
|
||||||
# 2) Measure the delay, slew and power for all slew/load pairs.
|
# 2) Finds the minimum period without degrading the delays by X%
|
||||||
|
self.set_load_slew(max(loads),max(slews))
|
||||||
|
min_period = self.find_min_period(feasible_delay_lh, feasible_delay_hl)
|
||||||
|
debug.check(type(min_period)==float,"Couldn't find minimum period.")
|
||||||
|
debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay_lh, feasible_delay_hl))
|
||||||
|
char_data["min_period"] = round_time(min_period)
|
||||||
|
|
||||||
# Make a list for each type of measurement to append results to
|
# Make a list for each type of measurement to append results to
|
||||||
char_data = {}
|
|
||||||
for m in ["delay_lh", "delay_hl", "slew_lh", "slew_hl", "read0_power",
|
for m in ["delay_lh", "delay_hl", "slew_lh", "slew_hl", "read0_power",
|
||||||
"read1_power", "write0_power", "write1_power", "leakage_power"]:
|
"read1_power", "write0_power", "write1_power", "leakage_power"]:
|
||||||
char_data[m]=[]
|
char_data[m]=[]
|
||||||
|
|
||||||
# 2a) Find the leakage power of the trimmmed and UNtrimmed arrays.
|
# 3) Find the leakage power of the trimmmed and UNtrimmed arrays.
|
||||||
(full_array_leakage, trim_array_leakage)=self.run_power_simulation()
|
(full_array_leakage, trim_array_leakage)=self.run_power_simulation()
|
||||||
char_data["leakage_power"]=full_array_leakage
|
char_data["leakage_power"]=full_array_leakage
|
||||||
|
|
||||||
|
# 4) At the minimum period, measure the delay, slew and power for all slew/load pairs.
|
||||||
for slew in slews:
|
for slew in slews:
|
||||||
for load in loads:
|
for load in loads:
|
||||||
self.set_load_slew(load,slew)
|
self.set_load_slew(load,slew)
|
||||||
# 2c) Find the delay, dynamic power, and leakage power of the trimmed array.
|
# Find the delay, dynamic power, and leakage power of the trimmed array.
|
||||||
(success, delay_results) = self.run_delay_simulation()
|
(success, delay_results) = self.run_delay_simulation()
|
||||||
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load))
|
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load))
|
||||||
for k,v in delay_results.items():
|
for k,v in delay_results.items():
|
||||||
|
|
@ -582,31 +583,9 @@ class delay():
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 3) Finds the minimum period without degrading the delays by X%
|
|
||||||
self.set_load_slew(max(loads),max(slews))
|
|
||||||
min_period = self.find_min_period(feasible_delay_lh, feasible_delay_hl)
|
|
||||||
debug.check(type(min_period)==float,"Couldn't find minimum period.")
|
|
||||||
debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay_lh, feasible_delay_hl))
|
|
||||||
|
|
||||||
# 4) Pack up the final measurements
|
|
||||||
char_data["min_period"] = round_time(min_period)
|
|
||||||
|
|
||||||
return char_data
|
return char_data
|
||||||
|
|
||||||
|
|
||||||
def add_noop(self, comment, address, data):
|
|
||||||
""" Add the control values for a read cycle. """
|
|
||||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0,
|
|
||||||
self.t_current,
|
|
||||||
comment))
|
|
||||||
self.cycle_times.append(self.t_current)
|
|
||||||
self.t_current += self.period
|
|
||||||
self.web_values.append(1)
|
|
||||||
self.oeb_values.append(1)
|
|
||||||
self.csb_values.append(1)
|
|
||||||
|
|
||||||
self.add_data(data)
|
|
||||||
self.add_address(address)
|
|
||||||
|
|
||||||
def add_data(self, data):
|
def add_data(self, data):
|
||||||
""" Add the array of data values """
|
""" Add the array of data values """
|
||||||
|
|
@ -633,13 +612,27 @@ class delay():
|
||||||
else:
|
else:
|
||||||
debug.error("Non-binary address string",1)
|
debug.error("Non-binary address string",1)
|
||||||
index += 1
|
index += 1
|
||||||
|
|
||||||
|
def add_noop(self, comment, address, data):
|
||||||
|
""" Add the control values for a read cycle. """
|
||||||
|
self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_times),
|
||||||
|
self.t_current,
|
||||||
|
comment))
|
||||||
|
self.cycle_times.append(self.t_current)
|
||||||
|
self.t_current += self.period
|
||||||
|
self.web_values.append(1)
|
||||||
|
self.oeb_values.append(1)
|
||||||
|
self.csb_values.append(1)
|
||||||
|
|
||||||
|
self.add_data(data)
|
||||||
|
self.add_address(address)
|
||||||
|
|
||||||
|
|
||||||
def add_read(self, comment, address, data):
|
def add_read(self, comment, address, data):
|
||||||
""" Add the control values for a read cycle. """
|
""" Add the control values for a read cycle. """
|
||||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0,
|
self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
||||||
self.t_current,
|
self.t_current,
|
||||||
comment))
|
comment))
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
|
|
@ -654,9 +647,9 @@ class delay():
|
||||||
|
|
||||||
def add_write(self, comment, address, data):
|
def add_write(self, comment, address, data):
|
||||||
""" Add the control values for a read cycle. """
|
""" Add the control values for a read cycle. """
|
||||||
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0,
|
self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
||||||
self.t_current,
|
self.t_current,
|
||||||
comment))
|
comment))
|
||||||
self.cycle_times.append(self.t_current)
|
self.cycle_times.append(self.t_current)
|
||||||
self.t_current += self.period
|
self.t_current += self.period
|
||||||
|
|
||||||
|
|
@ -667,20 +660,24 @@ class delay():
|
||||||
self.add_data(data)
|
self.add_data(data)
|
||||||
self.add_address(address)
|
self.add_address(address)
|
||||||
|
|
||||||
def obtain_cycle_times(self):
|
def create_test_cycles(self):
|
||||||
"""Returns a list of key time-points [ns] of the waveform (each rising edge)
|
"""Returns a list of key time-points [ns] of the waveform (each rising edge)
|
||||||
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."""
|
||||||
|
|
||||||
|
# Start at time 0
|
||||||
self.t_current = 0
|
self.t_current = 0
|
||||||
|
|
||||||
|
# Cycle times (positive edge) with comment
|
||||||
self.cycle_comments = []
|
self.cycle_comments = []
|
||||||
self.cycle_times = []
|
self.cycle_times = []
|
||||||
|
|
||||||
|
# Control logic signals each cycle
|
||||||
self.web_values = []
|
self.web_values = []
|
||||||
self.oeb_values = []
|
self.oeb_values = []
|
||||||
self.csb_values = []
|
self.csb_values = []
|
||||||
|
|
||||||
|
# Address and data values for each address/data bit
|
||||||
self.data_values=[]
|
self.data_values=[]
|
||||||
for i in range(self.word_size):
|
for i in range(self.word_size):
|
||||||
self.data_values.append([])
|
self.data_values.append([])
|
||||||
|
|
@ -688,6 +685,7 @@ class delay():
|
||||||
for i in range(self.addr_size):
|
for i in range(self.addr_size):
|
||||||
self.addr_values.append([])
|
self.addr_values.append([])
|
||||||
|
|
||||||
|
# Create the inverse address for a scratch address
|
||||||
inverse_address = ""
|
inverse_address = ""
|
||||||
for c in self.probe_address:
|
for c in self.probe_address:
|
||||||
if c=="0":
|
if c=="0":
|
||||||
|
|
@ -697,53 +695,49 @@ class delay():
|
||||||
else:
|
else:
|
||||||
debug.error("Non-binary address string",1)
|
debug.error("Non-binary address string",1)
|
||||||
|
|
||||||
|
# For now, ignore data patterns and write ones or zeros
|
||||||
data_ones = "1"*self.word_size
|
data_ones = "1"*self.word_size
|
||||||
data_zeros = "0"*self.word_size
|
data_zeros = "0"*self.word_size
|
||||||
|
|
||||||
# idle cycle, no operation
|
self.add_noop("Idle cycle (no positive clock edge)",
|
||||||
msg = "Idle cycle (no clock)"
|
inverse_address, data_zeros)
|
||||||
self.add_noop(msg, inverse_address, data_zeros)
|
|
||||||
|
|
||||||
# One period
|
self.add_write("W data 1 address 0..00",
|
||||||
msg = "W data 1 address 11..11 to initialize cell"
|
inverse_address,data_ones)
|
||||||
self.add_write(msg,self.probe_address,data_ones)
|
|
||||||
|
|
||||||
# One period
|
self.add_write("W data 0 address 11..11 to write value",
|
||||||
msg = "W data 0 address 11..11 (to ensure a write of value works)"
|
self.probe_address,data_zeros)
|
||||||
self.add_write(msg,self.probe_address,data_zeros)
|
self.write0_cycle=len(self.cycle_times)-1 # Remember for power measure
|
||||||
self.write0_cycle=len(self.cycle_times)-1
|
|
||||||
|
|
||||||
# One period
|
# This also ensures we will have a H->L transition on the next read
|
||||||
msg = "W data 1 address 00..00 (to clear bus caps)"
|
self.add_read("R data 1 address 00..00 to set DOUT caps",
|
||||||
self.add_write(msg,inverse_address,data_ones)
|
inverse_address,data_zeros)
|
||||||
|
|
||||||
# One period
|
self.add_read("R data 0 address 11..11 to check W0 worked",
|
||||||
msg = "R data 0 address 11..11 to check W0 worked"
|
self.probe_address,data_zeros)
|
||||||
self.add_read(msg,self.probe_address,data_zeros)
|
self.read0_cycle=len(self.cycle_times)-1 # Remember for power measure
|
||||||
self.read0_cycle=len(self.cycle_times)-1
|
|
||||||
|
|
||||||
# One period
|
|
||||||
msg = "Idle cycle (Read addr 00..00)"
|
|
||||||
self.add_noop(msg,inverse_address,data_zeros)
|
|
||||||
self.idle_cycle=len(self.cycle_times)-1
|
|
||||||
|
|
||||||
# One period
|
|
||||||
msg = "W data 1 address 11..11 (to ensure a write of value worked)"
|
|
||||||
self.add_write(msg,self.probe_address,data_ones)
|
|
||||||
self.write1_cycle=len(self.cycle_times)-1
|
|
||||||
|
|
||||||
# One period
|
|
||||||
msg = "W data 0 address 00..00 (to clear bus caps)"
|
|
||||||
self.add_write(msg,inverse_address,data_zeros)
|
|
||||||
|
|
||||||
# One period
|
self.add_noop("Idle cycle (if read takes >1 cycle)",
|
||||||
msg = "R data 1 address 11..11 to check W1 worked"
|
inverse_address,data_zeros)
|
||||||
self.add_read(msg,self.probe_address,data_zeros)
|
self.idle_cycle=len(self.cycle_times)-1 # Remember for power measure
|
||||||
self.read1_cycle=len(self.cycle_times)-1
|
|
||||||
|
|
||||||
# One period
|
self.add_write("W data 1 address 11..11 to write value",
|
||||||
msg = "Idle cycle (Read addr 11..11)"
|
self.probe_address,data_ones)
|
||||||
self.add_noop(msg,self.probe_address,data_zeros)
|
self.write1_cycle=len(self.cycle_times)-1 # Remember for power measure
|
||||||
|
|
||||||
|
self.add_write("W data 0 address 00..00 to clear DIN caps",
|
||||||
|
inverse_address,data_zeros)
|
||||||
|
|
||||||
|
# This also ensures we will have a L->H transition on the next read
|
||||||
|
self.add_read("R data 0 address 00..00 to clear DOUT caps",
|
||||||
|
inverse_address,data_zeros)
|
||||||
|
|
||||||
|
self.add_read("R data 1 address 11..11 to check W1 worked",
|
||||||
|
self.probe_address,data_zeros)
|
||||||
|
self.read1_cycle=len(self.cycle_times)-1 # Remember for power measure
|
||||||
|
|
||||||
|
self.add_noop("Idle cycle (if read takes >1 cycle))",
|
||||||
|
self.probe_address,data_zeros)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class timing_sram_test(openram_test):
|
||||||
|
|
||||||
probe_address = "1" * s.s.addr_size
|
probe_address = "1" * s.s.addr_size
|
||||||
probe_data = s.s.word_size - 1
|
probe_data = s.s.word_size - 1
|
||||||
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
|
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
|
||||||
|
|
||||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||||
d = delay(s.s, tempspice, corner)
|
d = delay(s.s, tempspice, corner)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ class timing_sram_test(openram_test):
|
||||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
OPTS.spice_name="ngspice"
|
OPTS.spice_name="ngspice"
|
||||||
OPTS.analytical_delay = False
|
OPTS.analytical_delay = False
|
||||||
|
OPTS.trim_netlist = False
|
||||||
|
|
||||||
# This is a hack to reload the characterizer __init__ with the spice version
|
# This is a hack to reload the characterizer __init__ with the spice version
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
|
|
@ -39,7 +40,7 @@ class timing_sram_test(openram_test):
|
||||||
|
|
||||||
probe_address = "1" * s.s.addr_size
|
probe_address = "1" * s.s.addr_size
|
||||||
probe_data = s.s.word_size - 1
|
probe_data = s.s.word_size - 1
|
||||||
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
|
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
|
||||||
|
|
||||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||||
d = delay(s.s, tempspice, corner)
|
d = delay(s.s, tempspice, corner)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue