From 1e24b780bb4b13e41a4b686606a624a18c781662 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 2 Oct 2020 13:32:52 -0700 Subject: [PATCH] Initial pex sram test. --- compiler/characterizer/delay.py | 184 +++++++++++++-------------- compiler/characterizer/functional.py | 2 +- compiler/characterizer/stimuli.py | 65 ++++------ compiler/sram/sram_base.py | 4 +- compiler/tests/testutils.py | 2 + compiler/verify/magic.py | 5 + 6 files changed, 128 insertions(+), 134 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 11235109..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.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.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 3ecbe48d..44c6d278 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -323,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 diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 0b2ecef4..da8e16f0 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" @@ -246,7 +237,7 @@ class stimuli(): 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") diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 5f02ce56..351619bf 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -94,7 +94,7 @@ class sram_base(design, verilog, lef): # 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 @@ -107,7 +107,7 @@ class sram_base(design, verilog, lef): bl = [] br = [] - storage_layer_name = self.bitcell.get_pin("Q").layer + storage_layer_name = "m1" bitline_layer_name = self.bitcell.get_pin("bl").layer for cell in range(len(bank_offset)): 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 1401d834..ece741cd 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -406,6 +406,10 @@ def write_script_pex_rule(gds_name, cell_name, output): else: pre = "" 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") @@ -413,6 +417,7 @@ def write_script_pex_rule(gds_name, cell_name, output): 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")