mirror of https://github.com/VLSIDA/OpenRAM.git
Initial Xyce support.
This commit is contained in:
parent
3959cf73d1
commit
67a67111a6
|
|
@ -31,7 +31,7 @@ if not OPTS.analytical_delay:
|
||||||
if OPTS.spice_exe=="" or OPTS.spice_exe==None:
|
if OPTS.spice_exe=="" or OPTS.spice_exe==None:
|
||||||
debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name), 1)
|
debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name), 1)
|
||||||
else:
|
else:
|
||||||
(OPTS.spice_name, OPTS.spice_exe) = get_tool("spice", ["ngspice", "ngspice.exe", "hspice", "xa"])
|
(OPTS.spice_name, OPTS.spice_exe) = get_tool("spice", ["Xyce", "ngspice", "ngspice.exe", "hspice", "xa"])
|
||||||
|
|
||||||
# set the input dir for spice files if using ngspice
|
# set the input dir for spice files if using ngspice
|
||||||
if OPTS.spice_name == "ngspice":
|
if OPTS.spice_name == "ngspice":
|
||||||
|
|
|
||||||
|
|
@ -11,21 +11,26 @@ import debug
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
def relative_compare(value1,value2,error_tolerance=0.001):
|
def relative_compare(value1, value2, error_tolerance=0.001):
|
||||||
""" This is used to compare relative values for convergence. """
|
""" This is used to compare relative values for convergence. """
|
||||||
return (abs(value1 - value2) / abs(max(value1,value2)) <= error_tolerance)
|
return (abs(value1 - value2) / abs(max(value1, value2)) <= error_tolerance)
|
||||||
|
|
||||||
|
|
||||||
def parse_spice_list(filename, key):
|
def parse_spice_list(filename, key):
|
||||||
"""Parses a hspice output.lis file for a key value"""
|
"""Parses a hspice output.lis file for a key value"""
|
||||||
|
|
||||||
|
lower_key = key.lower()
|
||||||
|
|
||||||
if OPTS.spice_name == "xa" :
|
if OPTS.spice_name == "xa" :
|
||||||
# customsim has a different output file name
|
# customsim has a different output file name
|
||||||
full_filename="{0}xa.meas".format(OPTS.openram_temp)
|
full_filename="{0}xa.meas".format(OPTS.openram_temp)
|
||||||
elif OPTS.spice_name == "spectre":
|
elif OPTS.spice_name == "spectre":
|
||||||
full_filename = os.path.join(OPTS.openram_temp, "delay_stim.measure")
|
full_filename = os.path.join(OPTS.openram_temp, "delay_stim.measure")
|
||||||
|
elif OPTS.spice_name == "Xyce":
|
||||||
|
full_filename = os.path.join(OPTS.openram_temp, "spice_stdout.log")
|
||||||
else:
|
else:
|
||||||
# ngspice/hspice using a .lis file
|
# ngspice/hspice using a .lis file
|
||||||
full_filename="{0}{1}.lis".format(OPTS.openram_temp, filename)
|
full_filename = "{0}{1}.lis".format(OPTS.openram_temp, filename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f = open(full_filename, "r")
|
f = open(full_filename, "r")
|
||||||
|
|
@ -33,31 +38,34 @@ def parse_spice_list(filename, key):
|
||||||
debug.error("Unable to open spice output file: {0}".format(full_filename),1)
|
debug.error("Unable to open spice output file: {0}".format(full_filename),1)
|
||||||
debug.archive()
|
debug.archive()
|
||||||
|
|
||||||
contents = f.read()
|
contents = f.read().lower()
|
||||||
f.close()
|
f.close()
|
||||||
# val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents)
|
# val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents)
|
||||||
val = re.search(r"{0}\s*=\s*(-?\d+.?\d*[e]?[-+]?[0-9]*\S*)\s+.*".format(key), contents)
|
val = re.search(r"{0}\s*=\s*(-?\d+.?\d*[e]?[-+]?[0-9]*\S*)\s+.*".format(lower_key), contents)
|
||||||
if val != None:
|
if val != None:
|
||||||
debug.info(4, "Key = " + key + " Val = " + val.group(1))
|
debug.info(4, "Key = " + lower_key + " Val = " + val.group(1))
|
||||||
return convert_to_float(val.group(1))
|
return convert_to_float(val.group(1))
|
||||||
else:
|
else:
|
||||||
return "Failed"
|
return "Failed"
|
||||||
|
|
||||||
def round_time(time,time_precision=3):
|
|
||||||
|
def round_time(time, time_precision=3):
|
||||||
# times are in ns, so this is how many digits of precision
|
# times are in ns, so this is how many digits of precision
|
||||||
# 3 digits = 1ps
|
# 3 digits = 1ps
|
||||||
# 4 digits = 0.1ps
|
# 4 digits = 0.1ps
|
||||||
# etc.
|
# etc.
|
||||||
return round(time,time_precision)
|
return round(time, time_precision)
|
||||||
|
|
||||||
def round_voltage(voltage,voltag_precision=5):
|
|
||||||
|
def round_voltage(voltage, voltage_precision=5):
|
||||||
# voltages are in volts
|
# voltages are in volts
|
||||||
# 3 digits = 1mv
|
# 3 digits = 1mv
|
||||||
# 4 digits = 0.1mv
|
# 4 digits = 0.1mv
|
||||||
# 5 digits = 0.01mv
|
# 5 digits = 0.01mv
|
||||||
# 6 digits = 1uv
|
# 6 digits = 1uv
|
||||||
# etc
|
# etc
|
||||||
return round(voltage,voltage_precision)
|
return round(voltage, voltage_precision)
|
||||||
|
|
||||||
|
|
||||||
def convert_to_float(number):
|
def convert_to_float(number):
|
||||||
"""Converts a string into a (float) number; also converts units(m,u,n,p)"""
|
"""Converts a string into a (float) number; also converts units(m,u,n,p)"""
|
||||||
|
|
@ -84,7 +92,7 @@ def convert_to_float(number):
|
||||||
'n': lambda x: x * 0.000000001, # nano
|
'n': lambda x: x * 0.000000001, # nano
|
||||||
'p': lambda x: x * 0.000000000001, # pico
|
'p': lambda x: x * 0.000000000001, # pico
|
||||||
'f': lambda x: x * 0.000000000000001 # femto
|
'f': lambda x: x * 0.000000000000001 # femto
|
||||||
}[unit.group(2)](float(unit.group(1)))
|
}[unit.group(2)](float(unit.group(1)))
|
||||||
|
|
||||||
# if we weren't able to convert it to a float then error out
|
# if we weren't able to convert it to a float then error out
|
||||||
if not type(float_value)==float:
|
if not type(float_value)==float:
|
||||||
|
|
@ -92,9 +100,10 @@ def convert_to_float(number):
|
||||||
|
|
||||||
return float_value
|
return float_value
|
||||||
|
|
||||||
|
|
||||||
def check_dict_values_is_float(dict):
|
def check_dict_values_is_float(dict):
|
||||||
"""Checks if all the values are floats. Useful for checking failed Spice measurements."""
|
"""Checks if all the values are floats. Useful for checking failed Spice measurements."""
|
||||||
for key, value in dict.items():
|
for key, value in dict.items():
|
||||||
if type(value)!=float:
|
if type(value)!=float:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,6 @@ class setup_hold():
|
||||||
setuphold_time,
|
setuphold_time,
|
||||||
feasible_bound,
|
feasible_bound,
|
||||||
2 * self.period))
|
2 * self.period))
|
||||||
# raw_input("Press Enter to continue...")
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
target_time = (feasible_bound + infeasible_bound) / 2
|
target_time = (feasible_bound + infeasible_bound) / 2
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ class stimuli():
|
||||||
edge. The first clk_time should be 0 and is the initial time that corresponds
|
edge. The first clk_time should be 0 and is the initial time that corresponds
|
||||||
to the initial value.
|
to the initial value.
|
||||||
"""
|
"""
|
||||||
# the initial value is not a clock time
|
|
||||||
str = "Clock and data value lengths don't match. {0} clock values, {1} data values for {2}"
|
str = "Clock and data value lengths don't match. {0} clock values, {1} data values for {2}"
|
||||||
debug.check(len(clk_times)==len(data_values),
|
debug.check(len(clk_times)==len(data_values),
|
||||||
str.format(len(clk_times),
|
str.format(len(clk_times),
|
||||||
|
|
@ -181,7 +181,7 @@ class stimuli():
|
||||||
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
|
def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
|
||||||
""" Creates the .meas statement for the measurement of delay """
|
""" Creates the .meas statement for the measurement of delay """
|
||||||
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
|
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
|
||||||
self.sf.write(measure_string.format(meas_name,
|
self.sf.write(measure_string.format(meas_name.lower(),
|
||||||
trig_name,
|
trig_name,
|
||||||
trig_val,
|
trig_val,
|
||||||
trig_dir,
|
trig_dir,
|
||||||
|
|
@ -194,7 +194,7 @@ class stimuli():
|
||||||
def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td):
|
def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td):
|
||||||
""" Creates the .meas statement for the measurement of delay """
|
""" Creates the .meas statement for the measurement of delay """
|
||||||
measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n"
|
measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n"
|
||||||
self.sf.write(measure_string.format(meas_name,
|
self.sf.write(measure_string.format(meas_name.lower(),
|
||||||
targ_name,
|
targ_name,
|
||||||
trig_name,
|
trig_name,
|
||||||
trig_val,
|
trig_val,
|
||||||
|
|
@ -204,7 +204,7 @@ class stimuli():
|
||||||
def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at):
|
def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at):
|
||||||
""" Creates the .meas statement for voltage at time"""
|
""" Creates the .meas statement for voltage at time"""
|
||||||
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
|
measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n"
|
||||||
self.sf.write(measure_string.format(meas_name,
|
self.sf.write(measure_string.format(meas_name.lower(),
|
||||||
targ_name,
|
targ_name,
|
||||||
time_at))
|
time_at))
|
||||||
|
|
||||||
|
|
@ -215,13 +215,13 @@ class stimuli():
|
||||||
power_exp = "power"
|
power_exp = "power"
|
||||||
else:
|
else:
|
||||||
power_exp = "par('(-1*v(" + str(self.vdd_name) + ")*I(v" + str(self.vdd_name) + "))')"
|
power_exp = "par('(-1*v(" + str(self.vdd_name) + ")*I(v" + str(self.vdd_name) + "))')"
|
||||||
self.sf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name,
|
self.sf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name.lower(),
|
||||||
power_exp,
|
power_exp,
|
||||||
t_initial,
|
t_initial,
|
||||||
t_final))
|
t_final))
|
||||||
|
|
||||||
def gen_meas_value(self, meas_name, dout, t_initial, t_final):
|
def gen_meas_value(self, meas_name, dout, t_initial, t_final):
|
||||||
measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_initial, t_final)
|
measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name.lower(), dout, t_initial, t_final)
|
||||||
self.sf.write(measure_string)
|
self.sf.write(measure_string)
|
||||||
|
|
||||||
def write_control(self, end_time, runlvl=4):
|
def write_control(self, end_time, runlvl=4):
|
||||||
|
|
@ -238,8 +238,8 @@ class stimuli():
|
||||||
reltol = 0.001 # 0.1%
|
reltol = 0.001 # 0.1%
|
||||||
timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
|
timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
|
||||||
|
|
||||||
self.sf.write(".TEMP {}\n".format(self.temperature))
|
|
||||||
if OPTS.spice_name == "ngspice":
|
if OPTS.spice_name == "ngspice":
|
||||||
|
self.sf.write(".TEMP {}\n".format(self.temperature))
|
||||||
# UIC is needed for ngspice to converge
|
# UIC is needed for ngspice to converge
|
||||||
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
|
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
|
||||||
# ngspice sometimes has convergence problems if not using gear method
|
# ngspice sometimes has convergence problems if not using gear method
|
||||||
|
|
@ -248,6 +248,7 @@ class stimuli():
|
||||||
# unless you figure out what these are.
|
# unless you figure out what these are.
|
||||||
self.sf.write(".OPTIONS POST=1 RELTOL={0} PROBE method=gear ACCT\n".format(reltol))
|
self.sf.write(".OPTIONS POST=1 RELTOL={0} PROBE method=gear ACCT\n".format(reltol))
|
||||||
elif OPTS.spice_name == "spectre":
|
elif OPTS.spice_name == "spectre":
|
||||||
|
self.sf.write(".TEMP {}\n".format(self.temperature))
|
||||||
self.sf.write("simulator lang=spectre\n")
|
self.sf.write("simulator lang=spectre\n")
|
||||||
if OPTS.use_pex:
|
if OPTS.use_pex:
|
||||||
nestlvl = 1
|
nestlvl = 1
|
||||||
|
|
@ -255,8 +256,7 @@ class stimuli():
|
||||||
else:
|
else:
|
||||||
nestlvl = 10
|
nestlvl = 10
|
||||||
spectre_save = "lvlpub"
|
spectre_save = "lvlpub"
|
||||||
self.sf.write('saveOptions options save={} nestlvl={} pwr=total \n'.format(
|
self.sf.write('saveOptions options save={} nestlvl={} pwr=total \n'.format(spectre_save, nestlvl))
|
||||||
spectre_save, nestlvl))
|
|
||||||
self.sf.write("simulatorOptions options reltol=1e-3 vabstol=1e-6 iabstol=1e-12 temp={0} try_fast_op=no "
|
self.sf.write("simulatorOptions options reltol=1e-3 vabstol=1e-6 iabstol=1e-12 temp={0} try_fast_op=no "
|
||||||
"rforce=10m maxnotes=10 maxwarns=10 "
|
"rforce=10m maxnotes=10 maxwarns=10 "
|
||||||
" preservenode=all topcheck=fixall "
|
" preservenode=all topcheck=fixall "
|
||||||
|
|
@ -265,12 +265,18 @@ class stimuli():
|
||||||
self.sf.write('tran tran step={} stop={}n ic=node write=spectre.dc errpreset=moderate '
|
self.sf.write('tran tran step={} stop={}n ic=node write=spectre.dc errpreset=moderate '
|
||||||
' annotate=status maxiters=5 \n'.format("5p", end_time))
|
' annotate=status maxiters=5 \n'.format("5p", end_time))
|
||||||
self.sf.write("simulator lang=spice\n")
|
self.sf.write("simulator lang=spice\n")
|
||||||
else:
|
elif OPTS.spice_name in ["hspice", "xa"]:
|
||||||
|
self.sf.write(".TEMP {}\n".format(self.temperature))
|
||||||
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
|
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
|
||||||
self.sf.write(".OPTIONS POST=1 RUNLVL={0} PROBE\n".format(runlvl))
|
self.sf.write(".OPTIONS POST=1 RUNLVL={0} PROBE\n".format(runlvl))
|
||||||
if OPTS.spice_name == "hspice": # for cadence plots
|
self.sf.write(".OPTIONS PSF=1 \n")
|
||||||
self.sf.write(".OPTIONS PSF=1 \n")
|
self.sf.write(".OPTIONS HIER_DELIM=1 \n")
|
||||||
self.sf.write(".OPTIONS HIER_DELIM=1 \n")
|
elif OPTS.spice_name == "Xyce":
|
||||||
|
self.sf.write(".OPTIONS DEVICE TEMP={}\n".format(self.temperature))
|
||||||
|
self.sf.write(".OPTIONS MEASURE MEASFAIL=1\n")
|
||||||
|
self.sf.write(".TRAN {0}p {1}n\n".format(timestep, end_time))
|
||||||
|
else:
|
||||||
|
debug.error("Unkown spice simulator {}".format(OPTS.spice_name))
|
||||||
|
|
||||||
# create plots for all signals
|
# create plots for all signals
|
||||||
if not OPTS.use_pex: # Don't save all for extracted simulations
|
if not OPTS.use_pex: # Don't save all for extracted simulations
|
||||||
|
|
@ -278,7 +284,7 @@ class stimuli():
|
||||||
if OPTS.verbose_level>0:
|
if OPTS.verbose_level>0:
|
||||||
if OPTS.spice_name in ["hspice", "xa"]:
|
if OPTS.spice_name in ["hspice", "xa"]:
|
||||||
self.sf.write(".probe V(*)\n")
|
self.sf.write(".probe V(*)\n")
|
||||||
else:
|
elif OPTS.spice_name != "Xyce":
|
||||||
self.sf.write(".plot V(*)\n")
|
self.sf.write(".plot V(*)\n")
|
||||||
else:
|
else:
|
||||||
self.sf.write("*.probe V(*)\n")
|
self.sf.write("*.probe V(*)\n")
|
||||||
|
|
@ -312,7 +318,10 @@ class stimuli():
|
||||||
|
|
||||||
# Adding a commented out supply for simulators where gnd and 0 are not global grounds.
|
# Adding a commented out supply for simulators where gnd and 0 are not global grounds.
|
||||||
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("\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))
|
if OPTS.spice_name == "Xyce":
|
||||||
|
self.sf.write("V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
|
||||||
|
else:
|
||||||
|
self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0))
|
||||||
|
|
||||||
def run_sim(self, name):
|
def run_sim(self, name):
|
||||||
""" Run hspice in batch mode and output rawfile to parse. """
|
""" Run hspice in batch mode and output rawfile to parse. """
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue