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")) 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 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 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_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 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