From a3bec5518cbe4afd47d5ae6eed579fa88b4b0918 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 8 Oct 2018 15:45:54 -0700 Subject: [PATCH] Put worst case test under the hierarchy of a delay test. Added option for pex option to worst case test. --- compiler/characterizer/trim_spice.py | 4 + compiler/characterizer/worst_case.py | 986 +-------------------- compiler/sram.py | 1 - compiler/tests/27_worst_case_delay_test.py | 43 +- compiler/tests/sram1.gds | Bin 304042 -> 0 bytes compiler/verify/__init__.py | 1 - 6 files changed, 43 insertions(+), 992 deletions(-) delete mode 100644 compiler/tests/sram1.gds diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index 9ddbe655..a3ef902a 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -111,6 +111,7 @@ class trim_spice(): match of the line with a term so you can search for a single net connection, the instance name, anything.. """ + removed_insts = 0 #Expects keep_inst_list are regex patterns. Compile them here. compiled_patterns = [re.compile(pattern) for pattern in keep_inst_list] @@ -127,11 +128,14 @@ class trim_spice(): new_buffer.append(line) in_subckt=False elif in_subckt: + removed_insts += 1 for pattern in compiled_patterns: if pattern.search(line) != None: new_buffer.append(line) + removed_insts -= 1 break else: new_buffer.append(line) self.sp_buffer = new_buffer + debug.info(2, "Removed {} instances from {} subcircuit.".format(removed_insts, subckt_name)) diff --git a/compiler/characterizer/worst_case.py b/compiler/characterizer/worst_case.py index 351b24e4..c0058dda 100644 --- a/compiler/characterizer/worst_case.py +++ b/compiler/characterizer/worst_case.py @@ -7,8 +7,9 @@ from .trim_spice import * from .charutils import * import utils from globals import OPTS +from .delay import delay -class worst_case(): +class worst_case(delay): """Functions to test for the worst case delay in a target SRAM The current worst case determines a feasible period for the SRAM then tests @@ -17,650 +18,9 @@ class worst_case(): """ def __init__(self, sram, spfile, corner): - self.sram = sram - self.name = sram.name - self.word_size = self.sram.word_size - self.addr_size = self.sram.addr_size - self.num_cols = self.sram.num_cols - self.num_rows = self.sram.num_rows - self.num_banks = self.sram.num_banks - self.sp_file = spfile + delay.__init__(self,sram,spfile,corner) - self.total_ports = self.sram.total_ports - self.total_write = self.sram.total_write - self.total_read = self.sram.total_read - self.read_index = self.sram.read_index - self.write_index = self.sram.write_index - self.port_id = self.sram.port_id - - # These are the member variables for a simulation - self.period = 0 - self.set_load_slew(0,0) - self.set_corner(corner) - self.create_port_names() - self.create_signal_names() - - #Create global measure names. Should maybe be an input at some point. - self.create_measurement_names() - - def create_measurement_names(self): - """Create measurement names. The names themselves currently define the type of measurement""" - #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. - self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] - self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] - - def create_signal_names(self): - self.addr_name = "A" - self.din_name = "DIN" - self.dout_name = "DOUT" - - #This is TODO once multiport control has been finalized. - #self.control_name = "CSB" - - def create_port_names(self): - """Generates the port names to be used in characterization and sets default simulation target ports""" - self.write_ports = [] - self.read_ports = [] - self.total_port_num = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - - #save a member variable to avoid accessing global. readwrite ports have different control signals. - self.readwrite_port_num = OPTS.num_rw_ports - - #Generate the port names. readwrite ports are required to be added first for this to work. - for readwrite_port_num in range(OPTS.num_rw_ports): - self.read_ports.append(readwrite_port_num) - self.write_ports.append(readwrite_port_num) - #This placement is intentional. It makes indexing input data easier. See self.data_values - for write_port_num in range(OPTS.num_rw_ports, OPTS.num_rw_ports+OPTS.num_w_ports): - self.write_ports.append(write_port_num) - for read_port_num in range(OPTS.num_rw_ports+OPTS.num_w_ports, OPTS.num_rw_ports+OPTS.num_w_ports+OPTS.num_r_ports): - self.read_ports.append(read_port_num) - - #Set the default target ports for simulation. Default is all the ports. - self.targ_read_ports = self.read_ports - self.targ_write_ports = self.write_ports - - def set_corner(self,corner): - """ Set the corner values """ - self.corner = corner - (self.process, self.vdd_voltage, self.temperature) = corner - - def set_load_slew(self,load,slew): - """ Set the load and slew """ - self.load = load - self.slew = slew - - def check_arguments(self): - """Checks if arguments given for write_stimulus() meets requirements""" - try: - int(self.probe_address, 2) - except ValueError: - debug.error("Probe Address is not of binary form: {0}".format(self.probe_address),1) - - if len(self.probe_address) != self.addr_size: - debug.error("Probe Address's number of bits does not correspond to given SRAM",1) - - if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0: - debug.error("Given probe_data is not an integer to specify a data bit",1) - - #Adding port options here which the characterizer cannot handle. Some may be added later like ROM - if len(self.read_ports) == 0: - debug.error("Characterizer does not currently support SRAMs without read ports.",1) - if len(self.write_ports) == 0: - debug.error("Characterizer does not currently support SRAMs without write ports.",1) - - def write_generic_stimulus(self): - """ Create the instance, supplies, loads, and access transistors. """ - - # add vdd/gnd statements - self.sf.write("\n* Global Power Supplies\n") - self.stim.write_supply() - - # instantiate the sram - self.sf.write("\n* Instantiation of the SRAM\n") - self.stim.inst_sram(sram=self.sram, - port_signal_names=(self.addr_name,self.din_name,self.dout_name), - port_info=(self.total_port_num,self.write_ports,self.read_ports), - abits=self.addr_size, - dbits=self.word_size, - sram_name=self.name) - self.sf.write("\n* SRAM output loads\n") - for port in self.read_ports: - for i in range(self.word_size): - self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load)) - - - def write_delay_stimulus(self): - """ Creates a stimulus file for simulations to probe a bitcell at a given clock period. - Address and bit were previously set with set_probe(). - Input slew (in ns) and output capacitive load (in fF) are required for charaterization. - """ - self.check_arguments() - - # obtains list of time-points for each rising clk edge - self.create_test_cycles() - - # 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("* Delay stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(self.period, - self.load, - self.slew)) - self.stim = stimuli(self.sf, self.corner) - # include files in stimulus file - self.stim.write_include(self.trim_sp_file) - - self.write_generic_stimulus() - - # generate data and addr signals - self.sf.write("\n* Generation of data and address signals\n") - self.gen_data() - self.gen_addr() - - - # generate control signals - self.sf.write("\n* Generation of control signals\n") - self.gen_control() - - self.sf.write("\n* Generation of Port clock signal\n") - for port in range(self.total_port_num): - self.stim.gen_pulse(sig_name="CLK{0}".format(port), - v1=0, - v2=self.vdd_voltage, - offset=self.period, - period=self.period, - t_rise=self.slew, - t_fall=self.slew) - - self.write_delay_measures() - - # run until the end of the cycle time - self.stim.write_control(self.cycle_times[-1] + self.period) - - self.sf.close() - - - def write_power_stimulus(self, trim): - """ Creates a stimulus file to measure leakage power only. - This works on the *untrimmed netlist*. - """ - self.check_arguments() - - # obtains list of time-points for each rising clk edge - #self.create_test_cycles() - - # 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("* Power stimulus for period of {0}n\n\n".format(self.period)) - self.stim = stimuli(self.sf, self.corner) - - # include UNTRIMMED files in stimulus file - if trim: - self.stim.write_include(self.trim_sp_file) - else: - self.stim.write_include(self.sim_sp_file) - - self.write_generic_stimulus() - - # generate data and addr signals - self.sf.write("\n* Generation of data and address signals\n") - for write_port in self.write_ports: - for i in range(self.word_size): - self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i), - v_val=0) - for port in range(self.total_port_num): - for i in range(self.addr_size): - self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i), - v_val=0) - - # generate control signals - self.sf.write("\n* Generation of control signals\n") - for port in range(self.total_port_num): - self.stim.gen_constant(sig_name="CSB{0}".format(port), v_val=self.vdd_voltage) - if port in self.write_ports and port in self.read_ports: - self.stim.gen_constant(sig_name="WEB{0}".format(port), v_val=self.vdd_voltage) - - self.sf.write("\n* Generation of global clock signal\n") - for port in range(self.total_port_num): - self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) - - self.write_power_measures() - - # run until the end of the cycle time - self.stim.write_control(2*self.period) - - self.sf.close() - - def get_delay_meas_values(self, delay_name, port): - """Get the values needed to generate a Spice measurement statement based on the name of the measurement.""" - debug.check('lh' in delay_name or 'hl' in delay_name, "Measure command {0} does not contain direction (lh/hl)") - trig_clk_name = "clk{0}".format(port) - meas_name="{0}{1}".format(delay_name, port) - targ_name = "{0}".format("{0}{1}_{2}".format(self.dout_name,port,self.probe_data)) - half_vdd = 0.5 * self.vdd_voltage - trig_slew_low = 0.1 * self.vdd_voltage - targ_slew_high = 0.9 * self.vdd_voltage - if 'delay' in delay_name: - trig_dir="RISE" - trig_val = half_vdd - targ_val = half_vdd - trig_name = trig_clk_name - if 'lh' in delay_name: - targ_dir="RISE" - trig_td = targ_td = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]] - else: - targ_dir="FALL" - trig_td = targ_td = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]] - - elif 'slew' in delay_name: - trig_name = targ_name - if 'lh' in delay_name: - trig_val = trig_slew_low - targ_val = targ_slew_high - targ_dir = trig_dir = "RISE" - trig_td = targ_td = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]] - else: - trig_val = targ_slew_high - targ_val = trig_slew_low - targ_dir = trig_dir = "FALL" - trig_td = targ_td = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]] - else: - debug.error(1, "Measure command {0} not recognized".format(delay_name)) - return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td) - - def write_delay_measures_read_port(self, port): - """ - Write the measure statements to quantify the delay and power results for a read port. - """ - # add measure statements for delays/slews - for dname in self.delay_meas_names: - meas_values = self.get_delay_meas_values(dname, port) - self.stim.gen_meas_delay(*meas_values) - - # add measure statements for power - for pname in self.power_meas_names: - if "read" not in pname: - continue - #Different naming schemes are used for the measure cycle dict and measurement names. - #TODO: make them the same so they can be indexed the same. - if '1' in pname: - t_initial = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]+1] - elif '0' in pname: - t_initial = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]+1] - self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port), - t_initial=t_initial, - t_final=t_final) - - def write_delay_measures_write_port(self, port): - """ - Write the measure statements to quantify the power results for a write port. - """ - # add measure statements for power - for pname in self.power_meas_names: - if "write" not in pname: - continue - t_initial = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]+1] - if '1' in pname: - t_initial = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]] - t_final = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]+1] - - self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port), - t_initial=t_initial, - t_final=t_final) - - def write_delay_measures(self): - """ - Write the measure statements to quantify the delay and power results for all targeted ports. - """ - 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)) - - for read_port in self.targ_read_ports: - self.write_delay_measures_read_port(read_port) - for write_port in self.targ_write_ports: - self.write_delay_measures_write_port(write_port) - - - def write_power_measures(self): - """ - Write the measure statements to quantify the leakage power only. - """ - - self.sf.write("\n* Measure statements for idle leakage power\n") - - # add measure statements for power - t_initial = self.period - t_final = 2*self.period - self.stim.gen_meas_power(meas_name="leakage_power", - t_initial=t_initial, - t_final=t_final) - - def find_feasible_period_one_port(self, port): - """ - 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. - """ - debug.check(port in self.read_ports, "Characterizer requires a read port to determine a period.") - - feasible_period = float(tech.spice["feasible_period"]) - #feasible_period = float(2.5)#What happens if feasible starting point is wrong? - time_out = 9 - while True: - time_out -= 1 - if (time_out <= 0): - debug.error("Timed out, could not find a feasible period.",2) - - #Clear any write target ports and set read port - self.targ_write_ports = [] - self.targ_read_ports = [port] - success = False - - debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port)) - self.period = feasible_period - (success, results)=self.run_delay_simulation() - #Clear these target ports after simulation - self.targ_read_ports = [] - - if not success: - feasible_period = 2 * feasible_period - continue - - #Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews - feasible_delays = [results[port][mname] for mname in self.delay_meas_names if "delay" in mname] - feasible_slews = [results[port][mname] for mname in self.delay_meas_names if "slew" in mname] - delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(*feasible_delays) - slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(*feasible_slews) - debug.info(2, "feasible_period passed for Port {3}: {0}ns {1} {2} ".format(feasible_period, - delay_str, - slew_str, - port)) - - if success: - debug.info(2, "Found feasible_period for port {0}: {1}ns".format(port, feasible_period)) - self.period = feasible_period - #Only return results related to input port. - return results[port] - - def find_feasible_period(self): - """ - Loops through all read ports determining the feasible period and collecting - delay information from each port. - """ - feasible_delays = [{} for i in range(self.total_port_num)] - self.period = float(tech.spice["feasible_period"]) - - #Get initial feasible delays from first port - feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0]) - previous_period = self.period - - - #Loops through all the ports checks if the feasible period works. Everything restarts it if does not. - #Write ports do not produce delays which is why they are not included here. - i = 1 - while i < len(self.read_ports): - port = self.read_ports[i] - #Only extract port values from the specified port, not the entire results. - feasible_delays[port].update(self.find_feasible_period_one_port(port)) - #Function sets the period. Restart the entire process if period changes to collect accurate delays - if self.period > previous_period: - i = 0 - else: - i+=1 - previous_period = self.period - debug.info(1, "Found feasible_period: {0}ns".format(self.period)) - return feasible_delays - - - def parse_values(self, values_names, port, mult = 1.0): - """Parse multiple values in the timing output file. Optional multiplier. - Return a dict of the input names and values. Port used for parsing file. - """ - values = [] - all_values_floats = True - for vname in values_names: - #ngspice converts all measure characters to lowercase, not tested on other sims - value = parse_spice_list("timing", "{0}{1}".format(vname.lower(), port)) - #Check if any of the values fail to parse - if type(value)!=float: - all_values_floats = False - values.append(value) - - #Apply Multiplier only if all values are floats. Let other check functions handle this error. - if all_values_floats: - return {values_names[i]:values[i]*mult for i in range(len(values))} - else: - return {values_names[i]:values[i] for i in range(len(values))} - - 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. - """ - #Sanity Check - debug.check(self.period > 0, "Target simulation period non-positive") - - result = [{} for i in range(self.total_port_num)] - # Checking from not data_value to data_value - self.write_delay_stimulus() - - self.stim.run_sim() - - #Loop through all targeted ports and collect delays and powers. - #Too much duplicate code here. Try reducing - for port in self.targ_read_ports: - debug.info(2, "Check delay values for port {}".format(port)) - delay_names = ["{0}{1}".format(mname,port) for mname in self.delay_meas_names] - delay_names = [mname for mname in self.delay_meas_names] - delays = self.parse_values(delay_names, port, 1e9) # scale delays to ns - if not self.check_valid_delays(tuple(delays.values())): - return (False,{}) - result[port].update(delays) - - power_names = [mname for mname in self.power_meas_names if 'read' in mname] - powers = self.parse_values(power_names, port, 1e3) # scale power to mw - #Check that power parsing worked. - for name, power in powers.items(): - if type(power)!=float: - debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad. - result[port].update(powers) - - for port in self.targ_write_ports: - power_names = [mname for mname in self.power_meas_names if 'write' in mname] - powers = self.parse_values(power_names, port, 1e3) # scale power to mw - #Check that power parsing worked. - for name, power in powers.items(): - if type(power)!=float: - debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad. - result[port].update(powers) - - # The delay is from the negative edge for our SRAM - return (True,result) - - - def run_power_simulation(self): - """ - This simulates a disabled SRAM to get the leakage power when it is off. - - """ - debug.info(1, "Performing leakage power simulations.") - self.write_power_stimulus(trim=False) - self.stim.run_sim() - leakage_power=parse_spice_list("timing", "leakage_power") - debug.check(leakage_power!="Failed","Could not measure leakage power.") - debug.info(1, "Leakage power of full array is {0} mW".format(leakage_power*1e3)) - #debug - #sys.exit(1) - - self.write_power_stimulus(trim=True) - self.stim.run_sim() - trim_leakage_power=parse_spice_list("timing", "leakage_power") - debug.check(trim_leakage_power!="Failed","Could not measure leakage power.") - debug.info(1, "Leakage power of trimmed array is {0} mW".format(trim_leakage_power*1e3)) - - # 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 check_valid_delays(self, delay_tuple): - """ Check if the measurements are defined and if they are valid. """ - - (delay_hl, delay_lh, slew_hl, slew_lh) = delay_tuple - period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew) - - # 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: - delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh) - slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh) - debug.info(2,"Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, - delays_str, - slews_str)) - return False - - delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh) - slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh) - if delay_hl>self.period or delay_lh>self.period or slew_hl>self.period or slew_lh>self.period: - debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, - delays_str, - slews_str)) - return False - else: - debug.info(2,"Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, - delays_str, - slews_str)) - - return True - - def find_min_period(self, feasible_delays): - """ - Determine a single minimum period for all ports. - """ - - feasible_period = ub_period = self.period - lb_period = 0.0 - target_period = 0.5 * (ub_period + lb_period) - - #Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position. - #For testing purposes, only checks read ports. - for port in self.read_ports: - target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period) - #The min period of one port becomes the new lower bound. Reset the upper_bound. - lb_period = target_period - ub_period = feasible_period - - #Clear the target ports before leaving - self.targ_read_ports = [] - self.targ_write_ports = [] - return target_period - - def find_min_period_one_port(self, feasible_delays, port, lb_period, ub_period, target_period): - """ - Searches for the smallest period with output delays being within 5% of - long period. For the current logic to characterize multiport, bounds are required as an input. - """ - - #previous_period = ub_period = self.period - #ub_period = self.period - #lb_period = 0.0 - #target_period = 0.5 * (ub_period + lb_period) - - # Binary search algorithm to find the min period (max frequency) of input port - time_out = 25 - self.targ_read_ports = [port] - while True: - time_out -= 1 - if (time_out <= 0): - debug.error("Timed out, could not converge on minimum period.",2) - - self.period = target_period - debug.info(1, "MinPeriod Search Port {3}: {0}ns (ub: {1} lb: {2})".format(target_period, - ub_period, - lb_period, - port)) - - if self.try_period(feasible_delays): - ub_period = target_period - else: - lb_period = target_period - - if relative_compare(ub_period, lb_period, error_tolerance=0.05): - # ub_period is always feasible. - return ub_period - - #Update target - target_period = 0.5 * (ub_period + lb_period) - - - def try_period(self, feasible_delays): - """ - 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. - """ - # Run Delay simulation but Power results not used. - (success, results) = self.run_delay_simulation() - if not success: - return False - - #Check the values of target readwrite and read ports. Write ports do not produce delays in this current version - for port in self.targ_read_ports: - delay_port_names = [mname for mname in self.delay_meas_names if "delay" in mname] - for dname in delay_port_names: - if not relative_compare(results[port][dname],feasible_delays[port][dname],error_tolerance=0.05): - debug.info(2,"Delay too big {0} vs {1}".format(results[port][dname],feasible_delays[port][dname])) - return False - - #key=raw_input("press return to continue") - - #Dynamic way to build string. A bit messy though. - delay_str = ', '.join("{0}={1}ns".format(mname, results[port][mname]) for mname in self.delay_meas_names) - debug.info(2,"Successful period {0}, Port {2}, {1}".format(self.period, - delay_str, - port)) - return True - - def set_probe(self,probe_address, probe_data): - """ Probe address and data can be set separately to utilize other - functions in this characterizer besides analyze.""" - self.probe_address = probe_address - self.probe_data = probe_data - - - - def prepare_netlist(self): - """ Prepare a trimmed netlist and regular netlist. """ - - # Set up to trim the netlist here if that is enabled - if OPTS.trim_netlist: - self.trim_sp_file = "{}reduced.sp".format(OPTS.openram_temp) - self.trimsp=trim_spice(self.sp_file, self.trim_sp_file) - self.trimsp.set_configuration(self.num_banks, - self.num_rows, - self.num_cols, - self.word_size) - self.trimsp.trim(self.probe_address,self.probe_data) - else: - # The non-reduced netlist file when it is disabled - self.trim_sp_file = "{}sram.sp".format(OPTS.openram_temp) - - # The non-reduced netlist file for power simulation - self.sim_sp_file = "{}sram.sp".format(OPTS.openram_temp) - # Make a copy in temp for debugging - shutil.copy(self.sp_file, self.sim_sp_file) - - - + def analyze(self,probe_address, probe_data, slews, loads): """ Main function to test the delays of different bits. @@ -671,7 +31,7 @@ class worst_case(): char_sram_data = {} self.set_probe(probe_address, probe_data) - self.prepare_netlist() + #self.prepare_netlist() self.load=max(loads) self.slew=max(slews) @@ -708,340 +68,10 @@ class worst_case(): def get_test_bits(self): """Statically determines address and bit values to test""" - #First and last address, first and last bit - bit_addrs = ["0"*self.addr_size, "1"*self.addr_size] - data_positions = [0, self.word_size-1] + #First and last address, first middle, and last bit. Last bit is repeated twice with different data position. + bit_addrs = ["0"*self.addr_size, "0"+"1"*(self.addr_size-1), "1"*self.addr_size, "1"*self.addr_size] + data_positions = [0, (self.word_size-1)//2, 0, self.word_size-1] #Return them in a tuple form return [(bit_addrs[i], data_positions[i]) for i in range(len(bit_addrs))] - def simulate_loads_and_slews(self, slews, loads, leakage_offset): - """Simulate all specified output loads and input slews pairs of all ports""" - measure_data = self.get_empty_measure_data_dict() - #Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways. - self.targ_read_ports = self.read_ports - self.targ_write_ports = self.write_ports - for slew in slews: - for load in loads: - self.set_load_slew(load,slew) - # Find the delay, dynamic power, and leakage power of the trimmed array. - (success, delay_results) = self.run_delay_simulation() - debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load)) - debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load)) - #The results has a dict for every port but dicts can be empty (e.g. ports were not targeted). - for port in range(self.total_port_num): - for mname,value in delay_results[port].items(): - if "power" in mname: - # Subtract partial array leakage and add full array leakage for the power measures - measure_data[port][mname].append(value + leakage_offset) - else: - measure_data[port][mname].append(value) - return measure_data - - def add_data(self, data, port): - """ Add the array of data values """ - debug.check(len(data)==self.word_size, "Invalid data word size.") - debug.check(port < len(self.data_values), "Port number cannot index data values.") - index = 0 - for c in data: - if c=="0": - self.data_values[port][index].append(0) - elif c=="1": - self.data_values[port][index].append(1) - else: - debug.error("Non-binary data string",1) - index += 1 - - def add_address(self, address, port): - """ Add the array of address values """ - debug.check(len(address)==self.addr_size, "Invalid address size.") - index = 0 - for c in address: - if c=="0": - self.addr_values[port][index].append(0) - elif c=="1": - self.addr_values[port][index].append(1) - else: - debug.error("Non-binary address string",1) - index += 1 - def add_noop_one_port(self, address, data, port): - """ Add the control values for a noop to a single port. """ - #This is to be used as a helper function for the other add functions. Cycle and comments are omitted. - self.add_control_one_port(port, "noop") - if port in self.write_ports: - self.add_data(data,port) - self.add_address(address, port) - - def add_noop_all_ports(self, comment, address, data): - """ Add the control values for a noop to all ports. """ - self.add_comment("All", comment) - self.cycle_times.append(self.t_current) - self.t_current += self.period - - for port in range(self.total_port_num): - self.add_noop_one_port(address, data, port) - - - def add_read(self, comment, address, data, port): - """ Add the control values for a read cycle. """ - debug.check(port in self.read_ports, "Cannot add read cycle to a write port.") - self.add_comment(port, comment) - self.cycle_times.append(self.t_current) - self.t_current += self.period - self.add_control_one_port(port, "read") - - #If the port is also a readwrite then add data. - if port in self.write_ports: - self.add_data(data,port) - self.add_address(address, port) - - #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port - noop_data = "0"*self.word_size - #Add noops to all other ports. - for unselected_port in range(self.total_port_num): - if unselected_port != port: - self.add_noop_one_port(address, noop_data, unselected_port) - - def add_write(self, comment, address, data, port): - """ Add the control values for a write cycle. """ - debug.check(port in self.write_ports, "Cannot add read cycle to a read port.") - self.add_comment(port, comment) - self.cycle_times.append(self.t_current) - self.t_current += self.period - - self.add_control_one_port(port, "write") - self.add_data(data,port) - self.add_address(address,port) - - #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port - noop_data = "0"*self.word_size - #Add noops to all other ports. - for unselected_port in range(self.total_port_num): - if unselected_port != port: - self.add_noop_one_port(address, noop_data, unselected_port) - - def add_control_one_port(self, port, op): - """Appends control signals for operation to a given port""" - #Determine values to write to port - web_val = 1 - csb_val = 1 - if op == "read": - csb_val = 0 - elif op == "write": - csb_val = 0 - web_val = 0 - elif op != "noop": - debug.error("Could not add control signals for port {0}. Command {1} not recognized".format(port,op),1) - - #Append the values depending on the type of port - self.csb_values[port].append(csb_val) - #If port is in both lists, add rw control signal. Condition indicates its a RW port. - if port in self.write_ports and port in self.read_ports: - self.web_values[port].append(web_val) - - def add_comment(self, port, comment): - """Add comment to list to be printed in stimulus file""" - #Clean up time before appending. Make spacing dynamic as well. - time = "{0:.2f} ns:".format(self.t_current) - time_spacing = len(time)+6 - self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times), - port, - time, - time_spacing, - comment)) - def gen_test_cycles_one_port(self, read_port, write_port): - """Intended but not implemented: Returns a list of key time-points [ns] of the waveform (each rising edge) - of the cycles to do a timing evaluation of a single port. Current: Values overwritten for multiple calls""" - - # Create the inverse address for a scratch address - inverse_address = "" - for c in self.probe_address: - if c=="0": - inverse_address += "1" - elif c=="1": - inverse_address += "0" - else: - debug.error("Non-binary address string",1) - - # For now, ignore data patterns and write ones or zeros - data_ones = "1"*self.word_size - data_zeros = "0"*self.word_size - - if self.t_current == 0: - self.add_noop_all_ports("Idle cycle (no positive clock edge)", - inverse_address, data_zeros) - - self.add_write("W data 1 address 0..00", - inverse_address,data_ones,write_port) - - self.add_write("W data 0 address 11..11 to write value", - self.probe_address,data_zeros,write_port) - self.measure_cycles["write0_{0}".format(write_port)] = len(self.cycle_times)-1 - #self.write0_cycle=len(self.cycle_times)-1 # Remember for power measure - - # This also ensures we will have a H->L transition on the next read - self.add_read("R data 1 address 00..00 to set DOUT caps", - inverse_address,data_zeros,read_port) - - self.add_read("R data 0 address 11..11 to check W0 worked", - self.probe_address,data_zeros,read_port) - self.measure_cycles["read0_{0}".format(read_port)] = len(self.cycle_times)-1 - #self.read0_cycle=len(self.cycle_times)-1 # Remember for power measure - - self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)", - inverse_address,data_zeros) - #Does not seem like is is used anywhere commenting out for now. - #self.idle_cycle=len(self.cycle_times)-1 # Remember for power measure - - self.add_write("W data 1 address 11..11 to write value", - self.probe_address,data_ones,write_port) - self.measure_cycles["write1_{0}".format(write_port)] = len(self.cycle_times)-1 - #self.write1_cycle=len(self.cycle_times)-1 # Remember for power measure - - self.add_write("W data 0 address 00..00 to clear DIN caps", - inverse_address,data_zeros,write_port) - - # This also ensures we will have a L->H transition on the next read - self.add_read("R data 0 address 00..00 to clear DOUT caps", - inverse_address,data_zeros,read_port) - - self.add_read("R data 1 address 11..11 to check W1 worked", - self.probe_address,data_zeros,read_port) - self.measure_cycles["read1_{0}".format(read_port)] = len(self.cycle_times)-1 - #self.read1_cycle=len(self.cycle_times)-1 # Remember for power measure - - self.add_noop_all_ports("Idle cycle (if read takes >1 cycle))", - self.probe_address,data_zeros) - - def get_available_port(self,get_read_port): - """Returns the first accessible read or write port. """ - if get_read_port and len(self.read_ports) > 0: - return self.read_ports[0] - elif not get_read_port and len(self.write_ports) > 0: - return self.write_ports[0] - return None - - def create_test_cycles(self): - """Returns a list of key time-points [ns] of the waveform (each rising edge) - of the cycles to do a timing evaluation. The last time is the end of the simulation - and does not need a rising edge.""" - #Using this requires setting at least one port to target for simulation. - if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0: - debug.error("No ports selected for characterization.",1) - - # Start at time 0 - self.t_current = 0 - - # Cycle times (positive edge) with comment - self.cycle_comments = [] - self.cycle_times = [] - self.measure_cycles = {} - - # Control signals for ports. These are not the final signals and will likely be changed later. - #web is the enable for write ports. Dicts used for simplicity as ports are not necessarily incremental. - self.web_values = {port:[] for port in self.write_ports} - #csb acts as an enable for the read ports. - self.csb_values = {port:[] for port in range(self.total_port_num)} - - # Address and data values for each address/data bit. A 3d list of size #ports x bits x cycles. - self.data_values=[[[] for bit in range(self.word_size)] for port in range(len(self.write_ports))] - self.addr_values=[[[] for bit in range(self.addr_size)] for port in range(self.total_port_num)] - - #Get any available read/write port in case only a single write or read ports is being characterized. - cur_read_port = self.get_available_port(get_read_port=True) - cur_write_port = self.get_available_port(get_read_port=False) - - #These checks should be superceded by check_arguments which should have been called earlier, so this is a double check. - debug.check(cur_read_port != None, "Characterizer requires at least 1 read port") - debug.check(cur_write_port != None, "Characterizer requires at least 1 write port") - - #Characterizing the remaining target ports. Not the final design. - write_pos = 0 - read_pos = 0 - while True: - #Exit when all ports have been characterized - if write_pos >= len(self.targ_write_ports) and read_pos >= len(self.targ_read_ports): - break - - #Select new write and/or read ports for the next cycle. Use previous port if none remaining. - if write_pos < len(self.targ_write_ports): - cur_write_port = self.targ_write_ports[write_pos] - write_pos+=1 - if read_pos < len(self.targ_read_ports): - cur_read_port = self.targ_read_ports[read_pos] - read_pos+=1 - - #Add test cycle of read/write port pair. One port could have been used already, but the other has not. - self.gen_test_cycles_one_port(cur_read_port, cur_write_port) - - def analytical_delay(self,sram, slews, loads): - """ Return the analytical model results for the SRAM. - """ - debug.check(OPTS.num_rw_ports < 2 and OPTS.num_w_ports < 1 and OPTS.num_r_ports < 1 , - "Analytical characterization does not currently support multiport.") - - delay_lh = [] - delay_hl = [] - slew_lh = [] - slew_hl = [] - for slew in slews: - for load in loads: - self.set_load_slew(load,slew) - bank_delay = sram.analytical_delay(self.slew,self.load) - # Convert from ps to ns - delay_lh.append(bank_delay.delay/1e3) - delay_hl.append(bank_delay.delay/1e3) - slew_lh.append(bank_delay.slew/1e3) - slew_hl.append(bank_delay.slew/1e3) - - power = sram.analytical_power(self.process, self.vdd_voltage, self.temperature, load) - #convert from nW to mW - power.dynamic /= 1e6 - power.leakage /= 1e6 - debug.info(1,"Dynamic Power: {0} mW".format(power.dynamic)) - debug.info(1,"Leakage Power: {0} mW".format(power.leakage)) - - sram_data = { "min_period": 0, - "leakage_power": power.leakage} - port_data = [{"delay_lh": delay_lh, - "delay_hl": delay_hl, - "slew_lh": slew_lh, - "slew_hl": slew_hl, - "read0_power": power.dynamic, - "read1_power": power.dynamic, - "write0_power": power.dynamic, - "write1_power": power.dynamic, - }] - return (sram_data,port_data) - - def gen_data(self): - """ Generates the PWL data inputs for a simulation timing test. """ - for write_port in self.write_ports: - for i in range(self.word_size): - sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i) - self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05) - - def gen_addr(self): - """ - Generates the address inputs for a simulation timing test. - This alternates between all 1's and all 0's for the address. - """ - for port in range(self.total_port_num): - for i in range(self.addr_size): - sig_name = "{0}{1}_{2}".format(self.addr_name,port,i) - self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05) - - def gen_control(self): - """ Generates the control signals """ - for port in range(self.total_port_num): - self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) - if port in self.read_ports and port in self.write_ports: - self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) - - - def get_empty_measure_data_dict(self): - """Make a dict of lists for each type of delay and power measurement to append results to""" - measure_names = self.delay_meas_names + self.power_meas_names - #Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. - measure_data = [{mname:[] for mname in measure_names} for i in range(self.total_port_num)] - return measure_data diff --git a/compiler/sram.py b/compiler/sram.py index eafdf80a..5cbf80a3 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -86,7 +86,6 @@ class sram(): # Save the extracted spice file if OPTS.use_pex: import verify - print(verify.__file__) start_time = datetime.datetime.now() # Output the extracted design if requested sp_file = OPTS.output_path + "temp_pex.sp" diff --git a/compiler/tests/27_worst_case_delay_test.py b/compiler/tests/27_worst_case_delay_test.py index 06283109..35d48b27 100755 --- a/compiler/tests/27_worst_case_delay_test.py +++ b/compiler/tests/27_worst_case_delay_test.py @@ -11,13 +11,17 @@ import globals from globals import OPTS import debug +@unittest.skip("SKIPPING 27_worst_case_delay_test") class worst_case_timing_sram_test(openram_test): def runTest(self): + OPTS.tech_name = "freepdk45" globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.spice_name="ngspice" + OPTS.spice_name="hspice" OPTS.analytical_delay = False OPTS.trim_netlist = False + OPTS.check_lvsdrc = True + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload @@ -27,21 +31,36 @@ class worst_case_timing_sram_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) + word_size, num_words, num_banks = 32, 32, 1 from sram import sram from sram_config import sram_config - c = sram_config(word_size=4, - num_words=32, - num_banks=1) - c.words_per_row=1 - debug.info(1, "Testing the timing for 2 bits inside a 2bit, 16words SRAM with 1 bank") + c = sram_config(word_size=word_size, + num_words=num_words, + num_banks=num_banks) + #c.words_per_row=1 + c.compute_sizes() + debug.info(1, "Testing the timing different bitecells inside a {}bit, {} words SRAM with {} bank".format( + word_size, num_words, num_banks)) s = sram(c, name="sram1") - - tempspice = OPTS.openram_temp + "temp.sp" - s.sp_write(tempspice) - - + + sp_netlist_file = OPTS.openram_temp + "temp.sp" + s.sp_write(sp_netlist_file) + + if OPTS.use_pex: + gdsname = OPTS.output_path + s.name + ".gds" + s.gds_write(gdsname) + + import verify + reload(verify) + # Output the extracted design if requested + sp_pex_file = OPTS.output_path + s.name + "_pex.sp" + verify.run_pex(s.name, gdsname, sp_netlist_file, output=sp_pex_file) + sp_sim_file = sp_pex_file + else: + sp_sim_file = sp_netlist_file + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - wc = worst_case(s.s, tempspice, corner) + wc = worst_case(s.s, sp_sim_file, corner) import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] diff --git a/compiler/tests/sram1.gds b/compiler/tests/sram1.gds deleted file mode 100644 index d6ed28eb6391318f026a98274c50b90601202712..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304042 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 SUFciqRxbEw86)q=Nbi5xVB-z| diff --git a/compiler/verify/__init__.py b/compiler/verify/__init__.py index 4ddd966c..1f0ffaab 100644 --- a/compiler/verify/__init__.py +++ b/compiler/verify/__init__.py @@ -54,7 +54,6 @@ else: if OPTS.pex_exe == None: from .none import run_pex,print_pex_stats - print("why god why") elif "calibre"==OPTS.pex_exe[0]: from .calibre import run_pex,print_pex_stats elif "magic"==OPTS.pex_exe[0]: