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.""" # Checking from not data_value to data_value self.write_stimulus(period, load, slew) stimuli.run_sim() delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0")) delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1")) slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0")) 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: return (False, 0, 0, 0, 0) delay0 *= 1e9 delay1 *= 1e9 slew0 *= 1e9 slew1 *= 1e9 if delay0 > period or delay1 > period or slew0 > period or slew1 > period: 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)) #key=raw_input("press return to continue") # The delay is from the negative edge for our SRAM return (True, delay1, slew1, delay0, slew0)
def analyze(self, probe_address, probe_data, slews, loads): """main function to calculate the min period for a low_to_high transistion and a high_to_low transistion returns a dictionary that contains all both the min period and associated delays Dictionary Keys: min_period1, delay1, min_period0, delay0 """ self.set_probe(probe_address, probe_data) (feasible_period, feasible_delay1, feasible_delay0) = self.find_feasible_period(max(loads), max(slews)) debug.check(feasible_delay1 > 0, "Negative delay may not be possible") debug.check(feasible_delay0 > 0, "Negative delay may not be possible") # The power variables are just scalars. These use the final feasible period simulation # which should have worked. read0_power = ch.convert_to_float( ch.parse_output("timing", "read0_power")) write0_power = ch.convert_to_float( ch.parse_output("timing", "write0_power")) read1_power = ch.convert_to_float( ch.parse_output("timing", "read1_power")) write1_power = ch.convert_to_float( ch.parse_output("timing", "write1_power")) LH_delay = [] HL_delay = [] LH_slew = [] HL_slew = [] for slew in slews: for load in loads: (success, delay1, slew1, delay0, slew0) = self.run_simulation(feasible_period, load, slew) debug.check(success, "Couldn't run a simulation properly.\n") LH_delay.append(delay1) HL_delay.append(delay0) LH_slew.append(slew1) HL_slew.append(slew0) # finds the minimum period without degrading the delays by X% min_period = self.find_min_period(feasible_period, max(loads), max(slews), feasible_delay1, feasible_delay0) debug.check(type(min_period) == float, "Couldn't find minimum period.") debug.info( 1, "Min Period: {0}n with a delay of {1}".format( min_period, feasible_delay1)) data = { "min_period": ch.round_time(min_period), "delay1": LH_delay, "delay0": HL_delay, "slew1": LH_slew, "slew0": HL_slew, "read0_power": read0_power * 1e3, "read1_power": read1_power * 1e3, "write0_power": write0_power * 1e3, "write1_power": write1_power * 1e3 } return data
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.""" # Checking from not data_value to data_value self.write_stimulus(period,load,slew) stimuli.run_sim() delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0")) delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1")) if type(delay0)==float: delay0 *= 1e9 if type(delay1)==float: delay1 *= 1e9 debug.info(2,"Period {0}, delay0={1}ns, delay1={2}ns".format(period,delay0, delay1)) # if it failed or the read was longer than a period if type(delay0)!=float or type(delay1)!=float: return False else: if ch.relative_compare(delay1*1e9,feasible_delay1,error_tolerance=0.05): return False elif ch.relative_compare(delay0*1e9,feasible_delay0,error_tolerance=0.05): return False #key=raw_input("press return to continue") return True
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.""" # Checking from not data_value to data_value self.write_stimulus(period,load,slew) stimuli.run_sim() delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0")) delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1")) slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0")) 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)) 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)) return False else: if not ch.relative_compare(delay1,feasible_delay1,error_tolerance=0.05): debug.info(2,"Delay too big {0} vs {1}".format(delay1,feasible_delay1)) return False elif not ch.relative_compare(delay0,feasible_delay0,error_tolerance=0.05): debug.info(2,"Delay too big {0} vs {1}".format(delay0,feasible_delay0)) return False #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)) return True
def analyze(self,probe_address, probe_data): """main function to calculate the min period for a low_to_high transistion and a high_to_low transistion returns a dictionary that contains all both the min period and associated delays Dictionary Keys: min_period1, delay1, min_period0, delay0 """ self.set_probe(probe_address, probe_data) (min_period1, delay1) = self.find_min_period(tech.spice["supply_voltage"]) if (min_period1 == None) or (delay1 == None): return None debug.info(1, "Min Period for low_to_high transistion: {0}n with a delay of {1}".format(min_period1, delay1)) (min_period0, delay0) = self.find_min_period(tech.spice["gnd_voltage"]) if (min_period0 == None) or (delay0 == None): return None debug.info(1, "Min Period for high_to_low transistion: {0}n with a delay of {1}".format(min_period0, delay0)) read_power=ch.convert_to_float(ch.parse_output("timing", "power_read")) write_power=ch.convert_to_float(ch.parse_output("timing", "power_write")) data = {"min_period1": min_period1, # period in ns "delay1": delay1, # delay in s "min_period0": min_period0, "delay0": delay0, "read_power": read_power, "write_power": write_power } return data
def try_period(self, feasible_delay_lh, feasible_delay_hl): """ 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_delay_stimulus() self.stim.run_sim() delay_hl = ch.parse_output("timing", "delay_hl") delay_lh = ch.parse_output("timing", "delay_lh") slew_hl = ch.parse_output("timing", "slew_hl") slew_lh = ch.parse_output("timing", "slew_lh") # 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: debug.info( 2, "Invalid measures: Period {0}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns" .format(self.period, delay_hl, delay_lh, slew_hl, slew_lh)) return False delay_hl *= 1e9 delay_lh *= 1e9 slew_hl *= 1e9 slew_lh *= 1e9 if delay_hl > self.period or delay_lh > self.period or slew_hl > self.period or slew_lh > self.period: debug.info( 2, "Too long delay/slew: Period {0}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns" .format(self.period, delay_hl, delay_lh, slew_hl, slew_lh)) return False else: if not ch.relative_compare( delay_lh, feasible_delay_lh, error_tolerance=0.05): debug.info( 2, "Delay too big {0} vs {1}".format(delay_lh, feasible_delay_lh)) return False elif not ch.relative_compare( delay_hl, feasible_delay_hl, error_tolerance=0.05): debug.info( 2, "Delay too big {0} vs {1}".format(delay_hl, feasible_delay_hl)) return False #key=raw_input("press return to continue") debug.info( 2, "Successful period {0}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns" .format(self.period, delay_hl, delay_lh, slew_hl, slew_lh)) return True
def run_power_simulation(self): """ This simulates a disabled SRAM to get the leakage power when it is off. """ self.write_power_stimulus(trim=False) self.stim.run_sim() leakage_power = ch.parse_output("timing", "leakage_power") debug.check(leakage_power != "Failed", "Could not measure leakage power.") self.write_power_stimulus(trim=True) self.stim.run_sim() trim_leakage_power = ch.parse_output("timing", "leakage_power") debug.check(trim_leakage_power != "Failed", "Could not measure leakage power.") # For debug, you sometimes want to inspect each simulation. #key=raw_input("press return to continue") return (leakage_power * 1e3, trim_leakage_power * 1e3)
def run_delay_simulation(self): """ This tries to simulate a period and checks if the result works. If so, it returns True and the delays, slews, and powers. It works on the trimmed netlist by default, so powers do not include leakage of all cells. """ # Checking from not data_value to data_value self.write_delay_stimulus() self.stim.run_sim() delay_hl = ch.parse_output("timing", "delay_hl") delay_lh = ch.parse_output("timing", "delay_lh") slew_hl = ch.parse_output("timing", "slew_hl") slew_lh = ch.parse_output("timing", "slew_lh") delays = (delay_hl, delay_lh, slew_hl, slew_lh) read0_power = ch.parse_output("timing", "read0_power") write0_power = ch.parse_output("timing", "write0_power") read1_power = ch.parse_output("timing", "read1_power") write1_power = ch.parse_output("timing", "write1_power") if not self.check_valid_delays(delays): return (False, {}) # For debug, you sometimes want to inspect each simulation. #key=raw_input("press return to continue") # Scale results to ns and mw, respectively result = { "delay_hl": delay_hl * 1e9, "delay_lh": delay_lh * 1e9, "slew_hl": slew_hl * 1e9, "slew_lh": slew_lh * 1e9, "read0_power": read0_power * 1e3, "read1_power": read1_power * 1e3, "write0_power": write0_power * 1e3, "write1_power": write1_power * 1e3 } # The delay is from the negative edge for our SRAM return (True, result)
def try_period(self, feasible_period, target_period, data_value): """ This tries to simulate a period and checks if the result works. If so, it returns True. If not, it it doubles the period and returns False.""" # Checking from not data_value to data_value self.write_stimulus(feasible_period, target_period, data_value) stimuli.run_sim() delay_value = ch.convert_to_float(ch.parse_output("timing", "delay")) # if it failed or the read was longer than a period if type(delay_value)!=float or delay_value*1e9>target_period: debug.info(2,"Infeasible period " + str(target_period) + " delay " + str(delay_value*1e9) + "ns") return (False, "NA") else: debug.info(2,"Feasible period " + str(feasible_period) \ + ", target period " + str(target_period) \ + ", read/write of " + str(data_value) \ + ", delay=" + str(delay_value*1e9) + "ns") #key=raw_input("press return to continue") return (True, delay_value*1e9)
def bidir_search(self, correct_value, mode): """ This will perform a bidirectional search for either setup or hold times. It starts with the feasible priod and looks a half period beyond or before it depending on whether we are doing setup or hold. """ # NOTE: The feasible bound is always feasible. This is why they are different for setup and hold. # The clock will always be offset by 2*period from the start, so we want to look before and after # 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 else: 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) stimuli.run_sim() ideal_clk_to_q = ch.convert_to_float(ch.parse_output("timing", "clk2q_delay")) setuphold_time = ch.convert_to_float(ch.parse_output("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) if mode == "SETUP": # SETUP is clk-din, not din-clk setuphold_time *= -1e9 else: 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)) #raw_input("Press Enter to continue...") while True: 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)) stimuli.run_sim() clk_to_q = ch.convert_to_float(ch.parse_output("timing", "clk2q_delay")) setuphold_time = ch.convert_to_float(ch.parse_output("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 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)) 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)) infeasible_bound = target_time #raw_input("Press Enter to continue...") if ch.relative_compare(feasible_bound, infeasible_bound, error_tolerance=0.001): 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)) return passing_setuphold_time
def run_sim(self, load, slew): """Run hsim & VCS in batch mode and output rawfile to parse.""" self.dut_generator(self.addr_bit, self.data_bit, load, self.name, self.w_per_row, self.num_rows) self.spice_deck(slew, load) self.verilog_testbench(self.addr_bit, self.data_bit) self.source_generator(self.addr_bit, self.data_bit, tech.spice["inv_delay"], slew) self.cosim_config() self.make.write("\n") self.make.write("all:\n") self.make.write( "\tvcs -full64 +vpi -ad_hsim=cosim.cfg -load libvcshsim.so:cs_vpi_startup +cli+3 test.v\n" ) self.make.write("\t./simv +nsda+cosim.cfg 2>&1 | tee -i simv.log\n") self.make.write("clean:\n") self.make.write("\trm -rf ucli.key\n") self.make.write("\trm -rf simv.log\n") self.make.write("\trm -rf simv.daidir\n") self.make.write("\trm -rf simv\n") self.make.write("\trm -rf nsda_cosim.sp\n") self.make.close() for myfile in [ "cosim.cfg", "test.sp", "test.v", "source.v", "dut.sp", "Makefile" ]: filename = "{0}{1}".format(OPTS.AMC_temp, myfile) while not path.exists(filename): time.sleep(1) else: os.chmod(filename, 0o777) os.chdir(OPTS.AMC_temp) spice_stdout = open("{0}spice_stdout.log".format(OPTS.AMC_temp), 'w') spice_stderr = open("{0}spice_stderr.log".format(OPTS.AMC_temp), 'w') retcode = subprocess.call("make", shell=True, stdout=spice_stdout, stderr=spice_stderr) spice_stdout.close() spice_stderr.close() if (retcode > 1): debug.error("Spice simulation error: " + cmd, -1) filename = "{0}{1}".format(OPTS.AMC_temp, "hsim.mt") while not path.exists(filename): time.sleep(1) #Parse the hsim.mt file to repoert delay and power values. write_delay = charutils.parse_output("hsim", "write_delay") read_delay = charutils.parse_output("hsim", "read_delay") read_write_delay = charutils.parse_output("hsim", "read_write_delay") slew_hl = charutils.parse_output("hsim", "slew_hl") slew_lh = charutils.parse_output("hsim", "slew_lh") leakage_power = charutils.parse_output("hsim", "leakage_power") write_power = charutils.parse_output("hsim", "write_power") read_power = charutils.parse_output("hsim", "read_power") read_write_power = charutils.parse_output("hsim", "read_write_power") self.result = { "write_delay_lh": write_delay * (10**9), "write_delay_hl": write_delay * (10**9), "read_delay_lh": read_delay * (10**9), "read_delay_hl": read_delay * (10**9), "read_write_delay_lh": read_write_delay * (10**9), "read_write_delay_hl": read_write_delay * (10**9), "slew_hl": slew_hl * (10**9), "slew_lh": slew_lh * 1e9, "leakage_power": leakage_power * (10**3), "read_power": read_power * (10**3), "write_power": write_power * (10**3), "read_write_power": read_write_power * (10**3) } return self.result
def bidir_search(self, correct_value, noise_margin, measure_name, mode): """ This will perform a bidirectional search for either setup or hold times. It starts with the feasible priod and looks a half period beyond or before it depending on whether we are doing setup or hold. """ period = tech.spice["feasible_period"] # The clock will start being offset by a period, so we want to look before and after # theis time. if mode == "HOLD": target_time = 1.5 * period lower_bound = 0.5 * period upper_bound = 1.5 * period else: target_time = 0.5 * period lower_bound = 0.5 * period upper_bound = 1.5 * period previous_time = target_time # Initial Check if reference setup time passes for correct_value self.write_stimulus(mode=mode, target_time=target_time, correct_value=correct_value, period=period, noise_margin=noise_margin) stimuli.run_sim() output_value = ch.convert_to_float( ch.parse_output("timing", measure_name)) debug.info( 3, "Correct: {0} Output: {1} NM: {2}".format(correct_value, output_value, noise_margin)) if mode == "HOLD": setuphold_time = target_time - period else: setuphold_time = period - target_time debug.info( 3, "Target time: {0} Low: {1} Up: {2} Measured: {3}".format( target_time, lower_bound, upper_bound, setuphold_time)) if not self.pass_fail_test(output_value, correct_value, noise_margin): debug.error("Initial period/target hold time fails for data value", 2) # We already found it feasible, so advance one step first thing. if mode == "HOLD": target_time -= 0.5 * (upper_bound - lower_bound) else: target_time += 0.5 * (upper_bound - lower_bound) while True: self.write_stimulus(mode=mode, target_time=target_time, correct_value=correct_value, period=period, noise_margin=noise_margin) if mode == "HOLD": setuphold_time = target_time - period else: setuphold_time = period - target_time debug.info( 3, "Target time: {0} Low: {1} Up: {2} Measured: {3}".format( target_time, lower_bound, upper_bound, setuphold_time)) stimuli.run_sim() output_value = ch.convert_to_float( ch.parse_output("timing", measure_name)) debug.info( 3, "Correct: {0} Output: {1} NM: {2}".format( correct_value, output_value, noise_margin)) if self.pass_fail_test(output_value, correct_value, noise_margin): debug.info(3, "PASS") if ch.relative_compare(target_time, previous_time): debug.info( 3, "CONVERGE " + str(target_time) + " " + str(previous_time)) break previous_time = target_time if mode == "HOLD": upper_bound = target_time target_time -= 0.5 * (upper_bound - lower_bound) else: lower_bound = target_time target_time += 0.5 * (upper_bound - lower_bound) else: debug.info(3, "FAIL") if mode == "HOLD": lower_bound = target_time target_time += 0.5 * (upper_bound - lower_bound) else: upper_bound = target_time target_time -= 0.5 * (upper_bound - lower_bound) #raw_input("Press Enter to continue...") # the clock starts offset by one clock period, # so we always measure our setup or hold relative to this time if mode == "HOLD": setuphold_time = target_time - period else: setuphold_time = period - target_time return setuphold_time