diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index cbf45cd8..2d8f0e57 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -10,7 +10,7 @@ from globals import OPTS class delay(): """ - Functions to measure the delay of the SRAM at a given address and + Functions to measure the delay of an SRAM at a given address and data bit. """ @@ -52,7 +52,7 @@ class delay(): # creates and opens stimulus file for writing temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) self.sf = open(temp_stim, "w") - self.sf.write("* Stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(period,load,slew)) + self.sf.write("\n* Stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(period,load,slew)) # include files in stimulus file model_list = tech.spice["fet_models"] + [self.sram_sp_file] @@ -60,26 +60,26 @@ class delay(): # add vdd/gnd statements - self.sf.write("* Global Power Supplies\n") + self.sf.write("\n* Global Power Supplies\n") stimuli.write_supply(self.sf) # instantiate the sram - self.sf.write("* Instantiation of the SRAM\n") + self.sf.write("\n* Instantiation of the SRAM\n") stimuli.inst_sram(stim_file=self.sf, abits=self.addr_size, dbits=self.word_size, sram_name=self.name) - self.sf.write("* SRAM output loads\n") + self.sf.write("\n* SRAM output loads\n") for i in range(self.word_size): self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,load)) # add access transistors for data-bus - self.sf.write("* Transmission Gates for data-bus and control signals\n") + self.sf.write("\n* Transmission Gates for data-bus and control signals\n") stimuli.inst_accesstx(stim_file=self.sf, dbits=self.word_size) # generate data and addr signals - self.sf.write("* Generation of data and address signals\n") + self.sf.write("\n* Generation of data and address signals\n") for i in range(self.word_size): if i == self.probe_data: self.gen_data(clk_times=self.cycle_times, @@ -97,20 +97,20 @@ class delay(): slew=slew) # generate control signals - self.sf.write("* Generation of control signals\n") + self.sf.write("\n* Generation of control signals\n") self.gen_csb(self.cycle_times, period, slew) self.gen_web(self.cycle_times, period, slew) self.gen_oeb(self.cycle_times, period, slew) - self.sf.write("* Generation of global clock signal\n") + self.sf.write("\n* Generation of global clock signal\n") stimuli.gen_pulse(stim_file=self.sf, sig_name="CLK", v1=self.gnd, v2=self.vdd, offset=period, period=period, - t_rise = slew, - t_fall = slew) + t_rise=slew, + t_fall=slew) self.write_measures(period) @@ -120,17 +120,23 @@ class delay(): self.sf.close() def write_measures(self,period): - # meas statement for delay and power measurements - self.sf.write("* Measure statements for delay and power\n") + """ + Write the measure statements to quantify the delay and power results. + """ + self.sf.write("\n* Measure statements for delay and power\n") + + # Output some comments to aid where cycles start and + # what is happening for comment in self.cycle_comments: self.sf.write("* {}\n".format(comment)) + # Trigger on the clk of the appropriate cycle trig_name = "clk" targ_name = "{0}".format("d[{0}]".format(self.probe_data)) trig_val = targ_val = 0.5 * self.vdd - # add measure statments for delay0 - # delay the target to measure after the negative edge + + # Delay the target to measure after the negative edge stimuli.gen_meas_delay(stim_file=self.sf, meas_name="DELAY0", trig_name=trig_name, @@ -205,11 +211,13 @@ class delay(): t_final=t_final) def find_feasible_period(self, load, slew): - """Uses an initial period and finds a feasible period before we + """ + Uses an initial period and finds a feasible period before we run the binary search algorithm to find min period. We check if the given clock period is valid and if it's not, we continue to double the period until we find a valid period to use as a - starting point. """ + starting point. + """ feasible_period = tech.spice["feasible_period"] time_out = 8 @@ -225,13 +233,20 @@ class delay(): feasible_period = 2 * feasible_period continue - debug.info(1, "Found feasible_period: {0}ns feasible_delay1/0 {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period,feasible_delay1,feasible_delay0,feasible_slew1,feasible_slew0)) + debug.info(1, "Found feasible_period: {0}ns " + + "feasible_delay1/0 {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period, + feasible_delay1, + feasible_delay0, + feasible_slew1, + feasible_slew0)) return (feasible_period, feasible_delay1, feasible_delay0) def run_simulation(self, period, load, slew): - """ This tries to simulate a period and checks if the result - works. If so, it returns True and the delays and slews.""" + """ + This tries to simulate a period and checks if the result + works. If so, it returns True and the delays and slews. + """ # Checking from not data_value to data_value self.write_stimulus(period, load, slew) @@ -243,17 +258,40 @@ class delay(): # if it failed or the read was longer than a period if type(delay0)!=float or type(delay1)!=float or type(slew1)!=float or type(slew0)!=float: - debug.info(2,"Failed simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,load,slew,delay0,delay1,slew0,slew1)) + debug.info(2,"Failed simulation: period {0} load {1} slew {2}, " + + "delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period, + load, + slew, + delay0, + delay1, + slew0, + slew1)) return (False,0,0,0,0) + # Scale delays to ns (they previously could have not been floats) delay0 *= 1e9 delay1 *= 1e9 slew0 *= 1e9 slew1 *= 1e9 if delay0>period or delay1>period or slew0>period or slew1>period: - debug.info(2,"UNsuccessful simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,load,slew,delay0,delay1,slew0,slew1)) + debug.info(2,"UNsuccessful simulation: period {0} load {1} slew {2}, " + + "delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period, + load, + slew, + delay0, + delay1, + slew0, + slew1)) return (False,0,0,0,0) else: - debug.info(2,"Successful simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,load,slew,delay0,delay1,slew0,slew1)) + debug.info(2,"Successful simulation: period {0} load {1} slew {2}, " + + "delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period, + load, + slew, + delay0, + delay1, + slew0, + slew1)) + # For debug, you sometimes want to inspect each simulation. #key=raw_input("press return to continue") # The delay is from the negative edge for our SRAM @@ -262,8 +300,10 @@ class delay(): def find_min_period(self,feasible_period, load, slew, feasible_delay1, feasible_delay0): - """Searches for the smallest period with output delays being within 5% of - long period. """ + """ + Searches for the smallest period with output delays being within 5% of + long period. + """ previous_period = ub_period = feasible_period lb_period = 0.0 @@ -291,8 +331,10 @@ class delay(): def try_period(self, period, load, slew, feasible_delay1, feasible_delay0): - """ This tries to simulate a period and checks if the result - works. If it does and the delay is within 5% still, it returns True.""" + """ + This tries to simulate a period and checks if the result + works. If it does and the delay is within 5% still, it returns True. + """ # Checking from not data_value to data_value self.write_stimulus(period,load,slew) @@ -303,14 +345,16 @@ class delay(): slew1 = ch.convert_to_float(ch.parse_output("timing", "slew1")) # if it failed or the read was longer than a period if type(delay0)!=float or type(delay1)!=float or type(slew1)!=float or type(slew0)!=float: - debug.info(2,"Invalid measures: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1)) + debug.info(2,"Invalid measures: Period {0}, " + + "delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1)) return False delay0 *= 1e9 delay1 *= 1e9 slew0 *= 1e9 slew1 *= 1e9 if delay0>period or delay1>period or slew0>period or slew1>period: - debug.info(2,"Too long delay/slew: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1)) + debug.info(2,"Too long delay/slew: Period {0}, " + + "delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1)) return False else: if not ch.relative_compare(delay1,feasible_delay1,error_tolerance=0.05): @@ -323,7 +367,8 @@ class delay(): #key=raw_input("press return to continue") - debug.info(2,"Successful period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1)) + debug.info(2,"Successful period {0}, " + + "delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1)) return True def set_probe(self,probe_address, probe_data): @@ -520,7 +565,7 @@ class delay(): return data def gen_data(self, clk_times, sig_name, period, slew): - """Generates the PWL data inputs for a simulation timing test.""" + """ Generates the PWL data inputs for a simulation timing test. """ # values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP # we are asserting the opposite value on the other side of the tx gate during # the read to be "worst case". Otherwise, it can actually assist the read. @@ -528,8 +573,9 @@ class delay(): stimuli.gen_pwl(self.sf, sig_name, clk_times, values, period, slew, 0.05) def gen_addr(self, clk_times, addr, period, slew): - """Generates the address inputs for a simulation timing test. - One cycle is different to clear the bus + """ + Generates the address inputs for a simulation timing test. + This alternates between all 1's and all 0's for the address. """ zero_values = [0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ] @@ -544,14 +590,14 @@ class delay(): def gen_csb(self, clk_times, period, slew): - """ Generates the PWL CSb signal""" + """ Generates the PWL CSb signal """ # values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP # Keep CSb asserted in NOP for measuring >1 period values = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] stimuli.gen_pwl(self.sf, "csb", clk_times, values, period, slew, 0.05) def gen_web(self, clk_times, period, slew): - """ Generates the PWL WEb signal""" + """ Generates the PWL WEb signal """ # values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP # Keep WEb deasserted in NOP for measuring >1 period values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1] @@ -564,7 +610,7 @@ class delay(): stimuli.gen_pwl(self.sf, "acc_en_inv", clk_times, values, period, slew, 0) def gen_oeb(self, clk_times, period, slew): - """ Generates the PWL WEb signal""" + """ Generates the PWL WEb signal """ # values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP # Keep OEb asserted in NOP for measuring >1 period values = [1, 1, 1, 1, 0, 0, 1, 1, 0, 0] diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 9bba2796..376b0d50 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -1,7 +1,7 @@ """ -This file generates the test structure and stimulus for an sram -simulation. There are various functions that can be be used to -generate stimulus for other simulations as well. +This file generates simple spice cards for simulation. There are +various functions that can be be used to generate stimulus for other +simulations as well. """ import tech @@ -22,7 +22,7 @@ tx_width = tech.spice["minwidth_tx"] tx_length = tech.spice["channel"] def inst_sram(stim_file, abits, dbits, sram_name): - """function to instatiate the sram subckt""" + """ Function to instatiate an SRAM subckt. """ stim_file.write("Xsram ") for i in range(dbits): stim_file.write("D[{0}] ".format(i)) @@ -32,11 +32,11 @@ def inst_sram(stim_file, abits, dbits, sram_name): stim_file.write("{0} ".format(i)) stim_file.write("{0} ".format(tech.spice["clk"])) stim_file.write("{0} {1} ".format(vdd_name, gnd_name)) - stim_file.write("{0}\n\n".format(sram_name)) + stim_file.write("{0}\n".format(sram_name)) def inst_model(stim_file, pins, model_name): - """function to instantiate a model""" + """ Function to instantiate a generic model with a set of pins """ stim_file.write("X{0} ".format(model_name)) for pin in pins: stim_file.write("{0} ".format(pin)) @@ -44,7 +44,7 @@ def inst_model(stim_file, pins, model_name): def create_inverter(stim_file, size=1, beta=2.5): - """Generates inverter for the top level signals (only for sim purposes)""" + """ Generates inverter for the top level signals (only for sim purposes) """ stim_file.write(".SUBCKT test_inv in out {0} {1}\n".format(vdd_name, gnd_name)) stim_file.write("mpinv out in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name, pmos_name, @@ -58,9 +58,10 @@ def create_inverter(stim_file, size=1, beta=2.5): def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5): - """Generates buffer for top level signals (only for sim - purposes). Size is pair for PMOS, NMOS width multiple. It includes - a beta of 3.""" + """ + Generates buffer for top level signals (only for sim + purposes). Size is pair for PMOS, NMOS width multiple. + """ stim_file.write(".SUBCKT test_{2} in out {0} {1}\n".format(vdd_name, gnd_name, @@ -85,7 +86,7 @@ def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5): def inst_buffer(stim_file, buffer_name, signal_list): - """Adds buffers to each top level signal that is in signal_list (only for sim purposes)""" + """ Adds buffers to each top level signal that is in signal_list (only for sim purposes) """ for signal in signal_list: stim_file.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal, "test"+vdd_name, @@ -94,7 +95,7 @@ def inst_buffer(stim_file, buffer_name, signal_list): def inst_inverter(stim_file, signal_list): - """Adds inv for each signal that needs its inverted version (only for sim purposes)""" + """ Adds inv for each signal that needs its inverted version (only for sim purposes) """ for signal in signal_list: stim_file.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal, "test"+vdd_name, @@ -102,7 +103,7 @@ def inst_inverter(stim_file, signal_list): def inst_accesstx(stim_file, dbits): - """Adds transmission gate for inputs to data-bus (only for sim purposes)""" + """ Adds transmission gate for inputs to data-bus (only for sim purposes) """ stim_file.write("* Tx Pin-list: Drain Gate Source Body\n") for i in range(dbits): pmos_access_string="mp{0} DATA[{0}] acc_en D[{0}] {1} {2} w={3}u l={4}u\n" @@ -119,8 +120,11 @@ def inst_accesstx(stim_file, dbits): tx_length)) def gen_pulse(stim_file, sig_name, v1=gnd_voltage, v2=vdd_voltage, offset=0, period=1, t_rise=0, t_fall=0): - """Generates a periodic signal with 50% duty cycle and slew rates. Period is measured - from 50% to 50%.""" + """ + Generates a periodic signal with 50% duty cycle and slew rates. Period is measured + from 50% to 50%. + """ + stim_file.write("* PULSE: period={0}\n".format(period)) pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n" stim_file.write(pulse_string.format(sig_name, v1, @@ -133,6 +137,11 @@ def gen_pulse(stim_file, sig_name, v1=gnd_voltage, v2=vdd_voltage, offset=0, per def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup): + """ + Generate a PWL stimulus given a signal name and data values at each period. + Automatically creates slews and ensures each data occurs a setup before the clock + edge. + """ # the initial value is not a clock time debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match.") @@ -140,6 +149,7 @@ def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup): times = np.array(clk_times) - setup*period values = np.array(data_values) * vdd_voltage half_slew = 0.5 * slew + stim_file.write("* (time, data): {}\n".format(zip(clk_times, data_values))) stim_file.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0])) for i in range(1,len(times)-1): stim_file.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew, @@ -148,13 +158,10 @@ def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup): values[i])) stim_file.write(")\n") - def gen_constant(stim_file, sig_name, v_val): - """Generates a constant signal with reference voltage and the voltage value""" + """ Generates a constant signal with reference voltage and the voltage value """ stim_file.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val)) - - def get_inverse_voltage(value): if value > 0.5*vdd_voltage: return gnd_voltage @@ -173,7 +180,7 @@ def get_inverse_value(value): def gen_meas_delay(stim_file, 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" stim_file.write(measure_string.format(meas_name, trig_name, @@ -186,7 +193,7 @@ def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_va targ_td)) def gen_meas_power(stim_file, meas_name, t_initial, t_final): - """Creates the .meas statement for the measurement of avg power""" + """ Creates the .meas statement for the measurement of avg power """ # power mea cmd is different in different spice: if OPTS.spice_name == "hspice": power_exp = "power" @@ -196,9 +203,9 @@ def gen_meas_power(stim_file, meas_name, t_initial, t_final): power_exp, t_initial, t_final)) - stim_file.write("\n") def write_control(stim_file, end_time): + """ Write the control cards to run and end the simulation """ # UIC is needed for ngspice to converge stim_file.write(".TRAN 5p {0}n UIC\n".format(end_time)) if OPTS.spice_name == "ngspice": @@ -227,22 +234,22 @@ def write_include(stim_file, models): """Writes include statements, inputs are lists of model files""" for item in list(models): if os.path.isfile(item): - stim_file.write(".include \"{0}\"\n\n".format(item)) + stim_file.write(".include \"{0}\"\n".format(item)) else: debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item)) def write_supply(stim_file): - """Writes supply voltage statements""" + """ Writes supply voltage statements """ stim_file.write("V{0} {0} 0.0 {1}\n".format(vdd_name, vdd_voltage)) stim_file.write("V{0} {0} 0.0 {1}\n".format(gnd_name, gnd_voltage)) # This is for the test power supply stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+vdd_name, vdd_voltage)) - stim_file.write("V{0} {0} 0.0 {1}\n\n".format("test"+gnd_name, gnd_voltage)) + stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+gnd_name, gnd_voltage)) def run_sim(): - """Run hspice in batch mode and output rawfile to parse.""" + """ Run hspice in batch mode and output rawfile to parse. """ temp_stim = "{0}stim.sp".format(OPTS.openram_temp) import datetime start_time = datetime.datetime.now()