mirror of https://github.com/VLSIDA/OpenRAM.git
Output functional stimulus to output directory.
This commit is contained in:
parent
10542d6cc3
commit
532492d5ae
|
|
@ -306,7 +306,8 @@ class delay(simulation):
|
|||
self.create_test_cycles()
|
||||
|
||||
# creates and opens stimulus file for writing
|
||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||
self.delay_stim_sp = "delay_stim.sp"
|
||||
temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.delay_stim_sp)
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.sf.write("* Delay stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(self.period,
|
||||
self.load,
|
||||
|
|
@ -350,7 +351,8 @@ class delay(simulation):
|
|||
self.check_arguments()
|
||||
|
||||
# creates and opens stimulus file for writing
|
||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||
self.power_stim_sp = "power_stim.sp"
|
||||
temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.power_stim_sp)
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
|
||||
self.stim = stimuli(self.sf, self.corner)
|
||||
|
|
@ -616,7 +618,7 @@ class delay(simulation):
|
|||
|
||||
self.write_delay_stimulus()
|
||||
|
||||
self.stim.run_sim()
|
||||
self.stim.run_sim(self.delay_stim_sp)
|
||||
|
||||
return self.check_measurements()
|
||||
|
||||
|
|
@ -772,7 +774,7 @@ class delay(simulation):
|
|||
|
||||
debug.info(1, "Performing leakage power simulations.")
|
||||
self.write_power_stimulus(trim=False)
|
||||
self.stim.run_sim()
|
||||
self.stim.run_sim(self.power_stim_sp)
|
||||
leakage_power=parse_spice_list("timing", "leakage_power")
|
||||
debug.check(leakage_power!="Failed", "Could not measure leakage power.")
|
||||
debug.info(1, "Leakage power of full array is {0} mW".format(leakage_power * 1e3))
|
||||
|
|
|
|||
|
|
@ -21,13 +21,24 @@ class functional(simulation):
|
|||
for successful SRAM operation.
|
||||
"""
|
||||
|
||||
def __init__(self, sram, spfile, corner, cycles=15):
|
||||
def __init__(self, sram, spfile, corner=None, cycles=15, period=None, output_path=None):
|
||||
super().__init__(sram, spfile, corner)
|
||||
|
||||
# Seed the characterizer with a constant seed for unit tests
|
||||
if OPTS.is_unit_test:
|
||||
random.seed(12345)
|
||||
|
||||
if not corner:
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
|
||||
if period:
|
||||
self.period = period
|
||||
|
||||
if not output_path:
|
||||
self.output_path = OPTS.openram_temp
|
||||
else:
|
||||
self.output_path = output_path
|
||||
|
||||
if self.write_size:
|
||||
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
|
||||
else:
|
||||
|
|
@ -58,15 +69,14 @@ class functional(simulation):
|
|||
self.read_check = []
|
||||
self.read_results = []
|
||||
|
||||
def run(self, feasible_period=None):
|
||||
if feasible_period: # period defaults to tech.py feasible period otherwise.
|
||||
self.period = feasible_period
|
||||
# Generate a random sequence of reads and writes
|
||||
self.create_random_memory_sequence()
|
||||
|
||||
# Run SPICE simulation
|
||||
# Write SPICE simulation
|
||||
self.write_functional_stimulus()
|
||||
self.stim.run_sim()
|
||||
|
||||
def run(self):
|
||||
self.stim.run_sim(self.stim_sp)
|
||||
|
||||
# read dout values from SPICE simulation. If the values do not fall within the noise margins, return the error.
|
||||
(success, error) = self.read_stim_results()
|
||||
|
|
@ -330,7 +340,8 @@ class functional(simulation):
|
|||
|
||||
def write_functional_stimulus(self):
|
||||
""" Writes SPICE stimulus. """
|
||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||
self.stim_sp = "functional_stim.sp"
|
||||
temp_stim = "{0}/{1}".format(self.output_path, self.stim_sp)
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
|
||||
self.stim = stimuli(self.sf, self.corner)
|
||||
|
|
|
|||
|
|
@ -27,23 +27,22 @@ class setup_hold():
|
|||
self.model_location = OPTS.openram_tech + "sp_lib/dff.sp"
|
||||
self.period = tech.spice["feasible_period"]
|
||||
|
||||
debug.info(2,"Feasible period from technology file: {0} ".format(self.period))
|
||||
debug.info(2, "Feasible period from technology file: {0} ".format(self.period))
|
||||
|
||||
self.set_corner(corner)
|
||||
|
||||
|
||||
def set_corner(self,corner):
|
||||
def set_corner(self, corner):
|
||||
""" Set the corner values """
|
||||
self.corner = corner
|
||||
(self.process, self.vdd_voltage, self.temperature) = corner
|
||||
self.gnd_voltage = 0
|
||||
|
||||
|
||||
def write_stimulus(self, mode, target_time, correct_value):
|
||||
"""Creates a stimulus file for SRAM setup/hold time calculation"""
|
||||
|
||||
# creates and opens the stimulus file for writing
|
||||
temp_stim = OPTS.openram_temp + "stim.sp"
|
||||
self.stim_sp = "sh_stim.sp"
|
||||
temp_stim = OPTS.openram_temp + self.stim_sp
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.stim = stimuli(self.sf, self.corner)
|
||||
|
||||
|
|
@ -63,8 +62,7 @@ class setup_hold():
|
|||
self.write_measures(mode=mode,
|
||||
correct_value=correct_value)
|
||||
|
||||
|
||||
self.stim.write_control(4*self.period)
|
||||
self.stim.write_control(4 * self.period)
|
||||
|
||||
self.sf.close()
|
||||
|
||||
|
|
@ -79,7 +77,6 @@ class setup_hold():
|
|||
self.sf.write("\n* Global Power Supplies\n")
|
||||
self.stim.write_supply()
|
||||
|
||||
|
||||
def write_data(self, mode, target_time, correct_value):
|
||||
"""Create the data signals for setup/hold analysis. First period is to
|
||||
initialize it to the opposite polarity. Second period is used for
|
||||
|
|
@ -113,14 +110,12 @@ class setup_hold():
|
|||
# without using .IC on an internal node.
|
||||
# Return input to value after one period.
|
||||
# The second pulse is the characterization one at 2*period
|
||||
clk_times=[0, 0.1*self.period,self.period,2*self.period],
|
||||
clk_times=[0, 0.1 * self.period, self.period, 2 * self.period],
|
||||
data_values=[0, 1, 0, 1],
|
||||
period=2*self.period,
|
||||
period=2 * self.period,
|
||||
slew=self.constrained_input_slew,
|
||||
setup=0)
|
||||
|
||||
|
||||
|
||||
def write_measures(self, mode, correct_value):
|
||||
""" Measure statements for setup/hold with right phases. """
|
||||
|
||||
|
|
@ -139,7 +134,6 @@ class setup_hold():
|
|||
else:
|
||||
din_rise_or_fall = "RISE"
|
||||
|
||||
|
||||
self.sf.write("\n* Measure statements for pass/fail verification\n")
|
||||
trig_name = "clk"
|
||||
targ_name = "dout"
|
||||
|
|
@ -152,8 +146,8 @@ class setup_hold():
|
|||
targ_val=targ_val,
|
||||
trig_dir="RISE",
|
||||
targ_dir=dout_rise_or_fall,
|
||||
trig_td=1.9*self.period,
|
||||
targ_td=1.9*self.period)
|
||||
trig_td=1.9 * self.period,
|
||||
targ_td=1.9 * self.period)
|
||||
|
||||
targ_name = "data"
|
||||
# Start triggers right after initialize value is returned to normal
|
||||
|
|
@ -165,11 +159,8 @@ class setup_hold():
|
|||
targ_val=targ_val,
|
||||
trig_dir="RISE",
|
||||
targ_dir=din_rise_or_fall,
|
||||
trig_td=1.2*self.period,
|
||||
targ_td=1.2*self.period)
|
||||
|
||||
|
||||
|
||||
trig_td=1.2 * self.period,
|
||||
targ_td=1.2 * self.period)
|
||||
|
||||
def bidir_search(self, correct_value, mode):
|
||||
""" This will perform a bidirectional search for either setup or hold times.
|
||||
|
|
@ -182,23 +173,26 @@ class setup_hold():
|
|||
# this time. They are also unbalanced so that the average won't be right on the clock edge in the
|
||||
# first iteration.
|
||||
if mode == "SETUP":
|
||||
feasible_bound = 1.25*self.period
|
||||
infeasible_bound = 2.5*self.period
|
||||
feasible_bound = 1.25 * self.period
|
||||
infeasible_bound = 2.5 * self.period
|
||||
else:
|
||||
infeasible_bound = 1.5*self.period
|
||||
feasible_bound = 2.75*self.period
|
||||
infeasible_bound = 1.5 * self.period
|
||||
feasible_bound = 2.75 * self.period
|
||||
|
||||
# Initial check if reference feasible bound time passes for correct_value, if not, we can't start the search!
|
||||
self.write_stimulus(mode=mode,
|
||||
target_time=feasible_bound,
|
||||
correct_value=correct_value)
|
||||
self.stim.run_sim()
|
||||
self.stim.run_sim(self.stim_sp)
|
||||
ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
||||
debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time))
|
||||
|
||||
if type(ideal_clk_to_q)!=float or type(setuphold_time)!=float:
|
||||
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,ideal_clk_to_q,setuphold_time),2)
|
||||
debug.error("Initial hold time fails for data value feasible bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound,
|
||||
ideal_clk_to_q,
|
||||
setuphold_time),
|
||||
2)
|
||||
|
||||
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||
setuphold_time *= -1e9
|
||||
|
|
@ -206,57 +200,53 @@ class setup_hold():
|
|||
setuphold_time *= 1e9
|
||||
|
||||
passing_setuphold_time = setuphold_time
|
||||
debug.info(2,"Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
|
||||
setuphold_time,
|
||||
feasible_bound,
|
||||
2*self.period))
|
||||
debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode,
|
||||
setuphold_time,
|
||||
feasible_bound,
|
||||
2 * self.period))
|
||||
#raw_input("Press Enter to continue...")
|
||||
|
||||
while True:
|
||||
target_time = (feasible_bound + infeasible_bound)/2
|
||||
target_time = (feasible_bound + infeasible_bound) / 2
|
||||
self.write_stimulus(mode=mode,
|
||||
target_time=target_time,
|
||||
correct_value=correct_value)
|
||||
|
||||
debug.info(2,"{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
|
||||
correct_value,
|
||||
target_time,
|
||||
infeasible_bound,
|
||||
feasible_bound))
|
||||
|
||||
debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode,
|
||||
correct_value,
|
||||
target_time,
|
||||
infeasible_bound,
|
||||
feasible_bound))
|
||||
|
||||
self.stim.run_sim()
|
||||
clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay"))
|
||||
setuphold_time = convert_to_float(parse_spice_list("timing", "setup_hold_time"))
|
||||
if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float:
|
||||
if type(clk_to_q) == float and (clk_to_q < 1.1 * ideal_clk_to_q) and type(setuphold_time)==float:
|
||||
if mode == "SETUP": # SETUP is clk-din, not din-clk
|
||||
setuphold_time *= -1e9
|
||||
else:
|
||||
setuphold_time *= 1e9
|
||||
|
||||
debug.info(2,"PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time))
|
||||
debug.info(2, "PASS Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
|
||||
passing_setuphold_time = setuphold_time
|
||||
feasible_bound = target_time
|
||||
else:
|
||||
debug.info(2,"FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q,setuphold_time))
|
||||
debug.info(2, "FAIL Clk-to-Q: {0} Setup/Hold: {1}".format(clk_to_q, setuphold_time))
|
||||
infeasible_bound = target_time
|
||||
|
||||
#raw_input("Press Enter to continue...")
|
||||
if relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001):
|
||||
debug.info(3,"CONVERGE {0} vs {1}".format(feasible_bound,infeasible_bound))
|
||||
debug.info(3, "CONVERGE {0} vs {1}".format(feasible_bound, infeasible_bound))
|
||||
break
|
||||
|
||||
|
||||
debug.info(2,"Converged on {0} time {1}.".format(mode,passing_setuphold_time))
|
||||
debug.info(2, "Converged on {0} time {1}.".format(mode, passing_setuphold_time))
|
||||
return passing_setuphold_time
|
||||
|
||||
|
||||
def setup_LH_time(self):
|
||||
"""Calculates the setup time for low-to-high transition for a DFF
|
||||
"""
|
||||
return self.bidir_search(1, "SETUP")
|
||||
|
||||
|
||||
def setup_HL_time(self):
|
||||
"""Calculates the setup time for high-to-low transition for a DFF
|
||||
"""
|
||||
|
|
@ -272,7 +262,6 @@ class setup_hold():
|
|||
"""
|
||||
return self.bidir_search(0, "HOLD")
|
||||
|
||||
|
||||
def analyze(self, related_slews, constrained_slews):
|
||||
"""main function to calculate both setup and hold time for the
|
||||
DFF and returns a dictionary that contains 4 lists for both
|
||||
|
|
@ -301,10 +290,10 @@ class setup_hold():
|
|||
# }
|
||||
# return times
|
||||
|
||||
|
||||
for self.related_input_slew in related_slews:
|
||||
for self.constrained_input_slew in constrained_slews:
|
||||
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,self.constrained_input_slew))
|
||||
debug.info(1, "Clock slew: {0} Data slew: {1}".format(self.related_input_slew,
|
||||
self.constrained_input_slew))
|
||||
LH_setup_time = self.setup_LH_time()
|
||||
debug.info(1, " Setup Time for low_to_high transition: {0}".format(LH_setup_time))
|
||||
HL_setup_time = self.setup_HL_time()
|
||||
|
|
@ -325,7 +314,7 @@ class setup_hold():
|
|||
}
|
||||
return times
|
||||
|
||||
def analytical_setuphold(self,related_slews, constrained_slews):
|
||||
def analytical_setuphold(self, related_slews, constrained_slews):
|
||||
""" Just return the fixed setup/hold times from the technology.
|
||||
"""
|
||||
LH_setup = []
|
||||
|
|
@ -336,10 +325,10 @@ class setup_hold():
|
|||
for self.related_input_slew in related_slews:
|
||||
for self.constrained_input_slew in constrained_slews:
|
||||
# convert from ps to ns
|
||||
LH_setup.append(tech.spice["dff_setup"]/1e3)
|
||||
HL_setup.append(tech.spice["dff_setup"]/1e3)
|
||||
LH_hold.append(tech.spice["dff_hold"]/1e3)
|
||||
HL_hold.append(tech.spice["dff_hold"]/1e3)
|
||||
LH_setup.append(tech.spice["dff_setup"] / 1e3)
|
||||
HL_setup.append(tech.spice["dff_setup"] / 1e3)
|
||||
LH_hold.append(tech.spice["dff_hold"] / 1e3)
|
||||
HL_hold.append(tech.spice["dff_hold"] / 1e3)
|
||||
|
||||
times = {"setup_times_LH": LH_setup,
|
||||
"setup_times_HL": HL_setup,
|
||||
|
|
|
|||
|
|
@ -299,9 +299,9 @@ class stimuli():
|
|||
self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n")
|
||||
self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
|
||||
|
||||
def run_sim(self):
|
||||
def run_sim(self, name):
|
||||
""" Run hspice in batch mode and output rawfile to parse. """
|
||||
temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
|
||||
temp_stim = "{0}{1}".format(OPTS.openram_temp, name)
|
||||
import datetime
|
||||
start_time = datetime.datetime.now()
|
||||
debug.check(OPTS.spice_exe != "", "No spice simulator has been found.")
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
import datetime
|
||||
import os
|
||||
import debug
|
||||
import verify
|
||||
from characterizer import functional
|
||||
from globals import OPTS, print_time
|
||||
|
||||
|
||||
|
|
@ -85,12 +87,11 @@ class sram():
|
|||
gdsname = OPTS.output_path + self.s.name + ".gds"
|
||||
debug.print_raw("GDS: Writing to {0}".format(gdsname))
|
||||
self.gds_write(gdsname)
|
||||
from verify import write_drc_script
|
||||
write_drc_script(cell_name=self.s.name,
|
||||
gds_name=os.path.basename(gdsname),
|
||||
extract=True,
|
||||
final_verification=True,
|
||||
output_path=OPTS.output_path)
|
||||
verify.write_drc_script(cell_name=self.s.name,
|
||||
gds_name=os.path.basename(gdsname),
|
||||
extract=True,
|
||||
final_verification=True,
|
||||
output_path=OPTS.output_path)
|
||||
print_time("GDS", datetime.datetime.now(), start_time)
|
||||
|
||||
# Create a LEF physical model
|
||||
|
|
@ -105,6 +106,9 @@ class sram():
|
|||
spname = OPTS.output_path + self.s.name + ".sp"
|
||||
debug.print_raw("SP: Writing to {0}".format(spname))
|
||||
self.sp_write(spname)
|
||||
functional(self.s,
|
||||
os.path.basename(spname),
|
||||
output_path=OPTS.output_path)
|
||||
print_time("Spice writing", datetime.datetime.now(), start_time)
|
||||
|
||||
# Save the LVS file
|
||||
|
|
@ -113,17 +117,15 @@ class sram():
|
|||
debug.print_raw("LVS: Writing to {0}".format(lvsname))
|
||||
self.lvs_write(lvsname)
|
||||
if not OPTS.netlist_only:
|
||||
from verify import write_lvs_script
|
||||
write_lvs_script(cell_name=self.s.name,
|
||||
gds_name=os.path.basename(gdsname),
|
||||
sp_name=os.path.basename(lvsname),
|
||||
final_verification=True,
|
||||
output_path=OPTS.output_path)
|
||||
verify.write_lvs_script(cell_name=self.s.name,
|
||||
gds_name=os.path.basename(gdsname),
|
||||
sp_name=os.path.basename(lvsname),
|
||||
final_verification=True,
|
||||
output_path=OPTS.output_path)
|
||||
print_time("LVS writing", datetime.datetime.now(), start_time)
|
||||
|
||||
# Save the extracted spice file
|
||||
if OPTS.use_pex:
|
||||
import verify
|
||||
start_time = datetime.datetime.now()
|
||||
# Output the extracted design if requested
|
||||
pexname = OPTS.output_path + self.s.name + ".pex.sp"
|
||||
|
|
|
|||
|
|
@ -56,8 +56,7 @@ class psram_1bank_2mux_func_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "sram.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
f = functional(s.s, tempspice)
|
||||
(fail, error) = f.run()
|
||||
self.assertTrue(fail, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -57,8 +57,7 @@ class psram_1bank_8mux_func_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "sram.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
f = functional(s.s, tempspice)
|
||||
(fail, error) = f.run()
|
||||
self.assertTrue(fail, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -56,8 +56,7 @@ class psram_1bank_nomux_func_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "sram.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
f = functional(s.s, tempspice)
|
||||
(fail, error) = f.run()
|
||||
self.assertTrue(fail, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@ class sram_1bank_2mux_func_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "sram.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
f = functional(s.s, tempspice)
|
||||
(fail, error) = f.run()
|
||||
self.assertTrue(fail, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -48,8 +48,7 @@ class sram_1bank_2mux_func_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "sram.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
f = functional(s.s, tempspice)
|
||||
(fail, error) = f.run()
|
||||
self.assertTrue(fail, error)
|
||||
|
||||
|
|
|
|||
|
|
@ -48,8 +48,7 @@ class sram_1bank_2mux_sparecols_func_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "sram.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
f = functional(s.s, tempspice)
|
||||
(fail, error) = f.run()
|
||||
self.assertTrue(fail, error)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue