diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 0cbe6e8c..133ecc9b 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -211,6 +211,12 @@ class layout(): def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): """ Adds an instance of a mod to this module """ + # Contacts are not really instances, so skip them + if "contact" not in mod.name: + # Check that the instance name is unique + for inst in self.insts: + debug.check(name != inst.name, "Duplicate named instance in {0}: {1}".format(self.name, name)) + self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) debug.info(3, "adding instance {}".format(self.insts[-1])) # This is commented out for runtime reasons @@ -1029,7 +1035,7 @@ class layout(): """ import channel_route cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self) - self.add_inst("vc", cr) + self.add_inst(cr.name, cr) self.connect_inst([]) def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None): @@ -1038,7 +1044,7 @@ class layout(): """ import channel_route cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self) - self.add_inst("hc", cr) + self.add_inst(cr.name, cr) self.connect_inst([]) def add_boundary(self, ll=vector(0, 0), ur=None): diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 273666cf..6323a351 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -56,8 +56,14 @@ class delay(simulation): """ Create measurement names. The names themselves currently define the type of measurement """ self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] - self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power", - "disabled_read0_power", "disabled_read1_power", "disabled_write0_power", "disabled_write1_power"] + self.power_meas_names = ["read0_power", + "read1_power", + "write0_power", + "write1_power", + "disabled_read0_power", + "disabled_read1_power", + "disabled_write0_power", + "disabled_write1_power"] # self.voltage_when_names = ["volt_bl", "volt_br"] # self.bitline_delay_names = ["delay_bl", "delay_br"] @@ -133,18 +139,18 @@ class delay(simulation): """ self.bitline_volt_meas = [] - self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO", - self.bl_name)) + self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ZERO", + self.bl_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO - self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO", - self.br_name)) + self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ZERO", + self.br_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ZERO - self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE", - self.bl_name)) + self.bitline_volt_meas.append(voltage_at_measure("v_bl_READ_ONE", + self.bl_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE - self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE", - self.br_name)) + self.bitline_volt_meas.append(voltage_at_measure("v_br_READ_ONE", + self.br_name)) self.bitline_volt_meas[-1].meta_str = sram_op.READ_ONE return self.bitline_volt_meas @@ -174,16 +180,16 @@ class delay(simulation): self.dout_volt_meas = [] for meas in self.delay_meas: # Output voltage measures - self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name), - meas.targ_name_no_port)) + self.dout_volt_meas.append(voltage_at_measure("v_{}".format(meas.name), + meas.targ_name_no_port)) self.dout_volt_meas[-1].meta_str = meas.meta_str if not OPTS.use_pex: - self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name+"{}", "FALL", "RISE", measure_scale=1e9) + self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name + "{}", "FALL", "RISE", measure_scale=1e9) else: self.sen_meas = delay_measure("delay_sen", self.clk_frmt, self.sen_name, "FALL", "RISE", measure_scale=1e9) - self.sen_meas.meta_str = sram_op.READ_ZERO + self.sen_meas.meta_str = sram_op.READ_ZERO self.sen_meas.meta_add_delay = True return self.dout_volt_meas + [self.sen_meas] @@ -191,26 +197,26 @@ class delay(simulation): def create_read_bit_measures(self): """ Adds bit measurements for read0 and read1 cycles """ - self.read_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} + self.read_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []} meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE) for cycle in meas_cycles: meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) - for polarity,meas in single_bit_meas.items(): + for polarity, meas in single_bit_meas.items(): meas.meta_str = cycle self.read_bit_meas[polarity].append(meas) # Dictionary values are lists, reduce to a single list of measurements - return [meas for meas_list in self.read_bit_meas.values() for meas in meas_list] + return [meas for meas_list in self.read_bit_meas.values() for meas in meas_list] def create_write_bit_measures(self): """ Adds bit measurements for write0 and write1 cycles """ - self.write_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} + self.write_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []} meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE) for cycle in meas_cycles: meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) - for polarity,meas in single_bit_meas.items(): + for polarity, meas in single_bit_meas.items(): meas.meta_str = cycle self.write_bit_meas[polarity].append(meas) # Dictionary values are lists, reduce to a single list of measurements @@ -279,12 +285,8 @@ class delay(simulation): # instantiate the sram self.sf.write("\n* Instantiation of the SRAM\n") - if not OPTS.use_pex: - self.stim.inst_model(pins=self.pins, - model_name=self.sram.name) - else: - self.stim.inst_sram_pex(pins=self.pins, - model_name=self.sram.name) + self.stim.inst_model(pins=self.pins, + model_name=self.sram.name) self.sf.write("\n* SRAM output loads\n") for port in self.read_ports: @@ -320,7 +322,6 @@ class delay(simulation): self.gen_data() self.gen_addr() - # generate control signals self.sf.write("\n* Generation of control signals\n") self.gen_control() @@ -382,7 +383,7 @@ class delay(simulation): self.sf.write("\n* Generation of global clock signal\n") for port in self.all_ports: - self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) + self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) self.write_power_measures() @@ -433,7 +434,7 @@ class delay(simulation): # These measurements have there time further delayed to the neg. edge of the clock. if delay_obj.meta_add_delay: - meas_cycle_delay += self.period/2 + meas_cycle_delay += self.period / 2 return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) @@ -442,7 +443,7 @@ class delay(simulation): # Return value is intended to match the power measure format: t_initial, t_final, port t_initial = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]] - t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str]+1] + t_final = self.cycle_times[self.measure_cycles[port][power_obj.meta_str] + 1] return (t_initial, t_final, port) @@ -455,7 +456,7 @@ class delay(simulation): # Measurement occurs slightly into the next period so we know that the value # "stuck" after the end of the period -> current period start + 1.25*period - at_time = meas_cycle+1.25*self.period + at_time = meas_cycle + 1.25 * self.period return (at_time, port) @@ -465,7 +466,7 @@ class delay(simulation): """ # Only checking 0 value reads for now. - t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]] + t_trig = self.cycle_times[self.measure_cycles[port][sram_op.READ_ZERO]] return (t_trig, self.vdd_voltage, port) @@ -480,7 +481,6 @@ class delay(simulation): measure_variant_inp_tuple = self.get_measure_variants(port, measure, "read") measure.write_measure(self.stim, measure_variant_inp_tuple) - def write_delay_measures_write_port(self, port): """ Write the measure statements to quantify the power results for a write port. @@ -513,7 +513,6 @@ class delay(simulation): self.sf.write("* Write ports {}\n".format(write_port)) self.write_delay_measures_write_port(write_port) - def write_power_measures(self): """ Write the measure statements to quantify the leakage power only. @@ -523,7 +522,7 @@ class delay(simulation): # add measure statements for power t_initial = self.period - t_final = 2*self.period + t_final = 2 * self.period self.stim.gen_meas_power(meas_name="leakage_power", t_initial=t_initial, t_final=t_final) @@ -543,7 +542,7 @@ class delay(simulation): while True: time_out -= 1 if (time_out <= 0): - debug.error("Timed out, could not find a feasible period.",2) + debug.error("Timed out, could not find a feasible period.", 2) # Write ports are assumed non-critical to timing, so the first available is used self.targ_write_ports = [self.write_ports[0]] @@ -589,7 +588,6 @@ class delay(simulation): 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 @@ -614,7 +612,7 @@ class delay(simulation): include leakage of all cells. """ - debug.check(self.period > 0, "Target simulation period non-positive") + debug.check(self.period > 0, "Target simulation period non-positive") self.write_delay_stimulus() @@ -630,30 +628,29 @@ class delay(simulation): for port in self.targ_write_ports: if not self.check_bit_measures(self.write_bit_meas, port): - return(False,{}) + return(False, {}) - debug.info(2, "Checking write values for port {}".format(port)) + debug.info(2, "Checking write values for port {}".format(port)) write_port_dict = {} for measure in self.write_lib_meas: write_port_dict[measure.name] = measure.retrieve_measure(port=port) if not check_dict_values_is_float(write_port_dict): - debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict),1) + debug.error("Failed to Measure Write Port Values:\n\t\t{0}".format(write_port_dict), 1) result[port].update(write_port_dict) - for port in self.targ_read_ports: # First, check that the memory has the right values at the right times if not self.check_bit_measures(self.read_bit_meas, port): - return(False,{}) + return(False, {}) debug.info(2, "Checking read delay values for port {}".format(port)) # Check sen timing, then bitlines, then general measurements. if not self.check_sen_measure(port): - return (False,{}) + return (False, {}) if not self.check_read_debug_measures(port): - return (False,{}) + return (False, {}) # Check timing for read ports. Power is only checked if it was read correctly read_port_dict = {} @@ -661,26 +658,25 @@ class delay(simulation): read_port_dict[measure.name] = measure.retrieve_measure(port=port) if not self.check_valid_delays(read_port_dict): - return (False,{}) + return (False, {}) if not check_dict_values_is_float(read_port_dict): - debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) + debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict), 1) result[port].update(read_port_dict) - return (True,result) + return (True, result) def check_sen_measure(self, port): """Checks that the sen occurred within a half-period""" sen_val = self.sen_meas.retrieve_measure(port=port) - debug.info(2,"s_en delay={}ns".format(sen_val)) + debug.info(2, "s_en delay={}ns".format(sen_val)) if self.sen_meas.meta_add_delay: - max_delay = self.period/2 + max_delay = self.period / 2 else: max_delay = self.period return not (type(sen_val) != float or sen_val > max_delay) - def check_read_debug_measures(self, port): """Debug measures that indicate special conditions.""" @@ -694,23 +690,23 @@ class delay(simulation): val = meas.retrieve_measure(port=port) if self.bl_name == meas.targ_name_no_port: bl_vals[meas.meta_str] = val - elif self.br_name == meas.targ_name_no_port: + elif self.br_name == meas.targ_name_no_port: br_vals[meas.meta_str] = val - debug.info(2,"{}={}".format(meas.name,val)) + debug.info(2, "{}={}".format(meas.name, val)) dout_success = True bl_success = False for meas in self.dout_volt_meas: val = meas.retrieve_measure(port=port) - debug.info(2,"{}={}".format(meas.name, val)) - debug.check(type(val)==float, "Error retrieving numeric measurement: {0} {1}".format(meas.name,val)) + debug.info(2, "{}={}".format(meas.name, val)) + debug.check(type(val)==float, "Error retrieving numeric measurement: {0} {1}".format(meas.name, val)) - if meas.meta_str == sram_op.READ_ONE and val < self.vdd_voltage*0.1: + if meas.meta_str == sram_op.READ_ONE and val < self.vdd_voltage * 0.1: dout_success = False debug.info(1, "Debug measurement failed. Value {}V was read on read 1 cycle.".format(val)) bl_success = self.check_bitline_meas(bl_vals[sram_op.READ_ONE], br_vals[sram_op.READ_ONE]) - elif meas.meta_str == sram_op.READ_ZERO and val > self.vdd_voltage*0.9: + elif meas.meta_str == sram_op.READ_ZERO and val > self.vdd_voltage * 0.9: dout_success = False debug.info(1, "Debug measurement failed. Value {}V was read on read 0 cycle.".format(val)) bl_success = self.check_bitline_meas(br_vals[sram_op.READ_ONE], bl_vals[sram_op.READ_ONE]) @@ -718,10 +714,9 @@ class delay(simulation): # If the bitlines have a correct value while the output does not then that is a # sen error. FIXME: there are other checks that can be done to solidfy this conclusion. if not dout_success and bl_success: - debug.error("Sense amp enable timing error. Increase the delay chain through the configuration file.",1) + debug.error("Sense amp enable timing error. Increase the delay chain through the configuration file.", 1) return dout_success - def check_bit_measures(self, bit_measures, port): """ @@ -732,29 +727,29 @@ class delay(simulation): for polarity, meas_list in bit_measures.items(): for meas in meas_list: val = meas.retrieve_measure(port=port) - debug.info(2,"{}={}".format(meas.name, val)) + debug.info(2, "{}={}".format(meas.name, val)) if type(val) != float: continue meas_cycle = meas.meta_str # Loose error conditions. Assume it's not metastable but account for noise during reads. if (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.NONINVERTING) or\ (meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.INVERTING): - success = val < self.vdd_voltage/2 + success = val < self.vdd_voltage / 2 elif (meas_cycle == sram_op.READ_ZERO and polarity == bit_polarity.INVERTING) or\ (meas_cycle == sram_op.READ_ONE and polarity == bit_polarity.NONINVERTING): - success = val > self.vdd_voltage/2 + success = val > self.vdd_voltage / 2 elif (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.INVERTING) or\ (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.NONINVERTING): - success = val > self.vdd_voltage/2 + success = val > self.vdd_voltage / 2 elif (meas_cycle == sram_op.WRITE_ONE and polarity == bit_polarity.INVERTING) or\ (meas_cycle == sram_op.WRITE_ZERO and polarity == bit_polarity.NONINVERTING): - success = val < self.vdd_voltage/2 + success = val < self.vdd_voltage / 2 if not success: - debug.info(1,("Wrong value detected on probe bit during read/write cycle. " - "Check writes and control logic for bugs.\n measure={}, op={}, " - "bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name,val)) + debug.info(1, ("Wrong value detected on probe bit during read/write cycle. " + "Check writes and control logic for bugs.\n measure={}, op={}, " + "bit_storage={}, V(bit)={}").format(meas.name, meas_cycle.name, polarity.name, val)) - return success + return success def check_bitline_meas(self, v_discharged_bl, v_charged_bl): """ @@ -764,11 +759,11 @@ class delay(simulation): # The inputs looks at discharge/charged bitline rather than left or right (bl/br) # Performs two checks, discharging bitline is at least 10% away from vdd and there is a # 10% vdd difference between the bitlines. Both need to fail to be considered a s_en error. - min_dicharge = v_discharged_bl < self.vdd_voltage*0.9 - min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage*0.1 + min_dicharge = v_discharged_bl < self.vdd_voltage * 0.9 + min_diff = (v_charged_bl - v_discharged_bl) > self.vdd_voltage * 0.1 - debug.info(1,"min_dicharge={}, min_diff={}".format(min_dicharge,min_diff)) - return (min_dicharge and min_diff) + debug.info(1, "min_dicharge={}, min_diff={}".format(min_dicharge, min_diff)) + return (min_dicharge and min_diff) def run_power_simulation(self): """ @@ -779,20 +774,20 @@ class delay(simulation): 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.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)) + 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) + return (leakage_power * 1e3, trim_leakage_power * 1e3) def check_valid_delays(self, result_dict): """ Check if the measurements are defined and if they are valid. """ @@ -802,30 +797,31 @@ class delay(simulation): delay_lh = result_dict["delay_lh"] slew_hl = result_dict["slew_hl"] slew_lh = result_dict["slew_lh"] - period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew) + 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)) + 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) - half_period = self.period/2 # high-to-low delays start at neg. clk edge, so they need to be less than half_period + slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl, slew_lh) + # high-to-low delays start at neg. clk edge, so they need to be less than half_period + half_period = self.period / 2 if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \ or delay_hl<0 or delay_lh<0 or slew_hl<0 or slew_lh<0: - 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)) + 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)) + 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 @@ -844,12 +840,12 @@ class delay(simulation): 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 + ub_period = feasible_period # Clear the target ports before leaving self.targ_read_ports = [] self.targ_write_ports = [] - return target_period + return target_period def find_min_period_one_port(self, feasible_delays, port, lb_period, ub_period, target_period): """ @@ -870,7 +866,7 @@ class delay(simulation): while True: time_out -= 1 if (time_out <= 0): - debug.error("Timed out, could not converge on minimum period.",2) + 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, @@ -1077,7 +1073,6 @@ class delay(simulation): data_ones = "1" * self.word_size data_zeros = "0" * self.word_size wmask_ones = "1" * self.num_wmasks - wmask_zeroes = "0" * self.num_wmasks if self.t_current == 0: self.add_noop_all_ports("Idle cycle (no positive clock edge)") @@ -1132,7 +1127,6 @@ class delay(simulation): self.add_noop_clock_one_port(read_port) self.measure_cycles[read_port]["disabled_read1"] = len(self.cycle_times) - 1 - # This also ensures we will have a L->H transition on the next read self.add_read("R data 0 address {} to clear dout caps".format(inverse_address), inverse_address, @@ -1173,8 +1167,10 @@ class delay(simulation): # 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) - 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") + 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") # Create test cycles for specified target ports. write_pos = 0 diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 270c1a2b..44c6d278 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -229,17 +229,25 @@ class functional(simulation): sp_read_value = "" for bit in range(self.word_size + self.num_spare_cols): value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(), bit, check)) - if value > self.v_high: - sp_read_value = "1" + sp_read_value - elif value < self.v_low: - sp_read_value = "0" + sp_read_value - else: - error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port, - bit, - value, - eo_period, - self.v_low, - self.v_high) + try: + value = float(value) + if value > self.v_high: + sp_read_value = "1" + sp_read_value + elif value < self.v_low: + sp_read_value = "0" + sp_read_value + else: + error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port, + bit, + value, + eo_period, + self.v_low, + self.v_high) + except ValueError: + error ="FAILED: {0}_{1} value {2} at time {3}n is not a float.".format(dout_port, + bit, + value, + eo_period) + return (0, error) self.read_results.append([sp_read_value, dout_port, eo_period, check]) @@ -315,7 +323,7 @@ class functional(simulation): else: expected_value = self.word_size + self.num_spare_cols for i in range(expected_value - len(new_value)): - new_value = "0" + new_value + new_value = "0" + new_value # print("Binary Conversion: {} to {}".format(value, new_value)) return new_value @@ -348,8 +356,8 @@ class functional(simulation): # Write important signals to stim file self.sf.write("\n\n* Important signals for debug\n") - self.sf.write("* bl: {}\n".format(self.bl_name)) - self.sf.write("* br: {}\n".format(self.br_name)) + self.sf.write("* bl: {}\n".format(self.bl_name.format(port))) + self.sf.write("* br: {}\n".format(self.br_name.format(port))) self.sf.write("* s_en: {}\n".format(self.sen_name)) self.sf.write("* q: {}\n".format(self.q_name)) self.sf.write("* qbar: {}\n".format(self.qbar_name)) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 51f99c03..ecb9adb8 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -51,23 +51,21 @@ class simulation(): self.load = tech.spice["dff_in_cap"] * 4 self.v_high = self.vdd_voltage - tech.spice["nom_threshold"] - self.v_low = tech.spice["nom_threshold"] + self.v_low = tech.spice["nom_threshold"] self.gnd_voltage = 0 def create_signal_names(self): self.addr_name = "a" self.din_name = "din" self.dout_name = "dout" - self.pins = self.gen_pin_names(port_signal_names=(self.addr_name,self.din_name,self.dout_name), - port_info=(len(self.all_ports),self.write_ports,self.read_ports), + self.pins = self.gen_pin_names(port_signal_names=(self.addr_name, self.din_name, self.dout_name), + port_info=(len(self.all_ports), self.write_ports, self.read_ports), abits=self.addr_size, dbits=self.word_size + self.num_spare_cols) debug.check(len(self.sram.pins) == len(self.pins), "Number of pins generated for characterization \ do not match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins, - self.pins)) - #This is TODO once multiport control has been finalized. - #self.control_name = "CSB" + self.pins)) def set_stimulus_variables(self): # Clock signals @@ -96,7 +94,7 @@ class simulation(): def add_control_one_port(self, port, op): """Appends control signals for operation to a given port""" - #Determine values to write to port + # Determine values to write to port web_val = 1 csb_val = 1 if op == "read": @@ -406,7 +404,9 @@ class simulation(): return pin_names def add_graph_exclusions(self): - """Exclude portions of SRAM from timing graph which are not relevant""" + """ + Exclude portions of SRAM from timing graph which are not relevant + """ # other initializations can only be done during analysis when a bit has been selected # for testing. @@ -414,10 +414,12 @@ class simulation(): self.sram.graph_exclude_addr_dff() self.sram.graph_exclude_data_dff() self.sram.graph_exclude_ctrl_dffs() - self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() + self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() def set_internal_spice_names(self): - """Sets important names for characterization such as Sense amp enable and internal bit nets.""" + """ + Sets important names for characterization such as Sense amp enable and internal bit nets. + """ port = self.read_ports[0] if not OPTS.use_pex: @@ -458,11 +460,10 @@ class simulation(): self.sen_name = self.get_sen_name(self.graph.all_paths) debug.info(2, "s_en name = {}".format(self.sen_name)) - self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1) self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1) - debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name)) + debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name)) def get_sen_name(self, paths, assumed_port=None): """ @@ -481,7 +482,9 @@ class simulation(): return sen_name def create_graph(self): - """Creates timing graph to generate the timing paths for the SRAM output.""" + """ + Creates timing graph to generate the timing paths for the SRAM output. + """ self.sram.clear_exclude_bits() # Removes previous bit exclusions self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column) @@ -492,7 +495,9 @@ class simulation(): self.sram.build_graph(self.graph, self.sram_instance_name, self.pins) def get_bl_name_search_exclusions(self): - """Gets the mods as a set which should be excluded while searching for name.""" + """ + Gets the mods as a set which should be excluded while searching for name. + """ # Exclude the RBL as it contains bitcells which are not in the main bitcell array # so it makes the search awkward @@ -519,7 +524,9 @@ class simulation(): return path_net_name def get_bl_name(self, paths, port): - """Gets the signal name associated with the bitlines in the bank.""" + """ + Gets the signal name associated with the bitlines in the bank. + """ cell_mod = factory.create(module_type=OPTS.bitcell) cell_bl = cell_mod.get_bl_name(port) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 90fd6213..785b2b3b 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -15,7 +15,6 @@ import tech import debug import subprocess import os -import sys import numpy as np from globals import OPTS @@ -40,32 +39,26 @@ class stimuli(): debug.info(2, "Not using spice library") self.device_models = tech.spice["fet_models"][self.process] - self.sram_name = "Xsram" - - def inst_sram(self, pins, inst_name): - """ Function to instatiate an SRAM subckt. """ - - self.sf.write("{} ".format(self.sram_name)) - for pin in self.sram_pins: - self.sf.write("{0} ".format(pin)) - self.sf.write("{0}\n".format(inst_name)) - def inst_model(self, pins, model_name): """ Function to instantiate a generic model with a set of pins """ - self.sf.write("X{0} ".format(model_name)) - for pin in pins: - self.sf.write("{0} ".format(pin)) - self.sf.write("{0}\n".format(model_name)) + + if OPTS.use_pex: + self.inst_pex_model(pins, model_name) + else: + self.sf.write("X{0} ".format(model_name)) + for pin in pins: + self.sf.write("{0} ".format(pin)) + self.sf.write("{0}\n".format(model_name)) - def inst_sram_pex(self, pins, model_name): + def inst_pex_model(self, pins, model_name): self.sf.write("X{0} ".format(model_name)) for pin in pins: self.sf.write("{0} ".format(pin)) for bank in range(OPTS.num_banks): row = int(OPTS.num_words / OPTS.words_per_row) - 1 - col = int(OPTS.word_size * OPTS.words_per_row) - 1 - self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col)) - self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col)) + col = int(OPTS.word_size * OPTS.words_per_row) - 1 + self.sf.write("bitcell_Q_b{0}_r{1}_c{2} ".format(bank, row, col)) + self.sf.write("bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank, row, col)) # can't add all bitcells to top level due to ngspice max port count of 1005 # for row in range(int(OPTS.num_words / OPTS.words_per_row)): # for col in range(int(OPTS.word_size * OPTS.words_per_row)): @@ -76,7 +69,6 @@ class stimuli(): for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports): self.sf.write("bl{0}_{1} ".format(port, col)) self.sf.write("br{0}_{1} ".format(port, col)) - self.sf.write("s_en{0} ".format(bank)) self.sf.write("{0}\n".format(model_name)) @@ -94,14 +86,13 @@ class stimuli(): self.tx_length)) self.sf.write(".ENDS test_inv\n") - - def create_buffer(self, buffer_name, size=[1,3], beta=2.5): + def create_buffer(self, 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. """ - self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name, + self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name, self.gnd_name, buffer_name)) self.sf.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name, @@ -122,8 +113,6 @@ class stimuli(): self.tx_length)) self.sf.write(".ENDS test_{0}\n\n".format(buffer_name)) - - def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall): """ Generates a periodic signal with 50% duty cycle and slew rates. Period is measured @@ -140,7 +129,6 @@ class stimuli(): 0.5*period-0.5*t_rise-0.5*t_fall, period)) - def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup): """ Generate a PWL stimulus given a signal name and data values at each period. @@ -149,18 +137,22 @@ class stimuli(): to the initial value. """ # the initial value is not a clock time - debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match. {0} clock values, {1} data values for {2}".format(len(clk_times), len(data_values), sig_name)) + str = "Clock and data value lengths don't match. {0} clock values, {1} data values for {2}" + debug.check(len(clk_times)==len(data_values), + str.format(len(clk_times), + len(data_values), + sig_name)) # shift signal times earlier for setup time - times = np.array(clk_times) - setup*period + times = np.array(clk_times) - setup * period values = np.array(data_values) * self.voltage half_slew = 0.5 * slew self.sf.write("* (time, data): {}\n".format(list(zip(clk_times, data_values)))) self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0])) - for i in range(1,len(times)): - self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew, - values[i-1], - times[i]+half_slew, + for i in range(1, len(times)): + self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i] - half_slew, + values[i - 1], + times[i] + half_slew, values[i])) self.sf.write(")\n") @@ -169,9 +161,9 @@ class stimuli(): self.sf.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val)) def get_inverse_voltage(self, value): - if value > 0.5*self.voltage: + if value > 0.5 * self.voltage: return 0 - elif value <= 0.5*self.voltage: + elif value <= 0.5 * self.voltage: return self.voltage else: debug.error("Invalid value to get an inverse of: {0}".format(value)) @@ -184,7 +176,6 @@ class stimuli(): else: debug.error("Invalid value to get an inverse of: {0}".format(value)) - def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td): """ Creates the .meas statement for the measurement of delay """ measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n" @@ -230,7 +221,7 @@ class stimuli(): def gen_meas_value(self, meas_name, dout, t_intital, t_final): measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name, dout, t_intital, t_final) self.sf.write(measure_string) - + def write_control(self, end_time, runlvl=4): """ Write the control cards to run and end the simulation """ @@ -243,10 +234,10 @@ class stimuli(): reltol = 0.005 # 0.5% else: reltol = 0.001 # 0.1% - timestep = 10 #ps, was 5ps but ngspice was complaining the timestep was too small in certain tests. + timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests. # UIC is needed for ngspice to converge - self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep,end_time)) + self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time)) self.sf.write(".TEMP {}\n".format(self.temperature)) if OPTS.spice_name == "ngspice": # ngspice sometimes has convergence problems if not using gear method @@ -260,7 +251,7 @@ class stimuli(): # create plots for all signals self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n") if OPTS.debug_level>0: - if OPTS.spice_name in ["hspice","xa"]: + if OPTS.spice_name in ["hspice", "xa"]: self.sf.write(".probe V(*)\n") else: self.sf.write(".plot V(*)\n") @@ -271,7 +262,6 @@ class stimuli(): # end the stimulus file self.sf.write(".end\n\n") - def write_include(self, circuit): """Writes include statements, inputs are lists of model files""" @@ -291,13 +281,12 @@ class stimuli(): else: debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item)) - def write_supply(self): """ Writes supply voltage statements """ gnd_node_name = "0" self.sf.write("V{0} {0} {1} {2}\n".format(self.vdd_name, gnd_node_name, self.voltage)) - #Adding a commented out supply for simulators where gnd and 0 are not global grounds. + # Adding a commented out supply for simulators where gnd and 0 are not global grounds. self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n") self.sf.write("*V{0} {0} {1} {2}\n".format(self.gnd_name, gnd_node_name, 0.0)) @@ -306,7 +295,7 @@ class stimuli(): temp_stim = "{0}stim.sp".format(OPTS.openram_temp) import datetime start_time = datetime.datetime.now() - debug.check(OPTS.spice_exe!="","No spice simulator has been found.") + debug.check(OPTS.spice_exe != "", "No spice simulator has been found.") if OPTS.spice_name == "xa": # Output the xa configurations here. FIXME: Move this to write it once. @@ -314,27 +303,32 @@ class stimuli(): xa_cfg.write("set_sim_level -level 7\n") xa_cfg.write("set_powernet_level 7 -node vdd\n") xa_cfg.close() - cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 2".format(OPTS.spice_exe, - temp_stim, - OPTS.openram_temp) + cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt {3}".format(OPTS.spice_exe, + temp_stim, + OPTS.openram_temp, + OPTS.num_threads) valid_retcode=0 elif OPTS.spice_name == "hspice": # TODO: Should make multithreading parameter a configuration option - cmd = "{0} -mt 2 -i {1} -o {2}timing".format(OPTS.spice_exe, - temp_stim, - OPTS.openram_temp) + cmd = "{0} -mt {1} -i {2} -o {3}timing".format(OPTS.spice_exe, + OPTS.num_threads, + temp_stim, + OPTS.openram_temp) valid_retcode=0 else: # ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit # Measurements can't be made with a raw file set in ngspice # -r {2}timing.raw + ng_cfg = open("{}.spiceinit".format(OPTS.openram_temp), "w") + ng_cfg.write("set num_threads={}\n".format(OPTS.num_threads)) + ng_cfg.close() + cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe, temp_stim, OPTS.openram_temp) # for some reason, ngspice-25 returns 1 when it only has acceptable warnings valid_retcode=1 - spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w') spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w') @@ -348,7 +342,7 @@ class stimuli(): debug.error("Spice simulation error: " + cmd, -1) else: end_time = datetime.datetime.now() - delta_time = round((end_time-start_time).total_seconds(),1) - debug.info(2,"*** Spice: {} seconds".format(delta_time)) + delta_time = round((end_time - start_time).total_seconds(), 1) + debug.info(2, "*** Spice: {} seconds".format(delta_time)) diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index 80708dec..c07b4f1b 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -124,7 +124,7 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): self.wl_insts = [] self.driver_wordline_outputs = [] for port in self.all_ports: - self.wl_insts.append(self.add_inst(name="wl_driver", + self.wl_insts.append(self.add_inst(name="wl_driver{}".format(port), mod=self.wl_array)) temp = [] temp += [self.get_rbl_wordline_names(port)[port]] diff --git a/compiler/options.py b/compiler/options.py index 7f86a64f..632cd34f 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -119,6 +119,9 @@ class options(optparse.Values): # For sky130, we need magic for filtering. magic_exe = None + # Number of threads to use + num_threads = 2 + # Should we print out the banner at startup print_banner = True diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 6b5d117d..992919f0 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -67,7 +67,7 @@ class sram(): """Dump config file with all options. Include defaults and anything changed by input config.""" f = open(name, "w") - var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name))) + var_dict = dict((name, getattr(OPTS, name)) for name in dir(OPTS) if not name.startswith('__') and not callable(getattr(OPTS, name))) for var_name, var_value in var_dict.items(): if isinstance(var_value, str): f.write(str(var_name) + " = " + "\"" + str(var_value) + "\"\n") @@ -156,4 +156,4 @@ class sram(): oname = OPTS.output_path + OPTS.output_name + "_extended.py" debug.print_raw("Extended Config: Writing to {0}".format(oname)) self.extended_config_write(oname) - print_time("Extended Config", datetime.datetime.now(), start_time) \ No newline at end of file + print_time("Extended Config", datetime.datetime.now(), start_time) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index d35baf23..2dfd8096 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -414,7 +414,7 @@ class sram_1bank(sram_base): layer_stack=self.m1_stack, parent=self) if add_routes: - self.add_inst("hc", cr) + self.add_inst(cr.name, cr) self.connect_inst([]) else: self.col_addr_bus_size[port] = cr.height @@ -470,7 +470,7 @@ class sram_1bank(sram_base): layer_stack=layer_stack, parent=self) if add_routes: - self.add_inst("hc", cr) + self.add_inst(cr.name, cr) self.connect_inst([]) else: self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap @@ -482,7 +482,7 @@ class sram_1bank(sram_base): layer_stack=layer_stack, parent=self) if add_routes: - self.add_inst("hc", cr) + self.add_inst(cr.name, cr) self.connect_inst([]) else: self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index d18e1366..351619bf 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -15,9 +15,6 @@ from design import design from verilog import verilog from lef import lef from sram_factory import factory -from tech import drc -import numpy as np -import logical_effort class sram_base(design, verilog, lef): @@ -43,9 +40,6 @@ class sram_base(design, verilog, lef): if not self.num_spare_cols: self.num_spare_cols = 0 - # For logical effort delay calculations. - self.all_mods_except_control_done = False - def add_pins(self): """ Add pins for entire SRAM. """ @@ -87,18 +81,20 @@ class sram_base(design, verilog, lef): for bit in range(self.word_size + self.num_spare_cols): self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") - self.add_pin("vdd","POWER") - self.add_pin("gnd","GROUND") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") def add_global_pex_labels(self): """ Add pex labels at the sram level for spice analysis """ + + # add pex labels for bitcells for bank_num in range(len(self.bank_insts)): bank = self.bank_insts[bank_num] - pex_data = bank.reverse_transformation_bitcell(bank.mod.bitcell.name) + pex_data = bank.reverse_transformation_bitcell(self.bitcell.name) bank_offset = pex_data[0] # offset bank relative to sram Q_offset = pex_data[1] # offset of storage relative to bank @@ -112,46 +108,61 @@ class sram_base(design, verilog, lef): br = [] storage_layer_name = "m1" - bitline_layer_name = "m2" + bitline_layer_name = self.bitcell.get_pin("bl").layer for cell in range(len(bank_offset)): - Q = [bank_offset[cell][0] + Q_offset[cell][0], bank_offset[cell][1] + Q_offset[cell][1]] - Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], bank_offset[cell][1] + Q_bar_offset[cell][1]] + Q = [bank_offset[cell][0] + Q_offset[cell][0], + bank_offset[cell][1] + Q_offset[cell][1]] + Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], + bank_offset[cell][1] + Q_bar_offset[cell][1]] OPTS.words_per_row = self.words_per_row - self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))) , storage_layer_name, Q) - self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, int(cell % (OPTS.num_words / self.words_per_row)), int(cell / (OPTS.num_words))), storage_layer_name, Q_bar) + row = int(cell % (OPTS.num_words / self.words_per_row)) + col = int(cell / (OPTS.num_words)) + self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, + row, + col), + storage_layer_name, + Q) + self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, + row, + col), + storage_layer_name, + Q_bar) for cell in range(len(bl_offsets)): col = bl_meta[cell][0][2] for bitline in range(len(bl_offsets[cell])): - bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]] + bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], + float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]] bl.append([bitline_location, bl_meta[cell][bitline][3], col]) for cell in range(len(br_offsets)): col = br_meta[cell][0][2] for bitline in range(len(br_offsets[cell])): - bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]] - br.append([bitline_location, br_meta[cell][bitline][3], col]) + bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], + float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]] + br.append([bitline_location, br_meta[cell][bitline][3], col]) for i in range(len(bl)): - self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), bitline_layer_name, bl[i][0]) + self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), + bitline_layer_name, bl[i][0]) for i in range(len(br)): - self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), bitline_layer_name, br[i][0]) + self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), + bitline_layer_name, br[i][0]) # add pex labels for control logic - for i in range (len(self.control_logic_insts)): + for i in range(len(self.control_logic_insts)): instance = self.control_logic_insts[i] control_logic_offset = instance.offset for output in instance.mod.output_list: pin = instance.mod.get_pin(output) - pin.transform([0,0], instance.mirror, instance.rotate) - offset = [control_logic_offset[0] + pin.center()[0], control_logic_offset[1] + pin.center()[1]] - self.add_layout_pin_rect_center("{0}{1}".format(pin.name,i), storage_layer_name, offset) - - - - + pin.transform([0, 0], instance.mirror, instance.rotate) + offset = [control_logic_offset[0] + pin.center()[0], + control_logic_offset[1] + pin.center()[1]] + self.add_layout_pin_rect_center("{0}{1}".format(pin.name, i), + storage_layer_name, + offset) def create_netlist(self): """ Netlist creation """ @@ -370,10 +381,6 @@ class sram_base(design, verilog, lef): self.bank_count = 0 - # The control logic can resize itself based on the other modules. - # Requires all other modules added before control logic. - self.all_mods_except_control_done = True - c = reload(__import__(OPTS.control_logic)) self.mod_control_logic = getattr(c, OPTS.control_logic) @@ -619,6 +626,7 @@ class sram_base(design, verilog, lef): sp.write("* Column mux: {}:1\n".format(self.words_per_row)) sp.write("**************************************************\n") # This causes unit test mismatch + # sp.write("* Created: {0}\n".format(datetime.datetime.now())) # sp.write("* User: {0}\n".format(getpass.getuser())) # sp.write(".global {0} {1}\n".format(spice["vdd_name"], diff --git a/compiler/tests/26_hspice_pex_pinv_test.py b/compiler/tests/26_hspice_pex_pinv_test.py index b4b55cdb..96b2d108 100755 --- a/compiler/tests/26_hspice_pex_pinv_test.py +++ b/compiler/tests/26_hspice_pex_pinv_test.py @@ -9,8 +9,8 @@ Run regression tests/pex test on an extracted pinv to ensure pex functionality with HSPICE. """ import unittest -from testutils import header,openram_test -import sys,os +from testutils import header, openram_test +import sys, os sys.path.append(os.path.join(sys.path[0],"..")) import globals from globals import OPTS @@ -35,43 +35,43 @@ class hspice_pex_pinv_test(openram_test): # generate the pinv prev_purge_value = OPTS.purge_temp - OPTS.purge_temp = False # force set purge to false to save the sp file + # force set purge to false to save the sp file + OPTS.purge_temp = False debug.info(2, "Checking 1x size inverter") tx = pinv.pinv(name="pinv", size=1) - tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name) tx.gds_write(tempgds) - tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name) + tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name) tx.sp_write(tempsp) - # make sure that the library simulation is successful\ - sp_delay = self.simulate_delay(test_module = tempsp, - top_level_name = tx.name) - if sp_delay is "Failed": + # make sure that the library simulation is successful + sp_delay = self.simulate_delay(test_module=tempsp, + top_level_name=tx.name) + if sp_delay == "Failed": self.fail('Library Spice module did not behave as expected') # now generate its pex file pex_file = self.run_pex(tx) - OPTS.purge_temp = prev_purge_value # restore the old purge value + OPTS.purge_temp = prev_purge_value # restore the old purge value # generate simulation for pex, make sure the simulation is successful - pex_delay = self.simulate_delay(test_module = pex_file, - top_level_name = tx.name) + pex_delay = self.simulate_delay(test_module=pex_file, + top_level_name=tx.name) # make sure the extracted spice simulated - if pex_delay is "Failed": + if pex_delay == "Failed": self.fail('Pex file did not behave as expected') # if pex data is bigger than original spice file then result is ok # However this may not always be true depending on the netlist provided # comment out for now - #debug.info(2,"pex_delay: {0}".format(pex_delay)) - #debug.info(2,"sp_delay: {0}".format(sp_delay)) + # debug.info(2,"pex_delay: {0}".format(pex_delay)) + # debug.info(2,"sp_delay: {0}".format(sp_delay)) - #assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\ - #.format(pex_delay,sp_delay) + # assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\ + # .format(pex_delay,sp_delay) globals.end_openram() def simulate_delay(self, test_module, top_level_name): - from characterizer import charutils from charutils import parse_spice_list # setup simulation sim_file = OPTS.openram_temp + "stim.sp" @@ -87,43 +87,43 @@ class hspice_pex_pinv_test(openram_test): from characterizer import measurements, stimuli corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) sim_file = open(sim_file, "w") - simulation = stimuli(sim_file,corner) + simulation = stimuli(sim_file, corner) # library files simulation.write_include(cir_file) # supply voltages - simulation.gen_constant(sig_name ="vdd", - v_val = tech.spice["nom_supply_voltage"]) - simulation.gen_constant(sig_name = "gnd", - v_val = "0v") + simulation.gen_constant(sig_name="vdd", + v_val=tech.spice["nom_supply_voltage"]) + simulation.gen_constant(sig_name="gnd", + v_val="0v") run_time = tech.spice["feasible_period"] * 4 # input voltage clk_period = tech.spice["feasible_period"] - simulation.gen_pwl(sig_name ="input", - clk_times = [clk_period,clk_period], - data_values = [1,0], - period = clk_period, - slew = 0.001*tech.spice["feasible_period"], - setup = 0) + simulation.gen_pwl(sig_name="input", + clk_times=[clk_period, clk_period], + data_values=[1, 0], + period=clk_period, + slew=0.001 * tech.spice["feasible_period"], + setup=0) # instantiation of simulated pinv - simulation.inst_model(pins = ["input", "output", "vdd", "gnd"], - model_name = top_module_name) + simulation.inst_model(pins=["input", "output", "vdd", "gnd"], + model_name=top_module_name) # delay measurement - delay_measure = measurements.delay_measure(measure_name = "pinv_delay", - trig_name = "input", - targ_name = "output", - trig_dir_str = "FALL", - targ_dir_str = "RISE", - has_port = False) + delay_measure = measurements.delay_measure(measure_name="pinv_delay", + trig_name="input", + targ_name="output", + trig_dir_str="FALL", + targ_dir_str="RISE", + has_port=False) trig_td = trag_td = 0.01 * run_time - rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"] + rest_info = trig_td, trag_td, tech.spice["nom_supply_voltage"] delay_measure.write_measure(simulation, rest_info) - simulation.write_control(end_time = run_time) + simulation.write_control(end_time=run_time) sim_file.close() return simulation diff --git a/compiler/tests/26_ngspice_pex_pinv_test.py b/compiler/tests/26_ngspice_pex_pinv_test.py index e6e0cfb2..e5dbe5db 100755 --- a/compiler/tests/26_ngspice_pex_pinv_test.py +++ b/compiler/tests/26_ngspice_pex_pinv_test.py @@ -34,43 +34,43 @@ class ngspice_pex_pinv_test(openram_test): # generate the pinv module prev_purge_value = OPTS.purge_temp - OPTS.purge_temp = False # force set purge to false to save the sp file + OPTS.purge_temp = False # force set purge to false to save the sp file debug.info(2, "Checking 1x size inverter") tx = pinv.pinv(name="pinv", size=1) - tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name) + tempgds = "{0}{1}.gds".format(OPTS.openram_temp, tx.name) tx.gds_write(tempgds) - tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name) + tempsp = "{0}{1}.sp".format(OPTS.openram_temp, tx.name) tx.sp_write(tempsp) # make sure that the library simulation is successful - sp_delay = self.simulate_delay(test_module = tempsp, - top_level_name = tx.name) - if sp_delay is "Failed": + sp_delay = self.simulate_delay(test_module=tempsp, + top_level_name=tx.name) + if sp_delay == "Failed": self.fail('Library Spice module did not behave as expected') # now generate its pex file pex_file = self.run_pex(tx) - OPTS.purge_temp = prev_purge_value # restore the old purge value + # restore the old purge value + OPTS.purge_temp = prev_purge_value # generate simulation for pex, make sure the simulation is successful - pex_delay = self.simulate_delay(test_module = pex_file, - top_level_name = tx.name) + pex_delay = self.simulate_delay(test_module=pex_file, + top_level_name=tx.name) # make sure the extracted spice simulated - if pex_delay is "Failed": + if pex_delay == "Failed": self.fail('Pex file did not behave as expected') # if pex data is bigger than original spice file then result is ok # However this may not always be true depending on the netlist provided # comment out for now - #debug.info(2,"pex_delay: {0}".format(pex_delay)) - #debug.info(2,"sp_delay: {0}".format(sp_delay)) + # debug.info(2,"pex_delay: {0}".format(pex_delay)) + # debug.info(2,"sp_delay: {0}".format(sp_delay)) - #assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\ - #.format(pex_delay,sp_delay) + # assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\ + # .format(pex_delay,sp_delay) globals.end_openram() def simulate_delay(self, test_module, top_level_name): - from characterizer import charutils from charutils import parse_spice_list # setup simulation sim_file = OPTS.openram_temp + "stim.sp" @@ -86,47 +86,46 @@ class ngspice_pex_pinv_test(openram_test): from characterizer import measurements, stimuli corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) sim_file = open(sim_file, "w") - simulation = stimuli(sim_file,corner) + simulation = stimuli(sim_file, corner) # library files simulation.write_include(cir_file) # supply voltages - simulation.gen_constant(sig_name ="vdd", - v_val = tech.spice["nom_supply_voltage"]) + simulation.gen_constant(sig_name="vdd", + v_val=tech.spice["nom_supply_voltage"]) # The scn4m_subm and ngspice combination will have a gnd source error: # "Fatal error: instance vgnd is a shorted VSRC" # However, remove gnd power for all techa pass for this test # simulation.gen_constant(sig_name = "gnd", # v_val = "0v") - run_time = tech.spice["feasible_period"] * 4 # input voltage clk_period = tech.spice["feasible_period"] - simulation.gen_pwl(sig_name ="input", - clk_times = [clk_period,clk_period], - data_values = [1,0], - period = clk_period, - slew = 0.001*tech.spice["feasible_period"], - setup = 0) + simulation.gen_pwl(sig_name="input", + clk_times=[clk_period, clk_period], + data_values=[1, 0], + period=clk_period, + slew=0.001 * tech.spice["feasible_period"], + setup=0) # instantiation of simulated pinv - simulation.inst_model(pins = ["input", "output", "vdd", "gnd"], - model_name = top_module_name) + simulation.inst_model(pins=["input", "output", "vdd", "gnd"], + model_name=top_module_name) # delay measurement - delay_measure = measurements.delay_measure(measure_name = "pinv_delay", - trig_name = "input", - targ_name = "output", - trig_dir_str = "FALL", - targ_dir_str = "RISE", - has_port = False) + delay_measure = measurements.delay_measure(measure_name="pinv_delay", + trig_name="input", + targ_name="output", + trig_dir_str="FALL", + targ_dir_str="RISE", + has_port=False) trig_td = trag_td = 0.01 * run_time - rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"] + rest_info = trig_td, trag_td, tech.spice["nom_supply_voltage"] delay_measure.write_measure(simulation, rest_info) - simulation.write_control(end_time = run_time) + simulation.write_control(end_time=run_time) sim_file.close() return simulation diff --git a/compiler/tests/26_pex_test.py b/compiler/tests/26_pex_test.py deleted file mode 100755 index 4eff7db5..00000000 --- a/compiler/tests/26_pex_test.py +++ /dev/null @@ -1,319 +0,0 @@ -#!/usr/bin/env python3 -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2019 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -import unittest -from testutils import * -import sys,os -sys.path.append(os.getenv("OPENRAM_HOME")) -import globals -from globals import OPTS -from sram_factory import factory -import debug - -@unittest.skip("SKIPPING 26_pex_test") -class sram_func_test(openram_test): - - def runTest(self): - config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) - globals.init_openram(config_file) - - OPTS.use_pex = True - - # This is a hack to reload the characterizer __init__ with the spice version - from importlib import reload - import characterizer - reload(characterizer) - from characterizer import setup_hold - if not OPTS.spice_exe: - debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - - self.func_test(bank_num=1) - self.func_test(bank_num=2) - self.func_test(bank_num=4) - - globals.end_openram() - - def func_test(self, bank_num): - - import sram - import tech - - debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - s = sram.sram(word_size=OPTS.word_size, - num_words=OPTS.num_words, - num_banks=OPTS.num_banks, - name="test_sram1") - - tempspice = OPTS.openram_temp + "temp.sp" - tempgds = OPTS.openram_temp + "temp.gds" - - s.sp_write(tempspice) - s.gds_write(tempgds) - - self.assertFalse(verify.run_drc(s.name, tempgds)) - self.assertFalse(verify.run_lvs(s.name, tempgds, tempspice)) - self.assertFalse(verify.run_pex(s.name, tempgds, - tempspice, output=OPTS.openram_temp + "temp_pex.sp")) - - import sp_file - stimulus_file = OPTS.openram_temp + "stimulus.sp" - a_stimulus = sp_file.sp_file(stimulus_file) - self.write_stimulus(a_stimulus) - - simulator_file = OPTS.openram_temp + "simulator.sp" - a_simulator = sp_file.sp_file(simulator_file) - self.write_simulator(a_simulator) - - result_file = OPTS.openram_temp + "result" - - import os - - if OPTS.spice_name == "hspice": - cmd = "hspice -mt 2 -i {0} > {1} ".format( - simulator_file, result_file) - else: - cmd = "ngspice -b -i {0} > {1} ".format( - simulator_file, result_file) - os.system(cmd) - - import re - sp_result = open(result_file, "r") - contents = sp_result.read() - key = "vr1" - val = re.search( - r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents) - val = val.group(3) - value1 = float(self.convert_voltage_unit(val)) - - key = "vr2" - val = re.search( - r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents) - val = val.group(3) - value2 = float(self.convert_voltage_unit(val)) - - self.assertTrue(round(value1) > 0.5 * tech.spice["supply_voltage"]) - self.assertTrue(round(value2) < 0.5 * tech.spice["supply_voltage"]) - - - - def convert_voltage_unit(self, string): - newstring = "" - for letter in string: - if letter == "m": - letter = "10e-3" - elif letter == "u": - letter = "10e-6" - else: - letter = letter - newstring = str(newstring) + str(letter) - return newstring - - def convert_time_unit(self, string): - newstring = "" - for letter in string: - if letter == "f": - letter = "10e-15" - elif letter == "p": - letter = "10e-12" - elif letter == "n": - letter = "10e-9" - elif letter == "u": - letter = "10e-6" - elif letter == "m": - letter = "10e-3" - else: - letter = letter - newstring = str(newstring) + str(letter) - return newstring - - def write_simulator(self, sim_file): - sim_file.write("\n") - import tech - time_step = tech.spice["clock_period"] - for model in tech.spice["fet_models"]: - sim_file.write(".inc " + str(model) + "\n") - sim_file.write(".inc stimulus.sp\n") - sim_file.write(".inc temp_pex.sp\n") - sim_file.write(".options post runlvl=6\n") - sim_file.write("\n") - - sim_file.write( - "Xsource DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss source\n") - sim_file.write( - "Xsram DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd vss test_sram1\n") - sim_file.write("\n") - - sim_file.write(".MEASURE TRAN vr1 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format( - 4.5 * tech.spice["clock_period"], 5 * tech.spice["clock_period"])) - sim_file.write(".MEASURE TRAN vr2 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format( - 9.5 * tech.spice["clock_period"], 10 * tech.spice["clock_period"])) - sim_file.write("\n") - - if OPTS.spice_name in ["hspice","xa"]: - sim_file.write(".probe v(x*.*)\n") - sim_file.write(".tran 0.1ns {0}ns\n".format( - 10 * tech.spice["clock_period"])) - sim_file.write(".end\n") - else: - sim_file.write( - ".meas tran DELAY1.0 TRIG v(clk) VAL=0.5 RISE=6 TARG v(DATA[0]) VAL=0.5 TD=0.5n RISE=1\n") - sim_file.write(".tran 0.1ns {0}ns\n".format( - 10 * tech.spice["clock_period"])) - sim_file.write(".control\n") - sim_file.write("run\n") - #sim_file.write("plot CSb WEb OEb \n") - #sim_file.write("plot clk DATA0 \n") - sim_file.write("quit\n") - sim_file.write(".endc\n") - sim_file.write(".end\n") - sim_file.file.close() - - def write_stimulus(self, sti_file): - import tech - import sp_file - sti_file.write( - ".subckt source DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss\n") - - time_step = tech.spice["clock_period"] - - clk = sp_file.PWL(name="clk", port=["clk", "0"]) - for i in range(0, 11): - clk.write_pulse(i * time_step, time_step, "UP") - clk.write_to_sp(sti_file) - - WEB_inv = sp_file.PWL(name="WEb_inv", port=["WEb_inv", "0"]) - WEB = sp_file.PWL(name="WEB", port=["WEb", "0"]) - OEb = sp_file.PWL(name="OEb", port=["OEb", "0"]) - CSb = sp_file.PWL(name="CSb", port=["CSb", "0"]) - - # write - CSb.write_pulse(0.75 * time_step, time_step, "DN") - WEB.write_pulse(0.75 * time_step, time_step, "DN") - WEB_inv.write_pulse(0.75 * time_step, time_step, "UP") - CSb.write_pulse(1.75 * time_step, time_step, "DN") - WEB.write_pulse(1.75 * time_step, time_step, "DN") - WEB_inv.write_pulse(1.75 * time_step, time_step, "UP") - - # read - OEb.write_pulse(3.75 * time_step, time_step, "DN") - CSb.write_pulse(3.75 * time_step, time_step, "DN") - - # write - CSb.write_pulse(5.75 * time_step, time_step, "DN") - WEB.write_pulse(5.75 * time_step, time_step, "DN") - WEB_inv.write_pulse(5.75 * time_step, time_step, "UP") - CSb.write_pulse(6.75 * time_step, time_step, "DN") - WEB.write_pulse(6.75 * time_step, time_step, "DN") - WEB_inv.write_pulse(6.75 * time_step, time_step, "UP") - - # read - OEb.write_pulse(8.75 * time_step, time_step, "DN") - CSb.write_pulse(8.75 * time_step, time_step, "DN") - - CSb.write_to_sp(sti_file) - WEB.write_to_sp(sti_file) - WEB_inv.write_to_sp(sti_file) - OEb.write_to_sp(sti_file) - - sti_file.write("VA[0] A[0] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[ - "clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"])) - sti_file.write("VA[1] A[1] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[ - "clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"])) - sti_file.write("VA[2] A[2] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[ - "clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"])) - sti_file.write("VA[3] A[3] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[ - "clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"])) - - sti_file.write( - "xA[0]_buff A[0] ADDR[0]_inv ADDR[0] vdd vss test_buf\n") - sti_file.write( - "xA[1]_buff A[1] ADDR[1]_inv ADDR[1] vdd vss test_buf\n") - sti_file.write( - "xA[2]_buff A[2] ADDR[2]_inv ADDR[2] vdd vss test_buf\n") - sti_file.write( - "xA[3]_buff A[3] ADDR[3]_inv ADDR[3] vdd vss test_buf\n") - - VD_0 = sp_file.PWL(name="VD[0]", port=["D[0]", "0"]) - VD_0.write_pulse(0, 5 * time_step, "S1") - VD_0.write_pulse(5 * time_step, 5 * time_step, "S0") - VD_0.write_to_sp(sti_file) - - sti_file.write( - "xD[0]_buff D[0] DATA[0]_inv DATA[0]s vdd vss test_buf\n") - sti_file.write( - "xD[0]_gate DATA[0]s WEb WEb_inv DATA[0] vdd vss tran_gate\n") - sti_file.write("mp[0]_gate_vdd vdd write_v DATA[0] vdd " + str(tech.spice["pmos"]) + - " w=" + str(2 * tech.parameter["min_tx_size"]) + "u" + - " l=" + str(tech.drc["minlength_channel"]) + "u" + - "\n") - sti_file.write("mn[0]_gate_vss vss write_g DATA[0] vss " + str(tech.spice["nmos"]) + - " w=" + str(tech.parameter["min_tx_size"]) + "u" + - " l=" + str(tech.drc["minlength_channel"]) + "u" + - "\n") - - Vwrite_v = sp_file.PWL(name="write_v", port=["write_vs", "0"]) - Vwrite_v.write_pulse(0, 0.5 * time_step, "S1") - Vwrite_v.write_pulse(7.5 * time_step, time_step, "DN") - Vwrite_v.write_to_sp(sti_file) - sti_file.write( - "xwrite_v write_vs write_v_inv write_v vdd vss test_buf\n") - - Vwrite_g = sp_file.PWL(name="write_g", port=["write_gs", "0"]) - Vwrite_g.write_pulse(0, 0.5 * time_step, "S0") - Vwrite_g.write_pulse(3 * time_step, time_step, "UP") - Vwrite_g.write_to_sp(sti_file) - sti_file.write( - "xwrite_g write_gs write_g_inv write_g vdd vss test_buf\n") - - sti_file.write("Vdd vdd 0 DC " + - str(tech.spice["supply_voltage"]) + "\n") - sti_file.write("Vvss vss 0 DC 0\n") - sti_file.write(".ENDS source\n") - sti_file.write("\n") - - sti_file.write(".SUBCKT tran_gate in gate gate_inv out vdd vss\n") - sti_file.write("mp0 in gate out vdd " + str(tech.spice["pmos"]) + - " w=" + str(2 * tech.parameter["min_tx_size"]) + "u" + - " l=" + str(tech.drc["minlength_channel"]) + "u" + - "\n") - sti_file.write("mn0 in gate_inv out vss " + str(tech.spice["nmos"]) + - " w=" + str(tech.parameter["min_tx_size"]) + "u" + - " l=" + str(tech.drc["minlength_channel"]) + "u" + - "\n") - sti_file.write(".ENDS tran_gate\n") - sti_file.write("\n") - - sti_file.write(".SUBCKT test_buf in out_inv out_buf vdd vss\n") - sti_file.write("mpinv1 out_inv in vdd vdd " + str(tech.spice["pmos"]) + - " w=" + str(2 * tech.parameter["min_tx_size"]) + "u" + - " l=" + str(tech.drc["minlength_channel"]) + "u" + - "\n") - sti_file.write("mninv1 out_inv in vss vss " + str(tech.spice["nmos"]) + - " w=" + str(tech.parameter["min_tx_size"]) + "u" + - " l=" + str(tech.drc["minlength_channel"]) + "u" + - "\n") - sti_file.write("mpinv2 out_buf out_inv vdd vdd " + str(tech.spice["pmos"]) + - " w=" + str(2 * tech.parameter["min_tx_size"]) + "u" + - " l=" + str(tech.drc["minlength_channel"]) + "u" + - "\n") - sti_file.write("mninv2 out_buf out_inv vss vss " + str(tech.spice["nmos"]) + - " w=" + str(tech.parameter["min_tx_size"]) + "u" + - " l=" + str(tech.drc["minlength_channel"]) + "u" + - "\n") - sti_file.write(".ENDS test_buf\n") - sti_file.write("\n") - - sti_file.file.close() - - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/26_sram_pex_test.py b/compiler/tests/26_sram_pex_test.py new file mode 100755 index 00000000..5a3253de --- /dev/null +++ b/compiler/tests/26_sram_pex_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +@unittest.skip("SKIPPING 26_sram_pex_test") +class sram_pex_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.analytical_delay = False + OPTS.use_pex = True + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import functional + from sram_config import sram_config + c = sram_config(word_size=4, + num_words=32, + num_banks=1) + c.words_per_row=2 + c.recompute_sizes() + debug.info(1, "Functional test for sram with " + "{} bit words, {} words, {} words per row, {} banks".format(c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + s = factory.create(module_type="sram", sram_config=c) + tempspice = self.run_pex(s) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + f = functional(s.s, tempspice, corner) + (fail, error) = f.run() + self.assertTrue(fail, error) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/50_riscv_func_test.py b/compiler/tests/50_riscv_func_test.py index 1d5720f7..0d9825e6 100755 --- a/compiler/tests/50_riscv_func_test.py +++ b/compiler/tests/50_riscv_func_test.py @@ -8,13 +8,14 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS from sram_factory import factory import debug + @unittest.skip("SKIPPING 50_riscv_func_test") class riscv_func_test(openram_test): @@ -24,6 +25,7 @@ class riscv_func_test(openram_test): OPTS.analytical_delay = False OPTS.netlist_only = True OPTS.trim_netlist = False + OPTS.local_array_size = 16 OPTS.num_rw_ports = 1 OPTS.num_w_ports = 0 OPTS.num_r_ports = 1 @@ -33,7 +35,7 @@ class riscv_func_test(openram_test): from importlib import reload import characterizer reload(characterizer) - from characterizer import functional, delay + from characterizer import functional from sram_config import sram_config c = sram_config(word_size=32, write_size=8, @@ -53,7 +55,7 @@ class riscv_func_test(openram_test): corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) f = functional(s.s, tempspice, corner) (fail, error) = f.run() - self.assertTrue(fail,error) + self.assertTrue(fail, error) globals.end_openram() diff --git a/compiler/tests/50_riscv_phys_test.py b/compiler/tests/50_riscv_phys_test.py index 0ae11025..724131dd 100755 --- a/compiler/tests/50_riscv_phys_test.py +++ b/compiler/tests/50_riscv_phys_test.py @@ -8,14 +8,15 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS from sram_factory import factory import debug -@unittest.skip("SKIPPING 50_riscv_phys_test") + +#@unittest.skip("SKIPPING 50_riscv_phys_test") class riscv_phys_test(openram_test): def runTest(self): @@ -26,10 +27,11 @@ class riscv_phys_test(openram_test): OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 + OPTS.local_array_size = 16 globals.setup_bitcell() - OPTS.route_supplies=False - OPTS.perimeter_pins=False - + OPTS.route_supplies = False + OPTS.perimeter_pins = False + c = sram_config(word_size=32, write_size=8, num_words=256, diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index a8da9fb4..9188a4f9 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -82,6 +82,8 @@ class openram_test(unittest.TestCase): output = OPTS.openram_temp + a.name + ".pex.netlist" tempspice = "{0}{1}.sp".format(OPTS.openram_temp, a.name) tempgds = "{0}{1}.gds".format(OPTS.openram_temp, a.name) + + a.gds_write(tempgds) import verify result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 2343da26..ece741cd 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -295,11 +295,8 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False): global num_pex_runs num_pex_runs += 1 - #debug.warning("PEX using magic not implemented.") - #return 1 os.chdir(OPTS.openram_temp) - from tech import drc if output == None: output = name + ".pex.netlist" @@ -312,17 +309,11 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False): # pex_fix did run the pex using a script while dev orignial method # use batch mode. # the dev old code using batch mode does not run and is split into functions - #pex_runset = write_batch_pex_rule(gds_name,name,sp_name,output) - pex_runset = write_script_pex_rule(gds_name,name,output) + pex_runset = write_script_pex_rule(gds_name, name, output) errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name) outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name) - # bash mode command from dev branch - #batch_cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.pex_exe, - # OPTS.openram_temp, - # errfile, - # outfile) script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset, errfile, outfile) @@ -334,8 +325,8 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False): pex_nelist = open(output, 'r') s = pex_nelist.read() pex_nelist.close() - s = s.replace('pfet','p') - s = s.replace('nfet','n') + s = s.replace('pfet', 'p') + s = s.replace('nfet', 'n') f = open(output, 'w') f.write(s) f.close() @@ -345,12 +336,13 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False): results = f.readlines() f.close() out_errors = find_error(results) - debug.check(os.path.isfile(output),"Couldn't find PEX extracted output.") + debug.check(os.path.isfile(output), "Couldn't find PEX extracted output.") - correct_port(name,output,sp_name) + correct_port(name, output, sp_name) return out_errors -def write_batch_pex_rule(gds_name,name,sp_name,output): + +def write_batch_pex_rule(gds_name, name, sp_name, output): """ The dev branch old batch mode runset 2. magic can perform extraction with the following: @@ -394,7 +386,8 @@ def write_batch_pex_rule(gds_name,name,sp_name,output): f.close() return file -def write_script_pex_rule(gds_name,cell_name,output): + +def write_script_pex_rule(gds_name, cell_name, output): global OPTS run_file = OPTS.openram_temp + "run_pex.sh" f = open(run_file, "w") @@ -412,23 +405,29 @@ def write_script_pex_rule(gds_name,cell_name,output): pre = "#" else: pre = "" - f.write(pre+"extract\n".format(cell_name)) - f.write(pre+"ext2spice hierarchy off\n") - f.write(pre+"ext2spice format ngspice\n") - f.write(pre+"ext2spice renumber off\n") - f.write(pre+"ext2spice scale off\n") - f.write(pre+"ext2spice blackbox on\n") - f.write(pre+"ext2spice subcircuit top on\n") - f.write(pre+"ext2spice global off\n") - f.write(pre+"ext2spice {}\n".format(cell_name)) + f.write(pre + "extract\n") + f.write(pre + "ext2sim labels on\n") + f.write(pre + "ext2sim\n") + f.write(pre + "extresist simplify off\n") + f.write(pre + "extresist all\n") + f.write(pre + "ext2spice hierarchy off\n") + f.write(pre + "ext2spice format ngspice\n") + f.write(pre + "ext2spice renumber off\n") + f.write(pre + "ext2spice scale off\n") + f.write(pre + "ext2spice blackbox on\n") + f.write(pre + "ext2spice subcircuit top on\n") + f.write(pre + "ext2spice global off\n") + f.write(pre + "ext2spice extresist on\n") + f.write(pre + "ext2spice {}\n".format(cell_name)) f.write("quit -noprompt\n") f.write("eof\n") - f.write("mv {0}.spice {1}\n".format(cell_name,output)) + f.write("mv {0}.spice {1}\n".format(cell_name, output)) f.close() os.system("chmod u+x {}".format(run_file)) return run_file + def find_error(results): # Errors begin with "ERROR:" test = re.compile("ERROR:") @@ -438,6 +437,7 @@ def find_error(results): out_errors = len(stdouterrors) return out_errors + def correct_port(name, output_file_name, ref_file_name): pex_file = open(output_file_name, "r") contents = pex_file.read() @@ -456,9 +456,9 @@ def correct_port(name, output_file_name, ref_file_name): for bank in range(OPTS.num_banks): for bank in range(OPTS.num_banks): row = int(OPTS.num_words / OPTS.words_per_row) - 1 - col = int(OPTS.word_size * OPTS.words_per_row) - 1 - bitcell_list += "bitcell_Q_b{0}_r{1}_c{2} ".format(bank,row,col) - bitcell_list += "bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank,row,col) + col = int(OPTS.word_size * OPTS.words_per_row) - 1 + bitcell_list += "bitcell_Q_b{0}_r{1}_c{2} ".format(bank, row, col) + bitcell_list += "bitcell_Q_bar_b{0}_r{1}_c{2} ".format(bank, row, col) for col in range(OPTS.word_size * OPTS.words_per_row): for port in range(OPTS.num_r_ports + OPTS.num_w_ports + OPTS.num_rw_ports): bitcell_list += "bl{0}_{1} ".format(bank, col) @@ -484,13 +484,18 @@ def correct_port(name, output_file_name, ref_file_name): # write the new pex file with info in the memory output_file = open(output_file_name, "w") output_file.write(part1) - output_file.write(circuit_title+'\n') + output_file.write(circuit_title + '\n') output_file.write(part2) output_file.close() + def print_drc_stats(): - debug.info(1,"DRC runs: {0}".format(num_drc_runs)) + debug.info(1, "DRC runs: {0}".format(num_drc_runs)) + + def print_lvs_stats(): - debug.info(1,"LVS runs: {0}".format(num_lvs_runs)) + debug.info(1, "LVS runs: {0}".format(num_lvs_runs)) + + def print_pex_stats(): - debug.info(1,"PEX runs: {0}".format(num_pex_runs)) + debug.info(1, "PEX runs: {0}".format(num_pex_runs))