diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index 0d9fbe83..3f3b8b60 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -31,7 +31,7 @@ if not OPTS.analytical_delay: if OPTS.spice_exe=="" or OPTS.spice_exe==None: debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_name), 1) 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 if OPTS.spice_name == "ngspice": diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index 8f33c279..b25093a0 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -11,21 +11,26 @@ import debug 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. """ - 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): """Parses a hspice output.lis file for a key value""" + + lower_key = key.lower() + if OPTS.spice_name == "xa" : # customsim has a different output file name full_filename="{0}xa.meas".format(OPTS.openram_temp) elif OPTS.spice_name == "spectre": 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: # 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: 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.archive() - contents = f.read() + contents = f.read().lower() 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*[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: - 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)) else: 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 # 3 digits = 1ps # 4 digits = 0.1ps # 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 # 3 digits = 1mv # 4 digits = 0.1mv # 5 digits = 0.01mv # 6 digits = 1uv # etc - return round(voltage,voltage_precision) + return round(voltage, voltage_precision) + def convert_to_float(number): """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 'p': lambda x: x * 0.000000000001, # pico '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 not type(float_value)==float: @@ -92,9 +100,10 @@ def convert_to_float(number): return float_value + def check_dict_values_is_float(dict): """Checks if all the values are floats. Useful for checking failed Spice measurements.""" for key, value in dict.items(): - if type(value)!=float: - return False + if type(value)!=float: + return False return True diff --git a/compiler/characterizer/setup_hold.py b/compiler/characterizer/setup_hold.py index 3cc4daa3..72e973d5 100644 --- a/compiler/characterizer/setup_hold.py +++ b/compiler/characterizer/setup_hold.py @@ -191,7 +191,6 @@ class setup_hold(): setuphold_time, feasible_bound, 2 * self.period)) - # raw_input("Press Enter to continue...") while True: target_time = (feasible_bound + infeasible_bound) / 2 diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index d60cab85..e0a37e74 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -146,7 +146,7 @@ class stimuli(): edge. The first clk_time should be 0 and is the initial time that corresponds 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}" debug.check(len(clk_times)==len(data_values), 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): """ 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" - self.sf.write(measure_string.format(meas_name, + self.sf.write(measure_string.format(meas_name.lower(), trig_name, trig_val, 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): """ 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" - self.sf.write(measure_string.format(meas_name, + self.sf.write(measure_string.format(meas_name.lower(), targ_name, trig_name, trig_val, @@ -204,7 +204,7 @@ class stimuli(): def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at): """ Creates the .meas statement for voltage at time""" 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, time_at)) @@ -215,13 +215,13 @@ class stimuli(): power_exp = "power" else: 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, 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) def write_control(self, end_time, runlvl=4): @@ -238,8 +238,8 @@ class stimuli(): reltol = 0.001 # 0.1% 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": + self.sf.write(".TEMP {}\n".format(self.temperature)) # UIC is needed for ngspice to converge self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time)) # ngspice sometimes has convergence problems if not using gear method @@ -248,6 +248,7 @@ class stimuli(): # unless you figure out what these are. self.sf.write(".OPTIONS POST=1 RELTOL={0} PROBE method=gear ACCT\n".format(reltol)) elif OPTS.spice_name == "spectre": + self.sf.write(".TEMP {}\n".format(self.temperature)) self.sf.write("simulator lang=spectre\n") if OPTS.use_pex: nestlvl = 1 @@ -255,8 +256,7 @@ class stimuli(): else: nestlvl = 10 spectre_save = "lvlpub" - self.sf.write('saveOptions options save={} nestlvl={} pwr=total \n'.format( - spectre_save, nestlvl)) + self.sf.write('saveOptions options save={} nestlvl={} pwr=total \n'.format(spectre_save, nestlvl)) 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 " " 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 ' ' annotate=status maxiters=5 \n'.format("5p", end_time)) 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(".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 HIER_DELIM=1 \n") + self.sf.write(".OPTIONS PSF=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 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.spice_name in ["hspice", "xa"]: self.sf.write(".probe V(*)\n") - else: + elif OPTS.spice_name != "Xyce": self.sf.write(".plot V(*)\n") else: 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. 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): """ Run hspice in batch mode and output rawfile to parse. """