From 97fc37aec1be392cb26a8a8cd4aae70bbb6338b8 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 12 Dec 2018 23:59:32 -0800 Subject: [PATCH 01/29] Added checks for the bitline voltage at sense amp enable 50%. --- compiler/characterizer/bitline_delay.py | 47 ++++++++++++++++++++++--- compiler/modules/control_logic.py | 2 +- technology/freepdk45/tech/tech.py | 4 +-- technology/scn4m_subm/tech/tech.py | 1 + 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/compiler/characterizer/bitline_delay.py b/compiler/characterizer/bitline_delay.py index e5a50c16..e4f992fc 100644 --- a/compiler/characterizer/bitline_delay.py +++ b/compiler/characterizer/bitline_delay.py @@ -89,6 +89,7 @@ class bitline_delay(delay): self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address), self.probe_address,data_zeros,read_port) self.measure_cycles[read_port]["read0"] = len(self.cycle_times)-1 + def get_data_bit_column_number(self, probe_address, probe_data): """Calculates bitline column number of data bit under test using bit position and mux size""" if self.sram.col_addr_size>0: @@ -128,6 +129,44 @@ class bitline_delay(delay): # The delay is from the negative edge for our SRAM return (True,result) + def check_bitline_all_results(self, results): + """Checks the bitline values measured for each tested port""" + for port in self.targ_read_ports: + self.check_bitline_port_results(results[port]) + + def check_bitline_port_results(self, port_results) + """Performs three different checks for the bitline values: functionality, bitline swing from vdd, and differential bit swing""" + bl_volt, br_volt = port_results["bl_volt"], port_results["br_volt"] + self.check_functionality(bl_volt,br_volt) + self.check_swing_from_vdd(bl_volt,br_volt) + self.check_differential_swing(bl_volt,br_volt) + + def check_functionality(self, bl_volt, br_volt): + """Checks whether the read failed or not. Measured values are hardcoded with the intention of reading a 0.""" + if bl_volt > br_volt: + debug.error("Read failure. Value 1 was read instead of 0.",1) + + def check_swing_from_vdd(self, bl_volt, br_volt): + """Checks difference on discharging bitline from VDD to see if it is within margin of the RBL height parameter.""" + if bl_volt < br_volt: + discharge_volt = bl_volt + else: + discharge_volt = br_volt + desired_bl_volt = tech.parameter["rbl_height_percentage"]*self.vdd_voltage + debug.info(1, "Active bitline={:.3f}v, Desired bitline={:.3f}v".format(discharge_volt,desired_bl_volt)) + vdd_error_margin = .2 #20% of vdd margin for bitline, a little high for now. + if abs(discharge_volt - desired_bl_volt) > vdd_error_margin*self.vdd_voltage: + debug.warning("Bitline voltage is not within {}% Vdd margin. Delay chain/RBL could need resizing.".format(vdd_error_margin)) + + def check_differential_swing(self, bl_volt, br_volt): + """This check looks at the difference between the bitline voltages. This needs to be large enough to prevent + sensing errors.""" + bitline_swing = abs(bl_volt-br_volt) + debug.info(1,"Bitline swing={:.3f}v".format(bitline_swing)) + vdd_error_margin = .2 #20% of vdd margin for bitline, a little high for now. + if bitline_swing < vdd_error_margin*self.vdd_voltage: + debug.warning("Bitline swing less than {}% Vdd margin. Sensing errors more likely to occur.".format(vdd_error_margin)) + def analyze(self, probe_address, probe_data, slews, loads): """Measures the bitline swing of the differential bitlines (bl/br) at 50% s_en """ self.set_probe(probe_address, probe_data) @@ -141,10 +180,10 @@ class bitline_delay(delay): debug.info(1,"Bitline swing test: corner {}".format(self.corner)) (success, results)=self.run_delay_simulation() debug.check(success, "Bitline Failed: period {}".format(self.period)) - for mname in self.bitline_meas_names: - bitline_swings[mname] = results[read_port][mname] - debug.info(1,"Bitline values (bl/br): {}".format(bitline_swings)) - return bitline_swings + debug.info(1,"Bitline values (bl/br): {}".format(results[read_port])) + self.check_bitline_all_results(results) + + return results diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index c30d91a0..3f5ac905 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -111,7 +111,7 @@ class control_logic(design.design): replica_bitline = getattr(c, OPTS.replica_bitline) delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() - bitcell_loads = int(math.ceil(self.num_rows / 2.0)) + bitcell_loads = int(math.ceil(self.num_rows * parameter["rbl_height_percentage"])) self.replica_bitline = replica_bitline([delay_fanout_heuristic]*delay_stages_heuristic, bitcell_loads, name="replica_bitline_"+self.port_type) if self.sram != None: diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 14ce1953..83d9b280 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -328,13 +328,13 @@ spice["nand2_transition_prob"] = .1875 # Transition probability of 2-input na spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input nand. spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor. -#Logical Effort relative values for the Handmade cells +#Parameters related to sense amp enable timing and delay chain/RBL sizing parameter["dff_clk_cin"] = 30.6 #relative capacitance parameter["6tcell_wl_cin"] = 3 #relative capacitance parameter["min_inv_para_delay"] = .5 #Tau delay units parameter["sa_en_pmos_size"] = .72 #micro-meters parameter["sa_en_nmos_size"] = .27 #micro-meters - +parameter["rbl_height_percentage"] = .5 #Height of RBL compared to bitcell array ################################################### ##END Spice Simulation Parameters ################################################### diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 3b7e2142..0b4789ae 100755 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -300,6 +300,7 @@ parameter["6tcell_wl_cin"] = 2 parameter["min_inv_para_delay"] = .5 parameter["sa_en_pmos_size"] = 24*_lambda_ parameter["sa_en_nmos_size"] = 9*_lambda_ +parameter["rbl_height_percentage"] = .5 #Height of RBL compared to bitcell array ################################################### ##END Spice Simulation Parameters From e4065929c296bb51ed85867c41f61667ee77823b Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 13 Dec 2018 22:21:30 -0800 Subject: [PATCH 02/29] Added bitline threshold delay checks to delay tests. --- compiler/characterizer/bitline_delay.py | 112 +++++++++++++++++------- compiler/tests/21_hspice_delay_test.py | 14 ++- 2 files changed, 92 insertions(+), 34 deletions(-) diff --git a/compiler/characterizer/bitline_delay.py b/compiler/characterizer/bitline_delay.py index e4f992fc..6368675c 100644 --- a/compiler/characterizer/bitline_delay.py +++ b/compiler/characterizer/bitline_delay.py @@ -22,11 +22,24 @@ class bitline_delay(delay): self.period = tech.spice["feasible_period"] self.is_bitline_measure = True + def create_signal_names(self): + delay.create_signal_names(self) + self.bl_signal_names = ["Xsram.Xbank0.bl", "Xsram.Xbank0.br"] + self.sen_name = "Xsram.s_en" + def create_measurement_names(self): """Create measurement names. The names themselves currently define the type of measurement""" #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. - self.bitline_meas_names = ["bl_volt", "br_volt"] + self.bl_volt_meas_names = ["volt_bl", "volt_br"] + self.bl_delay_meas_names = ["delay_bl", "delay_br"] #only used in SPICE simulation + self.bl_delay_result_name = "delay_bl_vth" #Used in the return value + def set_probe(self,probe_address, probe_data): + """ Probe address and data can be set separately to utilize other + functions in this characterizer besides analyze.""" + delay.set_probe(self,probe_address, probe_data) + self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) + def write_delay_measures(self): """ Write the measure statements to quantify the bitline voltage at sense amp enable 50%. @@ -38,26 +51,52 @@ class bitline_delay(delay): self.sf.write("* {}\n".format(comment)) for read_port in self.targ_read_ports: - self.write_bitline_measures_read_port(read_port) + self.write_bitline_voltage_measures(read_port) + self.write_bitline_delay_measures(read_port) - def write_bitline_measures_read_port(self, port): + def write_bitline_voltage_measures(self, port): + """ + Add measurments to capture the bitline voltages at 50% Sense amp enable + """ + debug.info(2, "Measuring bitline column={}, port={}".format(self.bitline_column,port)) + if len(self.all_ports) == 1: #special naming case for single port sram bitlines + bitline_port = "" + else: + bitline_port = str(port) + + sen_port_name = "{}{}".format(self.sen_name,port) + for (measure_name, bl_signal_name) in zip(self.bl_volt_meas_names, self.bl_signal_names): + bl_port_name = "{}{}_{}".format(bl_signal_name, bitline_port, self.bitline_column) + measure_port_name = "{}{}".format(measure_name,port) + self.stim.gen_meas_find_voltage(measure_port_name, sen_port_name, bl_port_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]]) + + def write_bitline_delay_measures(self, port): """ Write the measure statements to quantify the delay and power results for a read port. """ # add measure statements for delays/slews - measure_bitline = self.get_data_bit_column_number(self.probe_address, self.probe_data) - debug.info(2, "Measuring bitline column={}".format(measure_bitline)) - for port in self.targ_read_ports: - if len(self.all_ports) == 1: #special naming case for single port sram bitlines - bitline_port = "" - else: - bitline_port = str(port) - - sen_name = "Xsram.s_en{}".format(port) - bl_name = "Xsram.Xbank0.bl{}_{}".format(bitline_port, measure_bitline) - br_name = "Xsram.Xbank0.br{}_{}".format(bitline_port, measure_bitline) - self.stim.gen_meas_find_voltage("bl_volt", sen_name, bl_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]]) - self.stim.gen_meas_find_voltage("br_volt", sen_name, br_name, .5, "RISE", self.cycle_times[self.measure_cycles[port]["read0"]]) + for (measure_name, bl_signal_name) in zip(self.bl_delay_meas_names, self.bl_signal_names): + meas_values = self.get_delay_meas_values(measure_name, bl_signal_name, port) + self.stim.gen_meas_delay(*meas_values) + + def get_delay_meas_values(self, delay_name, bitline_name, port): + """Get the values needed to generate a Spice measurement statement based on the name of the measurement.""" + if len(self.all_ports) == 1: #special naming case for single port sram bitlines + bitline_port = "" + else: + bitline_port = str(port) + + meas_name="{0}{1}".format(delay_name, port) + targ_name = "{0}{1}_{2}".format(bitline_name,bitline_port,self.bitline_column) + half_vdd = 0.5 * self.vdd_voltage + trig_val = half_vdd + targ_val = self.vdd_voltage-tech.spice["v_threshold_typical"] + trig_name = "clk{0}".format(port) + trig_dir="FALL" + targ_dir="FALL" + #Half period added to delay measurement to negative clock edge + trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2 + return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td) def gen_test_cycles_one_port(self, read_port, write_port): """Sets a list of key time-points [ns] of the waveform (each rising edge) @@ -116,16 +155,29 @@ class bitline_delay(delay): self.stim.run_sim() #running sim prodoces spice output file. for port in self.targ_read_ports: - bitlines_meas_vals = {} - for mname in self.bitline_meas_names: - bitlines_meas_vals[mname] = parse_spice_list("timing", mname) - #Check that power parsing worked. - for name, val in bitlines_meas_vals.items(): - if type(val)!=float: - debug.error("Failed to Parse Bitline Values:\n\t\t{0}".format(bitlines_meas_vals),1) #Printing the entire dict looks bad. - result[port].update(bitlines_meas_vals) + #Parse and check the voltage measurements + bl_volt_meas_dict = {} + for mname in self.bl_volt_meas_names: + mname_port = "{}{}".format(mname,port) + volt_meas_val = parse_spice_list("timing", mname_port) + if type(volt_meas_val)!=float: + debug.error("Failed to Parse Bitline Voltage:\n\t\t{0}={1}".format(mname,volt_meas_val),1) + bl_volt_meas_dict[mname] = volt_meas_val + result[port].update(bl_volt_meas_dict) + + #Parse and check the delay measurements. Intended that one measurement will fail, save the delay that did not fail. + bl_delay_meas_dict = {} + values_added = 0 #For error checking + for mname in self.bl_delay_meas_names: #Parse + mname_port = "{}{}".format(mname,port) + delay_meas_val = parse_spice_list("timing", mname_port) + if type(delay_meas_val)==float: #Only add if value is float, do not error. + bl_delay_meas_dict[self.bl_delay_result_name] = delay_meas_val * 1e9 #convert to ns + values_added+=1 + debug.check(values_added>0, "Bitline delay measurements failed in SPICE simulation.") + debug.check(values_added<2, "Both bitlines experienced a Vth drop, check simulation results.") + result[port].update(bl_delay_meas_dict) - # The delay is from the negative edge for our SRAM return (True,result) @@ -134,9 +186,9 @@ class bitline_delay(delay): for port in self.targ_read_ports: self.check_bitline_port_results(results[port]) - def check_bitline_port_results(self, port_results) + def check_bitline_port_results(self, port_results): """Performs three different checks for the bitline values: functionality, bitline swing from vdd, and differential bit swing""" - bl_volt, br_volt = port_results["bl_volt"], port_results["br_volt"] + bl_volt, br_volt = port_results["volt_bl"], port_results["volt_br"] self.check_functionality(bl_volt,br_volt) self.check_swing_from_vdd(bl_volt,br_volt) self.check_differential_swing(bl_volt,br_volt) @@ -156,7 +208,7 @@ class bitline_delay(delay): debug.info(1, "Active bitline={:.3f}v, Desired bitline={:.3f}v".format(discharge_volt,desired_bl_volt)) vdd_error_margin = .2 #20% of vdd margin for bitline, a little high for now. if abs(discharge_volt - desired_bl_volt) > vdd_error_margin*self.vdd_voltage: - debug.warning("Bitline voltage is not within {}% Vdd margin. Delay chain/RBL could need resizing.".format(vdd_error_margin)) + debug.warning("Bitline voltage is not within {}% Vdd margin. Delay chain/RBL could need resizing.".format(vdd_error_margin*100)) def check_differential_swing(self, bl_volt, br_volt): """This check looks at the difference between the bitline voltages. This needs to be large enough to prevent @@ -166,7 +218,7 @@ class bitline_delay(delay): vdd_error_margin = .2 #20% of vdd margin for bitline, a little high for now. if bitline_swing < vdd_error_margin*self.vdd_voltage: debug.warning("Bitline swing less than {}% Vdd margin. Sensing errors more likely to occur.".format(vdd_error_margin)) - + def analyze(self, probe_address, probe_data, slews, loads): """Measures the bitline swing of the differential bitlines (bl/br) at 50% s_en """ self.set_probe(probe_address, probe_data) @@ -180,7 +232,7 @@ class bitline_delay(delay): debug.info(1,"Bitline swing test: corner {}".format(self.corner)) (success, results)=self.run_delay_simulation() debug.check(success, "Bitline Failed: period {}".format(self.period)) - debug.info(1,"Bitline values (bl/br): {}".format(results[read_port])) + debug.info(1,"Bitline values (voltages/delays):\n\t {}".format(results[read_port])) self.check_bitline_all_results(results) return results diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index edbd6a55..33f0ecc2 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -48,10 +48,10 @@ class timing_sram_test(openram_test): loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] data, port_data = d.analyze(probe_address, probe_data, slews, loads) - #bitline_swing = bl.analyze(probe_address, probe_data, slews, loads) + bitline_data = bl.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data data.update(port_data[0]) - + data.update(bitline_data[0]) if OPTS.tech_name == "freepdk45": golden_data = {'delay_hl': [0.2011], 'delay_lh': [0.2011], @@ -62,7 +62,10 @@ class timing_sram_test(openram_test): 'slew_hl': [0.10853], 'slew_lh': [0.10853], 'write0_power': [0.51742], - 'write1_power': [0.51095]} + 'write1_power': [0.51095], + 'volt_bl': 0.045626, + 'volt_br': 1.0709, + 'delay_bl_vth': 0.1813} elif OPTS.tech_name == "scn4m_subm": golden_data = {'delay_hl': [1.3911], 'delay_lh': [1.3911], @@ -73,7 +76,10 @@ class timing_sram_test(openram_test): 'slew_hl': [0.7397553], 'slew_lh': [0.7397553], 'write0_power': [19.4103], - 'write1_power': [20.1167]} + 'write1_power': [20.1167], + 'volt_bl': 1.8329, + 'volt_br': 5.081, + 'delay_bl_vth': 1.1141} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results From dc20bf9e114780390aef6b65b34f02f374ed0a66 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 13 Dec 2018 22:31:08 -0800 Subject: [PATCH 03/29] Added bitline measurements to ngspice delay test. --- compiler/tests/21_hspice_delay_test.py | 1 + compiler/tests/21_ngspice_delay_test.py | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 33f0ecc2..c847210b 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -52,6 +52,7 @@ class timing_sram_test(openram_test): #Combine info about port into all data data.update(port_data[0]) data.update(bitline_data[0]) + if OPTS.tech_name == "freepdk45": golden_data = {'delay_hl': [0.2011], 'delay_lh': [0.2011], diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 20ba14cc..16c3fb3a 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -23,7 +23,7 @@ class timing_sram_test(openram_test): from importlib import reload import characterizer reload(characterizer) - from characterizer import delay + from characterizer import delay, bitline_delay from sram import sram from sram_config import sram_config c = sram_config(word_size=1, @@ -43,12 +43,15 @@ class timing_sram_test(openram_test): corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) d = delay(s.s, tempspice, corner) + bl = bitline_delay(s.s, tempspice, corner) import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] data, port_data = d.analyze(probe_address, probe_data, slews, loads) + bitline_data = bl.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data data.update(port_data[0]) + data.update(bitline_data[0]) if OPTS.tech_name == "freepdk45": golden_data = {'delay_hl': [0.20443139999999999], @@ -60,7 +63,10 @@ class timing_sram_test(openram_test): 'slew_hl': [0.1138734], 'slew_lh': [0.1138734], 'write0_power': [0.5205761], - 'write1_power': [0.5213689]} + 'write1_power': [0.5213689], + 'volt_bl': 0.03667602, + 'volt_br': 1.056013, + 'delay_bl_vth': 0.184373} elif OPTS.tech_name == "scn4m_subm": golden_data = {'delay_hl': [1.610911], 'delay_lh': [1.610911], @@ -71,7 +77,10 @@ class timing_sram_test(openram_test): 'slew_hl': [0.7986348999999999], 'slew_lh': [0.7986348999999999], 'write0_power': [17.58272], - 'write1_power': [18.523419999999998]} + 'write1_power': [18.523419999999998], + 'volt_bl': 1.639692, + 'volt_br': 5.06107, + 'delay_bl_vth': 1.322235} else: self.assertTrue(False) # other techs fail From 51b1bd46dadd007ce07127bfcbc306fdf9b5d122 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Fri, 14 Dec 2018 18:02:19 -0800 Subject: [PATCH 04/29] Added option to use delay chain size defined in tech.py --- compiler/modules/control_logic.py | 47 ++++++++++++++++++++---------- compiler/options.py | 3 ++ technology/freepdk45/tech/tech.py | 3 ++ technology/scn4m_subm/tech/tech.py | 2 ++ 4 files changed, 40 insertions(+), 15 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 3f5ac905..8545480c 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -109,24 +109,41 @@ class control_logic(design.design): from importlib import reload c = reload(__import__(OPTS.replica_bitline)) replica_bitline = getattr(c, OPTS.replica_bitline) - - delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() bitcell_loads = int(math.ceil(self.num_rows * parameter["rbl_height_percentage"])) - self.replica_bitline = replica_bitline([delay_fanout_heuristic]*delay_stages_heuristic, bitcell_loads, name="replica_bitline_"+self.port_type) - - if self.sram != None: - self.set_sen_wl_delays() - - if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #check condition based on resizing method - #This resizes to match fall and rise delays, can make the delay chain weird sizes. - # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) - # self.replica_bitline = replica_bitline(stage_list, bitcell_loads, name="replica_bitline_resized_"+self.port_type) + if OPTS.use_tech_delay_chain_size: #Use tech parameters if set. + delay_stages = parameter["static_delay_stages"] + delay_fanout = parameter["static_fanout_per_stage"] + debug.info(1, "Using tech parameters to size delay chain: stages={}, fanout={}".format(delay_stages,delay_fanout)) + self.replica_bitline = replica_bitline([delay_fanout]*delay_stages, + bitcell_loads, + name="replica_bitline_"+self.port_type) - #This resizes based on total delay. - delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) - self.replica_bitline = replica_bitline([delay_fanout]*delay_stages, bitcell_loads, name="replica_bitline_resized_"+self.port_type) + else: #Otherwise, use a heuristic and/or model based sizing. + #First use a heuristic + delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() + self.replica_bitline = replica_bitline([delay_fanout_heuristic]*delay_stages_heuristic, + bitcell_loads, + name="replica_bitline_"+self.port_type) - self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing + #Use a model to determine the delays with that heuristic + if self.sram != None: + self.set_sen_wl_delays() + + #Resize if necessary + if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #check condition based on resizing method + #This resizes to match fall and rise delays, can make the delay chain weird sizes. + # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) + # self.replica_bitline = replica_bitline(stage_list, + # bitcell_loads, + # name="replica_bitline_resized_"+self.port_type) + + #This resizes based on total delay. + delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) + self.replica_bitline = replica_bitline([delay_fanout]*delay_stages, + bitcell_loads, + name="replica_bitline_resized_"+self.port_type) + + self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing self.add_mod(self.replica_bitline) diff --git a/compiler/options.py b/compiler/options.py index bd4bf607..5293e053 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -71,6 +71,9 @@ class options(optparse.Values): # You can manually specify banks, but it is better to auto-detect it. num_banks = 1 + #Uses the delay chain size in the tech.py file rather automatic sizing. + use_tech_delay_chain_size = False + # These are the default modules that can be over-riden decoder = "hierarchical_decoder" dff_array = "dff_array" diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 83d9b280..6b8f981c 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -329,12 +329,15 @@ spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input na spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor. #Parameters related to sense amp enable timing and delay chain/RBL sizing +parameter["static_delay_stages"] = 4 +parameter["static_fanout_per_stage"] = 3 parameter["dff_clk_cin"] = 30.6 #relative capacitance parameter["6tcell_wl_cin"] = 3 #relative capacitance parameter["min_inv_para_delay"] = .5 #Tau delay units parameter["sa_en_pmos_size"] = .72 #micro-meters parameter["sa_en_nmos_size"] = .27 #micro-meters parameter["rbl_height_percentage"] = .5 #Height of RBL compared to bitcell array + ################################################### ##END Spice Simulation Parameters ################################################### diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index 0b4789ae..fe2d686a 100755 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -295,6 +295,8 @@ spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input na spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor. #Logical Effort relative values for the Handmade cells +parameter["static_delay_stages"] = 4 +parameter["static_fanout_per_stage"] = 3 parameter["dff_clk_cin"] = 27.5 parameter["6tcell_wl_cin"] = 2 parameter["min_inv_para_delay"] = .5 From 8eb4812e166571db8892ac64652fc5bc7a3fa985 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 17 Dec 2018 23:32:02 -0800 Subject: [PATCH 05/29] Made parasitic delay parameter in Freepdk45 more accurate, added stage names to delay model. --- compiler/characterizer/logical_effort.py | 14 ++++++++++---- compiler/pgates/pinv.py | 2 +- compiler/pgates/pnand2.py | 2 +- compiler/pgates/pnand3.py | 3 ++- technology/freepdk45/tech/tech.py | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/compiler/characterizer/logical_effort.py b/compiler/characterizer/logical_effort.py index bf8c1585..bbb0bd77 100644 --- a/compiler/characterizer/logical_effort.py +++ b/compiler/characterizer/logical_effort.py @@ -10,7 +10,8 @@ class logical_effort(): min_inv_cin = 1+beta pinv=parameter["min_inv_para_delay"] - def __init__(self, size, cin, cout, parasitic, out_is_rise=True): + def __init__(self, name, size, cin, cout, parasitic, out_is_rise=True): + self.name = name self.cin = cin self.cout = cout self.logical_effort = (self.cin/size)/logical_effort.min_inv_cin @@ -19,8 +20,13 @@ class logical_effort(): self.is_rise = out_is_rise def __str__(self): - return "g=" + str(self.logical_effort) + ", h=" + str(self.eletrical_effort) + ", p=" + str(self.parasitic_scale)+"*pinv, rise_delay="+str(self.is_rise) - + return "Name={}, g={}, h={}, p={}*pinv, rise_delay={}".format(self.name, + self.logical_effort, + self.eletrical_effort, + self.parasitic_scale, + self.is_rise + ) + def get_stage_effort(self): return self.logical_effort*self.eletrical_effort @@ -40,7 +46,7 @@ def calculate_relative_rise_fall_delays(stage_effort_list, pinv=parameter["min_i debug.info(2, "Calculating rise/fall relative delays") total_rise_delay, total_fall_delay = 0,0 for stage in stage_effort_list: - debug.info(3, stage) + debug.info(2, stage) if stage.is_rise: total_rise_delay += stage.get_stage_delay(pinv) else: diff --git a/compiler/pgates/pinv.py b/compiler/pgates/pinv.py index 31682360..b1e1f033 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/pgates/pinv.py @@ -293,4 +293,4 @@ class pinv(pgate.pgate): Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ parasitic_delay = 1 - return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) + return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) diff --git a/compiler/pgates/pnand2.py b/compiler/pgates/pnand2.py index e767b87e..c9709864 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/pgates/pnand2.py @@ -260,4 +260,4 @@ class pnand2(pgate.pgate): Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ parasitic_delay = 2 - return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) \ No newline at end of file + return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) \ No newline at end of file diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 4dab5264..d159a27e 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -5,6 +5,7 @@ from tech import drc, parameter, spice from ptx import ptx from vector import vector from globals import OPTS +import logical_effort class pnand3(pgate.pgate): """ @@ -272,4 +273,4 @@ class pnand3(pgate.pgate): Optional is_rise refers to the input direction rise/fall. Input inverted by this stage. """ parasitic_delay = 3 - return logical_effort.logical_effort(self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) \ No newline at end of file + return logical_effort.logical_effort(self.name, self.size, self.get_cin(), cout, parasitic_delay, not inp_is_rise) \ No newline at end of file diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index 6b8f981c..ff9b5169 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -333,7 +333,7 @@ parameter["static_delay_stages"] = 4 parameter["static_fanout_per_stage"] = 3 parameter["dff_clk_cin"] = 30.6 #relative capacitance parameter["6tcell_wl_cin"] = 3 #relative capacitance -parameter["min_inv_para_delay"] = .5 #Tau delay units +parameter["min_inv_para_delay"] = 2.4 #Tau delay units parameter["sa_en_pmos_size"] = .72 #micro-meters parameter["sa_en_nmos_size"] = .27 #micro-meters parameter["rbl_height_percentage"] = .5 #Height of RBL compared to bitcell array From b10ef3fb7e5038a67c5ad9f187fe1099ec4ba11e Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Dec 2018 18:33:06 -0800 Subject: [PATCH 06/29] Replaced delay measure statement with object implementation. --- compiler/characterizer/__init__.py | 1 + compiler/characterizer/delay.py | 31 ++++++++- compiler/characterizer/measurements.py | 92 ++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 compiler/characterizer/measurements.py diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index 4a2f161e..009459b3 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -9,6 +9,7 @@ from .functional import * from .worst_case import * from .simulation import * from .bitline_delay import * +from .measurements import * debug.info(1,"Initializing characterizer...") OPTS.spice_exe = "" diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 9e0543d1..d04ccf50 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -8,6 +8,7 @@ from .charutils import * import utils from globals import OPTS from .simulation import simulation +from .measurements import * class delay(simulation): """Functions to measure the delay and power of an SRAM at a given address and @@ -45,6 +46,16 @@ class delay(simulation): #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] + + def create_measurement_objects(self): + self.meas_objs = [] + trig_delay_name = "clk{0}" + targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit + self.meas_objs.append(delay_measure("delay_lh{}", trig_delay_name, targ_name, "RISE", "RISE")) + self.meas_objs.append(delay_measure("delay_hl{}", trig_delay_name, targ_name, "FALL", "FALL")) + + self.meas_objs.append(slew_measure("slew_lh{}", targ_name, "RISE")) + self.meas_objs.append(slew_measure("slew_hl{}", targ_name, "FALL")) def create_signal_names(self): self.addr_name = "A" @@ -235,15 +246,28 @@ class delay(simulation): else: debug.error(1, "Measure command {0} not recognized".format(delay_name)) return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td) + + def get_measure_variants(self, port, measure_obj): + """Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" + #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port + #vdd is arguably constant as that is true for a single lib file. + if measure_obj.targ_dir_str == "FALL": + meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + elif measure_obj.targ_dir_str == "RISE": + meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read1"]] + else: + debug.error("Unrecognised measurement direction={}".format(measure_obj.targ_dir_str),1) + + return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) def write_delay_measures_read_port(self, port): """ Write the measure statements to quantify the delay and power results for a read port. """ # add measure statements for delays/slews - for dname in self.delay_meas_names: - meas_values = self.get_delay_meas_values(dname, port) - self.stim.gen_meas_delay(*meas_values) + for measure in self.meas_objs: + measure_variant_tuple = self.get_measure_variants(port, measure) + measure.write_measure(self.stim, measure_variant_tuple) # add measure statements for power for pname in self.power_meas_names: @@ -645,6 +669,7 @@ class delay(simulation): char_sram_data = {} self.set_probe(probe_address, probe_data) + self.create_measurement_objects() self.load=max(loads) self.slew=max(slews) diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py new file mode 100644 index 00000000..2822183c --- /dev/null +++ b/compiler/characterizer/measurements.py @@ -0,0 +1,92 @@ +import debug +from tech import drc, parameter, spice +from abc import ABC, abstractmethod +from .stimuli import * + +class spice_measurement(ABC): + """Base class for spice stimulus measurements.""" + def __init__(self, measure_name): + #Names must be unique for correct spice simulation, but not enforced here. + self.name = measure_name + + @abstractmethod + def get_measure_function(self): + return None + + @abstractmethod + def get_measure_values(self): + return None + + def write_measure(self, stim_obj, input_tuple): + measure_func = self.get_measure_function() + if measure_func == None: + debug.error("Did not set measure function",1) + measure_vals = self.get_measure_values(*input_tuple) + measure_func(stim_obj, *measure_vals) + + +class delay_measure(spice_measurement): + """Generates a spice measurement for the delay of 50%-to-50% points of two signals.""" + + def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str): + spice_measurement.__init__(self, measure_name) + self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str) + + def get_measure_function(self): + return stimuli.gen_meas_delay + + def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str): + """Set the values needed to generate a Spice measurement statement based on the name of the measurement.""" + self.trig_dir_str = trig_dir_str + self.targ_dir_str = targ_dir_str + + self.trig_val_of_vdd = 0.5 + self.targ_val_of_vdd = 0.5 + + self.trig_name_no_port = trig_name + self.targ_name_no_port = targ_name + + #Time delays and ports are variant and needed as inputs when writing the measurement + + def get_measure_values(self, trig_td, targ_td, vdd_voltage, port=None): + """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" + trig_val = self.trig_val_of_vdd * vdd_voltage + targ_val = self.targ_val_of_vdd * vdd_voltage + + if port != None: + meas_name = self.name.format(port) + trig_name = self.trig_name_no_port.format(port) + targ_name = self.targ_name_no_port.format(port) + else: + meas_name = self.name + trig_name = self.trig_name_no_port + targ_name = self.targ_name_no_port + + return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td) + + +class slew_measure(delay_measure): + + def __init__(self, measure_name, signal_name, slew_dir_str): + spice_measurement.__init__(self, measure_name) + self.set_meas_constants(signal_name, slew_dir_str) + + def set_meas_constants(self, signal_name, slew_dir_str): + """Set the values needed to generate a Spice measurement statement based on the name of the measurement.""" + self.trig_dir_str = slew_dir_str + self.targ_dir_str = slew_dir_str + + if slew_dir_str == "RISE": + self.trig_val_of_vdd = 0.1 + self.targ_val_of_vdd = 0.9 + elif slew_dir_str == "FALL": + self.trig_val_of_vdd = 0.9 + self.targ_val_of_vdd = 0.1 + else: + debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str),1) + + self.trig_name_no_port = signal_name + self.targ_name_no_port = signal_name + + #Time delays and ports are variant and needed as inputs when writing the measurement + \ No newline at end of file From 66b2fcdc91401e553ac494853f0b50b3dfe0f36a Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 20 Dec 2018 15:54:56 -0800 Subject: [PATCH 07/29] Added data parsing to measurement objects and adding power measurements. --- compiler/characterizer/charutils.py | 7 + compiler/characterizer/delay.py | 190 +++++++++++-------------- compiler/characterizer/measurements.py | 52 +++++-- 3 files changed, 135 insertions(+), 114 deletions(-) diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index a2140e51..061972cf 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -80,3 +80,10 @@ def convert_to_float(number): debug.error("Invalid number: {0}".format(number),1) return float_value + +def check_dict_values_is_float(dict): + """Checks if all the values are floats. Useful for checking failed Spice measurements.""" + for key, value in dict.items(): + if type(value)!=float: + return False + return True \ No newline at end of file diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index d04ccf50..7f25348b 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -48,15 +48,31 @@ class delay(simulation): self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] def create_measurement_objects(self): - self.meas_objs = [] + """Create the measurements used for read and write ports""" + self.create_read_port_measurement_objects() + self.create_write_port_measurement_objects() + + def create_read_port_measurement_objects(self): + """Create the measurements used for read ports: delays, slews, powers""" + self.read_meas_objs = [] trig_delay_name = "clk{0}" targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit - self.meas_objs.append(delay_measure("delay_lh{}", trig_delay_name, targ_name, "RISE", "RISE")) - self.meas_objs.append(delay_measure("delay_hl{}", trig_delay_name, targ_name, "FALL", "FALL")) + self.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", 1e9)) + self.read_meas_objs.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", 1e9)) - self.meas_objs.append(slew_measure("slew_lh{}", targ_name, "RISE")) - self.meas_objs.append(slew_measure("slew_hl{}", targ_name, "FALL")) + self.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", 1e9)) + self.read_meas_objs.append(slew_measure("slew_hl", targ_name, "FALL", 1e9)) + self.read_meas_objs.append(power_measure("read1_power", "RISE", 1e3)) + self.read_meas_objs.append(power_measure("read0_power", "FALL", 1e3)) + + def create_write_port_measurement_objects(self): + """Create the measurements used for read ports: delays, slews, powers""" + self.write_meas_objs = [] + + self.write_meas_objs.append(power_measure("write1_power", "RISE", 1e3)) + self.write_meas_objs.append(power_measure("write0_power", "FALL", 1e3)) + def create_signal_names(self): self.addr_name = "A" self.din_name = "DIN" @@ -209,99 +225,68 @@ class delay(simulation): self.sf.close() - def get_delay_meas_values(self, delay_name, port): - """Get the values needed to generate a Spice measurement statement based on the name of the measurement.""" - debug.check('lh' in delay_name or 'hl' in delay_name, "Measure command {0} does not contain direction (lh/hl)") - trig_clk_name = "clk{0}".format(port) - meas_name="{0}{1}".format(delay_name, port) - targ_name = "{0}".format("{0}{1}_{2}".format(self.dout_name,port,self.probe_data)) - half_vdd = 0.5 * self.vdd_voltage - trig_slew_low = 0.1 * self.vdd_voltage - targ_slew_high = 0.9 * self.vdd_voltage - if 'delay' in delay_name: - trig_val = half_vdd - targ_val = half_vdd - trig_name = trig_clk_name - if 'lh' in delay_name: - trig_dir="RISE" - targ_dir="RISE" - trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read1"]] - else: - trig_dir="FALL" - targ_dir="FALL" - trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] - - elif 'slew' in delay_name: - trig_name = targ_name - if 'lh' in delay_name: - trig_val = trig_slew_low - targ_val = targ_slew_high - targ_dir = trig_dir = "RISE" - trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read1"]] - else: - trig_val = targ_slew_high - targ_val = trig_slew_low - targ_dir = trig_dir = "FALL" - trig_td = targ_td = self.cycle_times[self.measure_cycles[port]["read0"]] + def get_read_measure_variants(self, port, measure_obj): + """Checks the measurement object and calls respective function for related measurement inputs.""" + meas_type = type(measure_obj) + if meas_type is delay_measure or meas_type is slew_measure: + return self.get_delay_measure_variants(port, measure_obj) + elif meas_type is power_measure: + return self.get_power_measure_variants(port, measure_obj, "read") else: - debug.error(1, "Measure command {0} not recognized".format(delay_name)) - return (meas_name,trig_name,targ_name,trig_val,targ_val,trig_dir,targ_dir,trig_td,targ_td) - - def get_measure_variants(self, port, measure_obj): + debug.error("Input function not defined for measurement type={}".format(meas_type)) + + def get_delay_measure_variants(self, port, delay_obj): """Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port #vdd is arguably constant as that is true for a single lib file. - if measure_obj.targ_dir_str == "FALL": + if delay_obj.targ_dir_str == "FALL": meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] - elif measure_obj.targ_dir_str == "RISE": + elif delay_obj.targ_dir_str == "RISE": meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read1"]] else: - debug.error("Unrecognised measurement direction={}".format(measure_obj.targ_dir_str),1) + debug.error("Unrecognised measurement direction={}".format(delay_obj.targ_dir_str),1) return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) - + + def get_power_measure_variants(self, port, power_obj, operation): + """Get the measurement values that can either vary port to port (time delays)""" + #Return value is intended to match the power measure format: t_initial, t_final, port + if power_obj.power_type == "FALL": + t_initial = self.cycle_times[self.measure_cycles[port]["{}0".format(operation)]] + t_final = self.cycle_times[self.measure_cycles[port]["{}0".format(operation)]+1] + elif power_obj.power_type == "RISE": + t_initial = self.cycle_times[self.measure_cycles[port]["{}1".format(operation)]] + t_final = self.cycle_times[self.measure_cycles[port]["{}1".format(operation)]+1] + else: + debug.error("Unrecognised power measurement type={}".format(power_obj.power_type),1) + + return (t_initial, t_final, port) + def write_delay_measures_read_port(self, port): """ Write the measure statements to quantify the delay and power results for a read port. """ # add measure statements for delays/slews - for measure in self.meas_objs: - measure_variant_tuple = self.get_measure_variants(port, measure) - measure.write_measure(self.stim, measure_variant_tuple) + for measure in self.read_meas_objs: + measure_variant_inp_tuple = self.get_read_measure_variants(port, measure) + measure.write_measure(self.stim, measure_variant_inp_tuple) + + def get_write_measure_variants(self, port, measure_obj): + """Checks the measurement object and calls respective function for related measurement inputs.""" + meas_type = type(measure_obj) + if meas_type is power_measure: + return self.get_power_measure_variants(port, measure_obj, "write") + else: + debug.error("Input function not defined for measurement type={}".format(meas_type)) - # add measure statements for power - for pname in self.power_meas_names: - if "read" not in pname: - continue - #Different naming schemes are used for the measure cycle dict and measurement names. - #TODO: make them the same so they can be indexed the same. - if '1' in pname: - t_initial = self.cycle_times[self.measure_cycles[port]["read1"]] - t_final = self.cycle_times[self.measure_cycles[port]["read1"]+1] - elif '0' in pname: - t_initial = self.cycle_times[self.measure_cycles[port]["read0"]] - t_final = self.cycle_times[self.measure_cycles[port]["read0"]+1] - self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port), - t_initial=t_initial, - t_final=t_final) - def write_delay_measures_write_port(self, port): """ Write the measure statements to quantify the power results for a write port. """ # add measure statements for power - for pname in self.power_meas_names: - if "write" not in pname: - continue - t_initial = self.cycle_times[self.measure_cycles[port]["write0"]] - t_final = self.cycle_times[self.measure_cycles[port]["write0"]+1] - if '1' in pname: - t_initial = self.cycle_times[self.measure_cycles[port]["write1"]] - t_final = self.cycle_times[self.measure_cycles[port]["write1"]+1] - - self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port), - t_initial=t_initial, - t_final=t_final) + for measure in self.write_meas_objs: + measure_variant_inp_tuple = self.get_read_measure_variants(port, measure) + measure.write_measure(self.stim, measure_variant_inp_tuple) def write_delay_measures(self): """ @@ -451,28 +436,27 @@ class delay(simulation): #Too much duplicate code here. Try reducing for port in self.targ_read_ports: debug.info(2, "Check delay values for port {}".format(port)) - delay_names = [mname for mname in self.delay_meas_names] - delays = self.parse_values(delay_names, port, 1e9) # scale delays to ns - if not self.check_valid_delays(delays): - return (False,{}) - result[port].update(delays) + read_port_dict = {} + #Get measurements from output file + for measure in self.read_meas_objs: + read_port_dict[measure.name] = measure.retrieve_measure(port=port) - power_names = [mname for mname in self.power_meas_names if 'read' in mname] - powers = self.parse_values(power_names, port, 1e3) # scale power to mw - #Check that power parsing worked. - for name, power in powers.items(): - if type(power)!=float: - debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad. - result[port].update(powers) - + #Check timing for read ports. Power is only checked if it was read correctly + if not self.check_valid_delays(read_port_dict): + 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) #Printing the entire dict looks bad. + + result[port].update(read_port_dict) + for port in self.targ_write_ports: - power_names = [mname for mname in self.power_meas_names if 'write' in mname] - powers = self.parse_values(power_names, port, 1e3) # scale power to mw - #Check that power parsing worked. - for name, power in powers.items(): - if type(power)!=float: - debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad. - result[port].update(powers) + write_port_dict = {} + for measure in self.read_meas_objs: + 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) #Printing the entire dict looks bad. + result[port].update(write_port_dict) # The delay is from the negative edge for our SRAM return (True,result) @@ -502,13 +486,13 @@ class delay(simulation): #key=raw_input("press return to continue") return (leakage_power*1e3, trim_leakage_power*1e3) - def check_valid_delays(self, delay_dict): + def check_valid_delays(self, result_dict): """ Check if the measurements are defined and if they are valid. """ #Hard coded names currently - delay_hl = delay_dict["delay_hl"] - delay_lh = delay_dict["delay_lh"] - slew_hl = delay_dict["slew_hl"] - slew_lh = delay_dict["slew_lh"] + delay_hl = result_dict["delay_hl"] + 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) # if it failed or the read was longer than a period diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py index 2822183c..f065ac70 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -2,13 +2,15 @@ import debug from tech import drc, parameter, spice from abc import ABC, abstractmethod from .stimuli import * +from .charutils import * class spice_measurement(ABC): """Base class for spice stimulus measurements.""" - def __init__(self, measure_name): + def __init__(self, measure_name, measure_scale=None): #Names must be unique for correct spice simulation, but not enforced here. self.name = measure_name - + self.measure_scale = measure_scale + @abstractmethod def get_measure_function(self): return None @@ -23,13 +25,19 @@ class spice_measurement(ABC): debug.error("Did not set measure function",1) measure_vals = self.get_measure_values(*input_tuple) measure_func(stim_obj, *measure_vals) - + + def retrieve_measure(self, port=""): + value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port)) + if type(value)!=float or self.measure_scale == None: + return value + else: + return value*self.measure_scale class delay_measure(spice_measurement): """Generates a spice measurement for the delay of 50%-to-50% points of two signals.""" - def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str): - spice_measurement.__init__(self, measure_name) + def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str, measure_scale=None): + spice_measurement.__init__(self, measure_name, measure_scale) self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str) def get_measure_function(self): @@ -54,7 +62,8 @@ class delay_measure(spice_measurement): targ_val = self.targ_val_of_vdd * vdd_voltage if port != None: - meas_name = self.name.format(port) + #For dictionary indexing reasons, the name is formatted differently than the signals + meas_name = "{}{}".format(self.name, port) trig_name = self.trig_name_no_port.format(port) targ_name = self.targ_name_no_port.format(port) else: @@ -63,12 +72,11 @@ class delay_measure(spice_measurement): targ_name = self.targ_name_no_port return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td) - - + class slew_measure(delay_measure): - def __init__(self, measure_name, signal_name, slew_dir_str): - spice_measurement.__init__(self, measure_name) + def __init__(self, measure_name, signal_name, slew_dir_str, measure_scale=None): + spice_measurement.__init__(self, measure_name, measure_scale) self.set_meas_constants(signal_name, slew_dir_str) def set_meas_constants(self, signal_name, slew_dir_str): @@ -89,4 +97,26 @@ class slew_measure(delay_measure): self.targ_name_no_port = signal_name #Time delays and ports are variant and needed as inputs when writing the measurement - \ No newline at end of file + +class power_measure(spice_measurement): + """Generates a spice measurement for the delay of 50%-to-50% points of two signals.""" + + def __init__(self, measure_name, power_type="", measure_scale=None): + spice_measurement.__init__(self, measure_name, measure_scale) + self.set_meas_constants(power_type) + + def get_measure_function(self): + return stimuli.gen_meas_power + + def set_meas_constants(self, power_type): + """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" + #Not needed for power simulation + self.power_type = power_type #Expected to be "RISE"/"FALL" + + def get_measure_values(self, t_initial, t_final, port=None): + """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" + if port != None: + meas_name = "{}{}".format(self.name, port) + else: + meas_name = self.name + return (meas_name,t_initial,t_final) \ No newline at end of file From 272267358f16eac7ffc689718814251a32f6306f Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 3 Jan 2019 05:51:28 -0800 Subject: [PATCH 08/29] Moved all bitline delay measurements to delay class. Added measurements to check delay model. --- compiler/characterizer/__init__.py | 1 + compiler/characterizer/bitline_delay.py | 19 +++ compiler/characterizer/delay.py | 156 +++++++++++++------- compiler/characterizer/logical_effort.py | 4 + compiler/characterizer/measurements.py | 52 +++++-- compiler/characterizer/model_check.py | 174 +++++++++++++++++++++++ compiler/modules/control_logic.py | 22 ++- compiler/tests/21_hspice_delay_test.py | 29 ++-- compiler/tests/21_ngspice_delay_test.py | 39 +++-- compiler/tests/28_delay_model_test.py | 61 ++++++++ 10 files changed, 456 insertions(+), 101 deletions(-) create mode 100644 compiler/characterizer/model_check.py create mode 100755 compiler/tests/28_delay_model_test.py diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index 009459b3..cbcf0a1a 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -10,6 +10,7 @@ from .worst_case import * from .simulation import * from .bitline_delay import * from .measurements import * +from .model_check import * debug.info(1,"Initializing characterizer...") OPTS.spice_exe = "" diff --git a/compiler/characterizer/bitline_delay.py b/compiler/characterizer/bitline_delay.py index 6368675c..2af290f1 100644 --- a/compiler/characterizer/bitline_delay.py +++ b/compiler/characterizer/bitline_delay.py @@ -26,7 +26,26 @@ class bitline_delay(delay): delay.create_signal_names(self) self.bl_signal_names = ["Xsram.Xbank0.bl", "Xsram.Xbank0.br"] self.sen_name = "Xsram.s_en" + + def create_measurement_objects(self): + """Create the measurements used for read and write ports""" + self.meas_objs = [] + self.create_bitline_find_measurement_objects() + self.create_bitline_delay_measurement_objects() + + def create_bitline_delay_measurement_objects(self): + self.find_meas_objs = [] + trig_delay_name = "clk{0}" + targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit + self.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", 1e9)) + self.read_meas_objs.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", 1e9)) + self.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", 1e9)) + self.read_meas_objs.append(slew_measure("slew_hl", targ_name, "FALL", 1e9)) + + self.read_meas_objs.append(power_measure("read1_power", "RISE", 1e3)) + self.read_meas_objs.append(power_measure("read0_power", "FALL", 1e3)) + def create_measurement_names(self): """Create measurement names. The names themselves currently define the type of measurement""" #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 7f25348b..a327c548 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -46,32 +46,77 @@ class delay(simulation): #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] + self.voltage_when_names = ["volt_bl", "volt_br"] + self.bitline_delay_names = ["delay_bl", "delay_br"] def create_measurement_objects(self): """Create the measurements used for read and write ports""" self.create_read_port_measurement_objects() self.create_write_port_measurement_objects() - + def create_read_port_measurement_objects(self): """Create the measurements used for read ports: delays, slews, powers""" + self.read_meas_objs = [] trig_delay_name = "clk{0}" targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit - self.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", 1e9)) - self.read_meas_objs.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", 1e9)) + self.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", measure_scale=1e9)) + self.read_meas_objs[-1].meta_str = "read1" #Used to index time delay values when measurements written to spice file. + self.read_meas_objs.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", measure_scale=1e9)) + self.read_meas_objs[-1].meta_str = "read0" - self.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", 1e9)) - self.read_meas_objs.append(slew_measure("slew_hl", targ_name, "FALL", 1e9)) + self.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", measure_scale=1e9)) + self.read_meas_objs[-1].meta_str = "read1" + self.read_meas_objs.append(slew_measure("slew_hl", targ_name, "FALL", measure_scale=1e9)) + self.read_meas_objs[-1].meta_str = "read0" - self.read_meas_objs.append(power_measure("read1_power", "RISE", 1e3)) - self.read_meas_objs.append(power_measure("read0_power", "FALL", 1e3)) - + self.read_meas_objs.append(power_measure("read1_power", "RISE", measure_scale=1e3)) + self.read_meas_objs[-1].meta_str = "read1" + self.read_meas_objs.append(power_measure("read0_power", "FALL", measure_scale=1e3)) + self.read_meas_objs[-1].meta_str = "read0" + + trig_name = "Xsram.s_en{}" #Sense amp enable + if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name + port_format = "" + else: + port_format = "{}" + + bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column) + br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column) + self.read_meas_objs.append(voltage_when_measure(self.voltage_when_names[0], trig_name, bl_name, "RISE", .5)) + self.read_meas_objs.append(voltage_when_measure(self.voltage_when_names[1], trig_name, br_name, "RISE", .5)) + + #These are read values but need to be separated for unique error checking. + self.create_bitline_delay_measurement_objects() + + def create_bitline_delay_measurement_objects(self): + """Create the measurements used for bitline delay values. Due to unique error checking, these are separated from other measurements. + These measurements are only associated with read values + """ + self.bitline_delay_objs = [] + trig_name = "clk{0}" + if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name + port_format = "" + else: + port_format = "{}" + bl_name = "Xsram.Xbank0.bl{}_{}".format(port_format, self.bitline_column) + br_name = "Xsram.Xbank0.br{}_{}".format(port_format, self.bitline_column) + targ_val = (self.vdd_voltage - tech.spice["v_threshold_typical"])/self.vdd_voltage #Calculate as a percentage of vdd + + targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit + self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[0], trig_name, bl_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9)) + self.bitline_delay_objs[-1].meta_str = "read0" + self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[1], trig_name, br_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9)) + self.bitline_delay_objs[-1].meta_str = "read1" + def create_write_port_measurement_objects(self): """Create the measurements used for read ports: delays, slews, powers""" self.write_meas_objs = [] - self.write_meas_objs.append(power_measure("write1_power", "RISE", 1e3)) - self.write_meas_objs.append(power_measure("write0_power", "FALL", 1e3)) + self.write_meas_objs.append(power_measure("write1_power", "RISE", measure_scale=1e3)) + self.write_meas_objs[-1].meta_str = "read1" + self.write_meas_objs.append(power_measure("write0_power", "FALL", measure_scale=1e3)) + self.write_meas_objs[-1].meta_str = "write0" def create_signal_names(self): self.addr_name = "A" @@ -232,6 +277,8 @@ class delay(simulation): return self.get_delay_measure_variants(port, measure_obj) elif meas_type is power_measure: return self.get_power_measure_variants(port, measure_obj, "read") + elif meas_type is voltage_when_measure: + return self.get_volt_when_measure_variants(port, measure_obj) else: debug.error("Input function not defined for measurement type={}".format(meas_type)) @@ -239,35 +286,37 @@ class delay(simulation): """Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port #vdd is arguably constant as that is true for a single lib file. - if delay_obj.targ_dir_str == "FALL": - meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] - elif delay_obj.targ_dir_str == "RISE": - meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read1"]] + if delay_obj.meta_str == "read0": + #Falling delay are measured starting from neg. clk edge. Delay adjusted to that. + meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] + self.period/2 + elif delay_obj.meta_str == "read1": + meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] else: - debug.error("Unrecognised measurement direction={}".format(delay_obj.targ_dir_str),1) + debug.error("Unrecognised delay Index={}".format(delay_obj.meta_str),1) return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) def get_power_measure_variants(self, port, power_obj, operation): """Get the measurement values that can either vary port to port (time delays)""" #Return value is intended to match the power measure format: t_initial, t_final, port - if power_obj.power_type == "FALL": - t_initial = self.cycle_times[self.measure_cycles[port]["{}0".format(operation)]] - t_final = self.cycle_times[self.measure_cycles[port]["{}0".format(operation)]+1] - elif power_obj.power_type == "RISE": - t_initial = self.cycle_times[self.measure_cycles[port]["{}1".format(operation)]] - t_final = self.cycle_times[self.measure_cycles[port]["{}1".format(operation)]+1] - else: - debug.error("Unrecognised power measurement type={}".format(power_obj.power_type),1) - + 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] + return (t_initial, t_final, port) + def get_volt_when_measure_variants(self, port, power_obj): + """Get the measurement values that can either vary port to port (time delays)""" + #Only checking 0 value reads for now. + t_trig = meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + + return (t_trig, self.vdd_voltage, port) + def write_delay_measures_read_port(self, port): """ Write the measure statements to quantify the delay and power results for a read port. """ # add measure statements for delays/slews - for measure in self.read_meas_objs: + for measure in self.read_meas_objs+self.bitline_delay_objs: measure_variant_inp_tuple = self.get_read_measure_variants(port, measure) measure.write_measure(self.stim, measure_variant_inp_tuple) @@ -285,7 +334,7 @@ class delay(simulation): """ # add measure statements for power for measure in self.write_meas_objs: - measure_variant_inp_tuple = self.get_read_measure_variants(port, measure) + measure_variant_inp_tuple = self.get_write_measure_variants(port, measure) measure.write_measure(self.stim, measure_variant_inp_tuple) def write_delay_measures(self): @@ -394,28 +443,7 @@ class delay(simulation): previous_period = self.period debug.info(1, "Found feasible_period: {0}ns".format(self.period)) return feasible_delays - - - def parse_values(self, values_names, port, mult = 1.0): - """Parse multiple values in the timing output file. Optional multiplier. - Return a dict of the input names and values. Port used for parsing file. - """ - values = [] - all_values_floats = True - for vname in values_names: - #ngspice converts all measure characters to lowercase, not tested on other sims - value = parse_spice_list("timing", "{0}{1}".format(vname.lower(), port)) - #Check if any of the values fail to parse - if type(value)!=float: - all_values_floats = False - values.append(value) - - #Apply Multiplier only if all values are floats. Let other check functions handle this error. - if all_values_floats: - return {values_names[i]:values[i]*mult for i in range(len(values))} - else: - return {values_names[i]:values[i] for i in range(len(values))} - + def run_delay_simulation(self): """ This tries to simulate a period and checks if the result works. If @@ -448,10 +476,13 @@ class delay(simulation): debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) #Printing the entire dict looks bad. result[port].update(read_port_dict) - + + bitline_delay_dict = self.evaluate_bitline_delay(port) + result[port].update(bitline_delay_dict) + for port in self.targ_write_ports: write_port_dict = {} - for measure in self.read_meas_objs: + for measure in self.write_meas_objs: write_port_dict[measure.name] = measure.retrieve_measure(port=port) if not check_dict_values_is_float(write_port_dict): @@ -461,7 +492,17 @@ class delay(simulation): # The delay is from the negative edge for our SRAM return (True,result) - + def evaluate_bitline_delay(self, port): + """Parse and check the bitline delay. One of the measurements is expected to fail which warrants its own function.""" + bl_delay_meas_dict = {} + values_added = 0 #For error checking + for measure in self.bitline_delay_objs: + bl_delay_val = measure.retrieve_measure(port=port) + if type(bl_delay_val) != float or 0 > bl_delay_val or bl_delay_val > self.period/2: #Only add if value is valid, do not error. + debug.error("Bitline delay measurement failed: half-period={}, {}={}".format(self.period/2, measure.name, bl_delay_val),1) + bl_delay_meas_dict[measure.name] = bl_delay_val + return bl_delay_meas_dict + def run_power_simulation(self): """ This simulates a disabled SRAM to get the leakage power when it is off. @@ -618,9 +659,18 @@ class delay(simulation): functions in this characterizer besides analyze.""" self.probe_address = probe_address self.probe_data = probe_data - + self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) + self.prepare_netlist() + def get_data_bit_column_number(self, probe_address, probe_data): + """Calculates bitline column number of data bit under test using bit position and mux size""" + if self.sram.col_addr_size>0: + col_address = int(probe_address[0:self.sram.col_addr_size],2) + else: + col_address = 0 + bl_column = int(self.sram.words_per_row*probe_data + col_address) + return bl_column def prepare_netlist(self): """ Prepare a trimmed netlist and regular netlist. """ @@ -899,7 +949,7 @@ class delay(simulation): def get_empty_measure_data_dict(self): """Make a dict of lists for each type of delay and power measurement to append results to""" - measure_names = self.delay_meas_names + self.power_meas_names + measure_names = self.delay_meas_names + self.power_meas_names + self.voltage_when_names + self.bitline_delay_names #Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. measure_data = [{mname:[] for mname in measure_names} for i in self.all_ports] return measure_data diff --git a/compiler/characterizer/logical_effort.py b/compiler/characterizer/logical_effort.py index bbb0bd77..c80e69a2 100644 --- a/compiler/characterizer/logical_effort.py +++ b/compiler/characterizer/logical_effort.py @@ -35,6 +35,10 @@ class logical_effort(): def get_stage_delay(self, pinv): return self.get_stage_effort()+self.get_parasitic_delay(pinv) + +def calculate_delays(stage_effort_list, pinv): + """Convert stage effort objects to list of delay values""" + return [stage.get_stage_delay(pinv) for stage in stage_effort_list] def calculate_relative_delay(stage_effort_list, pinv=parameter["min_inv_para_delay"]): """Calculates the total delay of a given delay path made of a list of logical effort objects.""" diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py index f065ac70..aec4d769 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -10,6 +10,7 @@ class spice_measurement(ABC): #Names must be unique for correct spice simulation, but not enforced here. self.name = measure_name self.measure_scale = measure_scale + self.meta_str = None #Some measurements set this, set here to be clear on existence @abstractmethod def get_measure_function(self): @@ -36,20 +37,20 @@ class spice_measurement(ABC): class delay_measure(spice_measurement): """Generates a spice measurement for the delay of 50%-to-50% points of two signals.""" - def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str, measure_scale=None): + def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd=0.5, targ_vdd=0.5, measure_scale=None): spice_measurement.__init__(self, measure_name, measure_scale) - self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str) + self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd) def get_measure_function(self): return stimuli.gen_meas_delay - def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str): - """Set the values needed to generate a Spice measurement statement based on the name of the measurement.""" + def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd): + """Set the constants for this measurement: signal names, directions, and trigger scales""" self.trig_dir_str = trig_dir_str self.targ_dir_str = targ_dir_str - self.trig_val_of_vdd = 0.5 - self.targ_val_of_vdd = 0.5 + self.trig_val_of_vdd = trig_vdd + self.targ_val_of_vdd = targ_vdd self.trig_name_no_port = trig_name self.targ_name_no_port = targ_name @@ -99,7 +100,7 @@ class slew_measure(delay_measure): #Time delays and ports are variant and needed as inputs when writing the measurement class power_measure(spice_measurement): - """Generates a spice measurement for the delay of 50%-to-50% points of two signals.""" + """Generates a spice measurement for the average power between two time points.""" def __init__(self, measure_name, power_type="", measure_scale=None): spice_measurement.__init__(self, measure_name, measure_scale) @@ -119,4 +120,39 @@ class power_measure(spice_measurement): meas_name = "{}{}".format(self.name, port) else: meas_name = self.name - return (meas_name,t_initial,t_final) \ No newline at end of file + return (meas_name,t_initial,t_final) + +class voltage_when_measure(spice_measurement): + """Generates a spice measurement to measure the voltage of a signal based on the voltage of another.""" + + def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, trig_vdd, measure_scale=None): + spice_measurement.__init__(self, measure_name, measure_scale) + self.set_meas_constants(trig_name, targ_name, trig_dir_str, trig_vdd) + + def get_measure_function(self): + return stimuli.gen_meas_find_voltage + + def set_meas_constants(self, trig_name, targ_name, trig_dir_str, trig_vdd): + """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" + self.trig_dir_str = trig_dir_str + self.trig_val_of_vdd = trig_vdd + + self.trig_name_no_port = trig_name + self.targ_name_no_port = targ_name + + def get_measure_values(self, trig_td, vdd_voltage, port=None): + """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" + + if port != None: + #For dictionary indexing reasons, the name is formatted differently than the signals + meas_name = "{}{}".format(self.name, port) + trig_name = self.trig_name_no_port.format(port) + targ_name = self.targ_name_no_port.format(port) + else: + meas_name = self.name + trig_name = self.trig_name_no_port + targ_name = self.targ_name_no_port + + trig_voltage = self.trig_val_of_vdd*vdd_voltage + + return (meas_name,trig_name,targ_name,trig_voltage,self.trig_dir_str,trig_td) \ No newline at end of file diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py new file mode 100644 index 00000000..7a06ab7a --- /dev/null +++ b/compiler/characterizer/model_check.py @@ -0,0 +1,174 @@ +import sys,re,shutil +import debug +import tech +import math +from .stimuli import * +from .trim_spice import * +from .charutils import * +import utils +from globals import OPTS +from .delay import delay +from .measurements import * + +class model_check(delay): + """Functions to test for the worst case delay in a target SRAM + + The current worst case determines a feasible period for the SRAM then tests + several bits and record the delay and differences between the bits. + + """ + + def __init__(self, sram, spfile, corner): + delay.__init__(self,sram,spfile,corner) + self.period = tech.spice["feasible_period"] + + def create_measurement_names(self): + """Create measurement names. The names themselves currently define the type of measurement""" + #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. + self.wl_delay_meas_names = ["delay_wl_en_bar", "delay_wl_en", "delay_dvr_en_bar", "delay_wl"] + self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in", "delay_delay_chain_stage_1", "delay_delay_chain_stage_2"] + self.sae_delay_meas_names = ["delay_pre_sen", "delay_sen_bar", "delay_sen"] + + def create_signal_names(self): + delay.create_signal_names(self) + #Signal names are all hardcoded, need to update to make it work for probe address and different configurations. + self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xbuf_wl_en.zb_int", "Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_15", "Xsram.Xbank0.wl_15"] + self.rbl_en_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in", "Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1", "Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] + self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en", "Xsram.Xcontrol0.Xbuf_s_en.zb_int", "Xsram.s_en0"] + + def create_measurement_objects(self): + """Create the measurements used for read and write ports""" + self.create_wordline_measurement_objects() + self.create_sae_measurement_objects() + self.all_measures = self.wl_meas_objs+self.sae_meas_objs + + def create_wordline_measurement_objects(self): + """Create the measurements to measure the wordline path from the gated_clk_bar signal""" + self.wl_meas_objs = [] + trig_dir = "RISE" + targ_dir = "FALL" + + for i in range(1, len(self.wl_signal_names)): + self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1], self.wl_signal_names[i-1], self.wl_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) + temp_dir = trig_dir + trig_dir = targ_dir + targ_dir = temp_dir + + def create_sae_measurement_objects(self): + """Create the measurements to measure the sense amp enable path from the gated_clk_bar signal. The RBL splits this path into two.""" + + self.sae_meas_objs = [] + trig_dir = "RISE" + targ_dir = "FALL" + #Add measurements from gated_clk_bar to RBL + for i in range(1, len(self.rbl_en_signal_names)): + self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1], self.rbl_en_signal_names[i-1], self.rbl_en_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) + temp_dir = trig_dir + trig_dir = targ_dir + targ_dir = temp_dir + + #Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL. + trig_dir = "FALL" + targ_dir = "RISE" + #Add measurements from gated_clk_bar to RBL + for i in range(1, len(self.sae_signal_names)): + self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1], self.sae_signal_names[i-1], self.sae_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) + temp_dir = trig_dir + trig_dir = targ_dir + targ_dir = temp_dir + + def write_delay_measures(self): + """ + Write the measure statements to quantify the delay and power results for all targeted ports. + """ + self.sf.write("\n* Measure statements for delay and power\n") + + # Output some comments to aid where cycles start and what is happening + for comment in self.cycle_comments: + self.sf.write("* {}\n".format(comment)) + + for read_port in self.targ_read_ports: + self.write_measures_read_port(read_port) + + def get_delay_measure_variants(self, port, delay_obj): + """Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" + #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port + #Assuming only read 0 for now + meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2 + return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) + + def write_measures_read_port(self, port): + """ + Write the measure statements for all nodes along the wordline path. + """ + # add measure statements for delays/slews + for measure in self.all_measures: + measure_variant_inp_tuple = self.get_delay_measure_variants(port, measure) + measure.write_measure(self.stim, measure_variant_inp_tuple) + + + def run_delay_simulation(self): + """ + This tries to simulate a period and checks if the result works. If + so, it returns True and the delays, slews, and powers. It + works on the trimmed netlist by default, so powers do not + include leakage of all cells. + """ + #Sanity Check + debug.check(self.period > 0, "Target simulation period non-positive") + + wl_result = [[] for i in self.all_ports] + sae_result = [[] for i in self.all_ports] + # Checking from not data_value to data_value + self.write_delay_stimulus() + + self.stim.run_sim() #running sim prodoces spice output file. + + for port in self.targ_read_ports: + #Parse and check the voltage measurements + wl_meas_list = [] + for measure in self.wl_meas_objs: + wl_meas_list.append(measure.retrieve_measure(port=port)) + if type(wl_meas_list[-1]) != float: + debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, wl_meas_list[-1]),1) #Printing the entire dict looks bad. + wl_result[port] = wl_meas_list + + sae_meas_list = [] + for measure in self.sae_meas_objs: + sae_meas_list.append(measure.retrieve_measure(port=port)) + if type(sae_meas_list[-1]) != float: + debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, sae_meas_list[-1]),1) #Printing the entire dict looks bad. + sae_result[port] = sae_meas_list + + # The delay is from the negative edge for our SRAM + return (True,wl_result, sae_result) + + def get_model_delays(self, port): + """Get model delays based on port. Currently assumes single RW port.""" + return self.sram.control_logic_rw.get_wl_sen_delays() + + def analyze(self, probe_address, probe_data, slews, loads): + """Measures entire delay path along the wordline and sense amp enable and compare it to the model delays.""" + self.set_probe(probe_address, probe_data) + self.load=max(loads) + self.slew=max(slews) + self.create_measurement_objects() + + read_port = self.read_ports[0] #only test the first read port + self.targ_read_ports = [read_port] + self.targ_write_ports = [self.write_ports[0]] + debug.info(1,"Bitline swing test: corner {}".format(self.corner)) + (success, wl_delays, sae_delays)=self.run_delay_simulation() + debug.check(success, "Model measurements Failed: period={}".format(self.period)) + wl_model_delays, sae_model_delays = self.get_model_delays(read_port) + + debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port])) + debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays)) + debug.info(1,"Measured SAE delays (ns):\n\t {}".format(sae_delays[read_port])) + debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays)) + + return wl_delays, sae_delays + + + + \ No newline at end of file diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 8545480c..9a945383 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -37,6 +37,8 @@ class control_logic(design.design): #self.sram=None #disable re-sizing for debugging, FIXME: resizing is not working, needs to be adjusted for new control logic. self.wl_timing_tolerance = 1 #Determines how much larger the sen delay should be. Accounts for possible error in model. self.parasitic_inv_delay = parameter["min_inv_para_delay"] #Keeping 0 for now until further testing. + self.wl_stage_efforts = None + self.sen_stage_efforts = None if self.port_type == "rw": self.num_control_signals = 2 @@ -157,7 +159,7 @@ class control_logic(design.design): elif self.words_per_row == 2: delay_stages = 6 else: - delay_stages = 4 + delay_stages = 2 return (delay_stages, delay_fanout) @@ -808,8 +810,8 @@ class control_logic(design.design): def get_delays_to_wl(self): """Get the delay (in delay units) of the clk to a wordline in the bitcell array""" debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") - stage_efforts = self.determine_wordline_stage_efforts() - clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(stage_efforts, self.parasitic_inv_delay) + self.wl_stage_efforts = self.determine_wordline_stage_efforts() + clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts, self.parasitic_inv_delay) total_delay = clk_to_wl_rise + clk_to_wl_fall debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay)) return clk_to_wl_rise,clk_to_wl_fall @@ -838,8 +840,8 @@ class control_logic(design.design): This does not incorporate the delay of the replica bitline. """ debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") - stage_efforts = self.determine_sa_enable_stage_efforts() - clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(stage_efforts, self.parasitic_inv_delay) + self.sen_stage_efforts = self.determine_sa_enable_stage_efforts() + clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts, self.parasitic_inv_delay) total_delay = clk_to_sen_rise + clk_to_sen_fall debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay)) return clk_to_sen_rise, clk_to_sen_fall @@ -870,4 +872,12 @@ class control_logic(design.design): last_stage_rise = stage_effort_list[-1].is_rise return stage_effort_list - \ No newline at end of file + + def get_wl_sen_delays(self): + """Gets a list of the stages and delays in order of their path.""" + if self.sen_stage_efforts == None or self.wl_stage_efforts == None: + debug.error("Model delays not calculated for SRAM.", 1) + wl_delays = logical_effort.calculate_delays(self.wl_stage_efforts, self.parasitic_inv_delay) + sen_delays = logical_effort.calculate_delays(self.sen_stage_efforts, self.parasitic_inv_delay) + return wl_delays, sen_delays + \ No newline at end of file diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index c847210b..26735870 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -23,7 +23,7 @@ class timing_sram_test(openram_test): from importlib import reload import characterizer reload(characterizer) - from characterizer import delay, bitline_delay + from characterizer import delay from sram import sram from sram_config import sram_config c = sram_config(word_size=1, @@ -43,15 +43,14 @@ class timing_sram_test(openram_test): corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) d = delay(s.s, tempspice, corner) - bl = bitline_delay(s.s, tempspice, corner) import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] data, port_data = d.analyze(probe_address, probe_data, slews, loads) - bitline_data = bl.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data data.update(port_data[0]) - data.update(bitline_data[0]) + + print(data) if OPTS.tech_name == "freepdk45": golden_data = {'delay_hl': [0.2011], @@ -60,13 +59,14 @@ class timing_sram_test(openram_test): 'min_period': 0.41, 'read0_power': [0.63604], 'read1_power': [0.6120599999999999], - 'slew_hl': [0.10853], - 'slew_lh': [0.10853], + 'slew_hl': [0.07078999999999999], + 'slew_lh': [0.07078999999999999], 'write0_power': [0.51742], 'write1_power': [0.51095], - 'volt_bl': 0.045626, - 'volt_br': 1.0709, - 'delay_bl_vth': 0.1813} + 'volt_bl': [0.2017], + 'volt_br': [1.0765], + 'delay_bl': [0.18114999999999998], + 'delay_br': [0.17763]} elif OPTS.tech_name == "scn4m_subm": golden_data = {'delay_hl': [1.3911], 'delay_lh': [1.3911], @@ -74,13 +74,14 @@ class timing_sram_test(openram_test): 'min_period': 2.812, 'read0_power': [22.1183], 'read1_power': [21.4388], - 'slew_hl': [0.7397553], - 'slew_lh': [0.7397553], + 'slew_hl': [0.6], + 'slew_lh': [0.6], 'write0_power': [19.4103], 'write1_power': [20.1167], - 'volt_bl': 1.8329, - 'volt_br': 5.081, - 'delay_bl_vth': 1.1141} + 'volt_bl': [3.1763], + 'volt_br': [5.5731], + 'delay_bl': [1.1133000000000002], + 'delay_br': [0.9958395]} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 16c3fb3a..98f01e53 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -23,7 +23,7 @@ class timing_sram_test(openram_test): from importlib import reload import characterizer reload(characterizer) - from characterizer import delay, bitline_delay + from characterizer import delay from sram import sram from sram_config import sram_config c = sram_config(word_size=1, @@ -43,30 +43,28 @@ class timing_sram_test(openram_test): corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) d = delay(s.s, tempspice, corner) - bl = bitline_delay(s.s, tempspice, corner) import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] data, port_data = d.analyze(probe_address, probe_data, slews, loads) - bitline_data = bl.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data data.update(port_data[0]) - data.update(bitline_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [0.20443139999999999], - 'delay_lh': [0.20443139999999999], - 'leakage_power': 0.0017840640000000001, - 'min_period': 0.41, - 'read0_power': [0.6435831], - 'read1_power': [0.6233463], - 'slew_hl': [0.1138734], - 'slew_lh': [0.1138734], - 'write0_power': [0.5205761], - 'write1_power': [0.5213689], - 'volt_bl': 0.03667602, - 'volt_br': 1.056013, - 'delay_bl_vth': 0.184373} + golden_data = {'delay_bl': [0.1840938], + 'delay_br': [0.1804373], + 'delay_hl': [0.2130831], + 'delay_lh': [0.2130831], + 'leakage_power': 0.001595639, + 'min_period': 0.527, + 'read0_power': [0.4852456], + 'read1_power': [0.46341889999999997], + 'slew_hl': [0.07351041999999999], + 'slew_lh': [0.07351041999999999], + 'volt_bl': [0.1954744], + 'volt_br': [1.058266], + 'write0_power': [0.4065201], + 'write1_power': [0.46341889999999997]} elif OPTS.tech_name == "scn4m_subm": golden_data = {'delay_hl': [1.610911], 'delay_lh': [1.610911], @@ -78,9 +76,10 @@ class timing_sram_test(openram_test): 'slew_lh': [0.7986348999999999], 'write0_power': [17.58272], 'write1_power': [18.523419999999998], - 'volt_bl': 1.639692, - 'volt_br': 5.06107, - 'delay_bl_vth': 1.322235} + 'volt_bl': [3.1763], + 'volt_br': [5.5731], + 'delay_bl': [1.1133000000000002], + 'delay_br': [0.9958395]} else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/28_delay_model_test.py b/compiler/tests/28_delay_model_test.py new file mode 100755 index 00000000..74ad22f3 --- /dev/null +++ b/compiler/tests/28_delay_model_test.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug + +class delay_model_test(openram_test): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.spice_name="hspice" + OPTS.analytical_delay = False + OPTS.netlist_only = 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 model_check + from sram import sram + from sram_config import sram_config + c = sram_config(word_size=1, + num_words=16, + num_banks=1) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = sram(c, name="sram1") + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + probe_address = "1" * s.s.addr_size + probe_data = s.s.word_size - 1 + debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data)) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + mc = model_check(s.s, tempspice, corner) + import tech + loads = [tech.spice["msflop_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + wl_data, sae_data = mc.analyze(probe_address, probe_data, slews, loads) + #Combine info about port into all data + + #debug.info(1,"Data:\n{}".format(wl_data)) + + 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() From 21663439ccc17456f566b7c8b5702241e22bd646 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 9 Jan 2019 22:42:34 -0800 Subject: [PATCH 09/29] Added slews measurements to the model checker. Removed unused code in bitline delay class. --- compiler/characterizer/bitline_delay.py | 21 +------- compiler/characterizer/model_check.py | 69 ++++++++++++++++--------- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/compiler/characterizer/bitline_delay.py b/compiler/characterizer/bitline_delay.py index 2af290f1..c85f06bc 100644 --- a/compiler/characterizer/bitline_delay.py +++ b/compiler/characterizer/bitline_delay.py @@ -26,26 +26,7 @@ class bitline_delay(delay): delay.create_signal_names(self) self.bl_signal_names = ["Xsram.Xbank0.bl", "Xsram.Xbank0.br"] self.sen_name = "Xsram.s_en" - - def create_measurement_objects(self): - """Create the measurements used for read and write ports""" - self.meas_objs = [] - self.create_bitline_find_measurement_objects() - self.create_bitline_delay_measurement_objects() - - def create_bitline_delay_measurement_objects(self): - self.find_meas_objs = [] - trig_delay_name = "clk{0}" - targ_name = "{0}{1}_{2}".format(self.dout_name,"{}",self.probe_data) #Empty values are the port and probe data bit - self.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", 1e9)) - self.read_meas_objs.append(delay_measure("delay_hl", trig_delay_name, targ_name, "FALL", "FALL", 1e9)) - - self.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", 1e9)) - self.read_meas_objs.append(slew_measure("slew_hl", targ_name, "FALL", 1e9)) - - self.read_meas_objs.append(power_measure("read1_power", "RISE", 1e3)) - self.read_meas_objs.append(power_measure("read0_power", "FALL", 1e3)) - + def create_measurement_names(self): """Create measurement names. The names themselves currently define the type of measurement""" #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 7a06ab7a..a12117d3 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -26,8 +26,12 @@ class model_check(delay): """Create measurement names. The names themselves currently define the type of measurement""" #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. self.wl_delay_meas_names = ["delay_wl_en_bar", "delay_wl_en", "delay_dvr_en_bar", "delay_wl"] + self.wl_slew_meas_names = ["slew_wl_gated_clk_bar","slew_wl_en_bar", "slew_wl_en", "slew_drv_en_bar", "slew_wl"] + self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in", "delay_delay_chain_stage_1", "delay_delay_chain_stage_2"] + self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in", "slew_delay_chain_stage_1", "slew_delay_chain_stage_2"] self.sae_delay_meas_names = ["delay_pre_sen", "delay_sen_bar", "delay_sen"] + self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen", "slew_sen_bar", "slew_sen"] def create_signal_names(self): delay.create_signal_names(self) @@ -50,10 +54,12 @@ class model_check(delay): for i in range(1, len(self.wl_signal_names)): self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1], self.wl_signal_names[i-1], self.wl_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) + self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1], self.wl_signal_names[i-1], trig_dir, measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir - + self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[-1], self.wl_signal_names[-1], trig_dir, measure_scale=1e9)) + def create_sae_measurement_objects(self): """Create the measurements to measure the sense amp enable path from the gated_clk_bar signal. The RBL splits this path into two.""" @@ -63,20 +69,24 @@ class model_check(delay): #Add measurements from gated_clk_bar to RBL for i in range(1, len(self.rbl_en_signal_names)): self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1], self.rbl_en_signal_names[i-1], self.rbl_en_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) + self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1], self.rbl_en_signal_names[i-1], trig_dir, measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir - + self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1], self.rbl_en_signal_names[-1], trig_dir, measure_scale=1e9)) + #Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL. trig_dir = "FALL" targ_dir = "RISE" #Add measurements from gated_clk_bar to RBL for i in range(1, len(self.sae_signal_names)): self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1], self.sae_signal_names[i-1], self.sae_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) + self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1], self.sae_signal_names[i-1], trig_dir, measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir - + self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1], self.sae_signal_names[-1], trig_dir, measure_scale=1e9)) + def write_delay_measures(self): """ Write the measure statements to quantify the delay and power results for all targeted ports. @@ -90,10 +100,12 @@ class model_check(delay): for read_port in self.targ_read_ports: self.write_measures_read_port(read_port) - def get_delay_measure_variants(self, port, delay_obj): + def get_delay_measure_variants(self, port, measure_obj): """Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port #Assuming only read 0 for now + if not (type(measure_obj) is delay_measure or type(measure_obj) is slew_measure): + debug.error("Measurement not recognized by the model checker.",1) meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read0"]] + self.period/2 return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) @@ -106,7 +118,22 @@ class model_check(delay): measure_variant_inp_tuple = self.get_delay_measure_variants(port, measure) measure.write_measure(self.stim, measure_variant_inp_tuple) - + def get_measurement_values(self, meas_objs, port): + """Gets the delays and slews from a specified port from the spice output file and returns them as lists.""" + delay_meas_list = [] + slew_meas_list = [] + for measure in meas_objs: + measure_value = measure.retrieve_measure(port=port) + if type(measure_value) != float: + debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, measure_value),1) + if type(measure) is delay_measure: + delay_meas_list.append(measure_value) + elif type(measure)is slew_measure: + slew_meas_list.append(measure_value) + else: + debug.error("Measurement object not recognized.",1) + return delay_meas_list, slew_meas_list + def run_delay_simulation(self): """ This tries to simulate a period and checks if the result works. If @@ -117,31 +144,21 @@ class model_check(delay): #Sanity Check debug.check(self.period > 0, "Target simulation period non-positive") - wl_result = [[] for i in self.all_ports] - sae_result = [[] for i in self.all_ports] + wl_delay_result = [[] for i in self.all_ports] + wl_slew_result = [[] for i in self.all_ports] + sae_delay_result = [[] for i in self.all_ports] + sae_slew_result = [[] for i in self.all_ports] # Checking from not data_value to data_value self.write_delay_stimulus() self.stim.run_sim() #running sim prodoces spice output file. - + + #Retrieve the results from the output file for port in self.targ_read_ports: #Parse and check the voltage measurements - wl_meas_list = [] - for measure in self.wl_meas_objs: - wl_meas_list.append(measure.retrieve_measure(port=port)) - if type(wl_meas_list[-1]) != float: - debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, wl_meas_list[-1]),1) #Printing the entire dict looks bad. - wl_result[port] = wl_meas_list - - sae_meas_list = [] - for measure in self.sae_meas_objs: - sae_meas_list.append(measure.retrieve_measure(port=port)) - if type(sae_meas_list[-1]) != float: - debug.error("Failed to Measure Value:\n\t\t{}={}".format(measure.name, sae_meas_list[-1]),1) #Printing the entire dict looks bad. - sae_result[port] = sae_meas_list - - # The delay is from the negative edge for our SRAM - return (True,wl_result, sae_result) + wl_delay_result[port], wl_slew_result[port] = self.get_measurement_values(self.wl_meas_objs, port) + sae_delay_result[port], sae_slew_result[port] = self.get_measurement_values(self.sae_meas_objs, port) + return (True,wl_delay_result, sae_delay_result, wl_slew_result, sae_slew_result) def get_model_delays(self, port): """Get model delays based on port. Currently assumes single RW port.""" @@ -158,14 +175,16 @@ class model_check(delay): self.targ_read_ports = [read_port] self.targ_write_ports = [self.write_ports[0]] debug.info(1,"Bitline swing test: corner {}".format(self.corner)) - (success, wl_delays, sae_delays)=self.run_delay_simulation() + (success, wl_delays, sae_delays, wl_slews, sae_slews)=self.run_delay_simulation() debug.check(success, "Model measurements Failed: period={}".format(self.period)) wl_model_delays, sae_model_delays = self.get_model_delays(read_port) debug.info(1,"Measured Wordline delays (ns):\n\t {}".format(wl_delays[read_port])) debug.info(1,"Wordline model delays:\n\t {}".format(wl_model_delays)) + debug.info(1,"Measured Wordline slews:\n\t {}".format(wl_slews[read_port])) debug.info(1,"Measured SAE delays (ns):\n\t {}".format(sae_delays[read_port])) debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays)) + debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port])) return wl_delays, sae_delays From cc0be510c75cd534e1312caf785f7297af43effc Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 16 Jan 2019 00:46:24 -0800 Subject: [PATCH 10/29] Added some data scaling and error calculation in model check. --- compiler/characterizer/model_check.py | 37 ++++++++++++++++++++++++++ compiler/tests/21_hspice_delay_test.py | 4 +-- compiler/tests/28_delay_model_test.py | 2 ++ compiler/tests/testutils.py | 1 + 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index a12117d3..94b200bc 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -164,6 +164,36 @@ class model_check(delay): """Get model delays based on port. Currently assumes single RW port.""" return self.sram.control_logic_rw.get_wl_sen_delays() + def scale_delays(self, delay_list): + """Takes in a list of measured delays and convert it to simple units to easily compare to model values.""" + converted_values = [] + #Calculate average + total = 0 + for meas_value in delay_list: + total+=meas_value + average = total/len(delay_list) + + #Convert values + for meas_value in delay_list: + converted_values.append(meas_value/average) + return converted_values + + def min_max_normalization(self, value_list): + """Re-scales input values on a range from 0-1 where min(list)=0, max(list)=1""" + scaled_values = [] + min_val = min(value_list) + min_max_diff = max(value_list) - min_val + for value in value_list: + scaled_values.append((value-min_val)/(min_max_diff)) + return scaled_values + + def calculate_error_l2_norm(self, list_a, list_b): + """Calculates error between two lists using the l2 norm""" + error_list = [] + for val_a, val_b in zip(list_a, list_b): + error_list.append((val_a-val_b)**2) + return error_list + def analyze(self, probe_address, probe_data, slews, loads): """Measures entire delay path along the wordline and sense amp enable and compare it to the model delays.""" self.set_probe(probe_address, probe_data) @@ -186,6 +216,13 @@ class model_check(delay): debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays)) debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port])) + scaled_wl_meas = self.min_max_normalization(wl_delays[read_port]) + debug.info(1, "Scaled wordline delays:\n{}".format(scaled_wl_meas)) + scaled_wl_model = self.min_max_normalization(wl_model_delays) + debug.info(1, "Scaled wordline model:\n{}".format(scaled_wl_model)) + errors = self.calculate_error_l2_norm(scaled_wl_meas, scaled_wl_model) + debug.info(1, "Model errors:\n{}".format(errors)) + return wl_delays, sae_delays diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 26735870..8be7152c 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -49,9 +49,7 @@ class timing_sram_test(openram_test): data, port_data = d.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data data.update(port_data[0]) - - print(data) - + if OPTS.tech_name == "freepdk45": golden_data = {'delay_hl': [0.2011], 'delay_lh': [0.2011], diff --git a/compiler/tests/28_delay_model_test.py b/compiler/tests/28_delay_model_test.py index 74ad22f3..8f9154e0 100755 --- a/compiler/tests/28_delay_model_test.py +++ b/compiler/tests/28_delay_model_test.py @@ -18,6 +18,8 @@ class delay_model_test(openram_test): OPTS.spice_name="hspice" OPTS.analytical_delay = False OPTS.netlist_only = True + OPTS.trim_netlist = False + debug.info(1, "Trimming disabled for this test. Simulation could be slow.") # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 6b2a7dcf..74d97960 100755 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -62,6 +62,7 @@ class openram_test(unittest.TestCase): delay_obj.set_load_slew(load, slew) delay_obj.set_probe(probe_address="1"*sram.addr_size, probe_data=(sram.word_size-1)) test_port = delay_obj.read_ports[0] #Only test one port, assumes other ports have similar period. + delay_obj.create_measurement_objects() delay_obj.find_feasible_period_one_port(test_port) return delay_obj.period From 5bbc43d0a0cb9d4911388dd81d8088eace5b90b0 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 17 Jan 2019 01:59:41 -0800 Subject: [PATCH 11/29] Added data collection of wordline and s_en measurements. --- compiler/characterizer/model_check.py | 49 +++++++++++++++++++++------ compiler/tests/28_delay_model_test.py | 5 +-- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 94b200bc..03d7982a 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -21,6 +21,11 @@ class model_check(delay): def __init__(self, sram, spfile, corner): delay.__init__(self,sram,spfile,corner) self.period = tech.spice["feasible_period"] + self.create_data_names() + + def create_data_names(self): + self.wl_meas_name, self.wl_model_name = "wl_measures", "wl_model" + self.sae_meas_name, self.sae_model_name = "sae_measures", "sae_model" def create_measurement_names(self): """Create measurement names. The names themselves currently define the type of measurement""" @@ -40,6 +45,16 @@ class model_check(delay): self.rbl_en_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in", "Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1", "Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en", "Xsram.Xcontrol0.Xbuf_s_en.zb_int", "Xsram.s_en0"] + def get_all_signal_names(self): + """Returns all signals names as a dict indexed by hardcoded names. Useful for writing the head of the CSV.""" + name_dict = {} + #Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names. + name_dict[self.wl_meas_name] = self.wl_signal_names[1:] + name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured. + name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:]+self.sae_signal_names[1:] + name_dict[self.sae_model_name] = name_dict["sae_measures"] + return name_dict + def create_measurement_objects(self): """Create the measurements used for read and write ports""" self.create_wordline_measurement_objects() @@ -181,10 +196,10 @@ class model_check(delay): def min_max_normalization(self, value_list): """Re-scales input values on a range from 0-1 where min(list)=0, max(list)=1""" scaled_values = [] - min_val = min(value_list) - min_max_diff = max(value_list) - min_val + min_max_diff = max(value_list) - min(value_list) + average = sum(value_list)/len(value_list) for value in value_list: - scaled_values.append((value-min_val)/(min_max_diff)) + scaled_values.append((value-average)/(min_max_diff)) return scaled_values def calculate_error_l2_norm(self, list_a, list_b): @@ -193,6 +208,15 @@ class model_check(delay): for val_a, val_b in zip(list_a, list_b): error_list.append((val_a-val_b)**2) return error_list + + def compare_measured_and_model(self, measured_vals, model_vals): + """First scales both inputs into similar ranges and then compares the error between both.""" + scaled_meas = self.min_max_normalization(measured_vals) + debug.info(1, "Scaled measurements:\n{}".format(scaled_meas)) + scaled_model = self.min_max_normalization(model_vals) + debug.info(1, "Scaled model:\n{}".format(scaled_model)) + errors = self.calculate_error_l2_norm(scaled_meas, scaled_model) + debug.info(1, "Errors:\n{}\n".format(errors)) def analyze(self, probe_address, probe_data, slews, loads): """Measures entire delay path along the wordline and sense amp enable and compare it to the model delays.""" @@ -200,6 +224,7 @@ class model_check(delay): self.load=max(loads) self.slew=max(slews) self.create_measurement_objects() + data_dict = {} read_port = self.read_ports[0] #only test the first read port self.targ_read_ports = [read_port] @@ -216,14 +241,18 @@ class model_check(delay): debug.info(1,"SAE model delays:\n\t {}".format(sae_model_delays)) debug.info(1,"Measured SAE slews:\n\t {}".format(sae_slews[read_port])) - scaled_wl_meas = self.min_max_normalization(wl_delays[read_port]) - debug.info(1, "Scaled wordline delays:\n{}".format(scaled_wl_meas)) - scaled_wl_model = self.min_max_normalization(wl_model_delays) - debug.info(1, "Scaled wordline model:\n{}".format(scaled_wl_model)) - errors = self.calculate_error_l2_norm(scaled_wl_meas, scaled_wl_model) - debug.info(1, "Model errors:\n{}".format(errors)) + data_dict[self.wl_meas_name] = wl_delays[read_port] + data_dict[self.wl_model_name] = wl_model_delays + data_dict[self.sae_meas_name] = sae_delays[read_port] + data_dict[self.sae_model_name] = sae_model_delays - return wl_delays, sae_delays + #Some evaluations of the model and measured values + debug.info(1, "Comparing wordline measurements and model.") + self.compare_measured_and_model(wl_delays[read_port], wl_model_delays) + debug.info(1, "Comparing SAE measurements and model") + self.compare_measured_and_model(sae_delays[read_port], sae_model_delays) + + return data_dict diff --git a/compiler/tests/28_delay_model_test.py b/compiler/tests/28_delay_model_test.py index 8f9154e0..a02a42e9 100755 --- a/compiler/tests/28_delay_model_test.py +++ b/compiler/tests/28_delay_model_test.py @@ -25,10 +25,11 @@ class delay_model_test(openram_test): from importlib import reload import characterizer reload(characterizer) + from characterizer import model_check from sram import sram from sram_config import sram_config - c = sram_config(word_size=1, + c = sram_config(word_size=4, num_words=16, num_banks=1) c.words_per_row=1 @@ -48,7 +49,7 @@ class delay_model_test(openram_test): import tech loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] - wl_data, sae_data = mc.analyze(probe_address, probe_data, slews, loads) + sram_data = mc.analyze(probe_address, probe_data, slews, loads) #Combine info about port into all data #debug.info(1,"Data:\n{}".format(wl_data)) From 4ced6be6bdf63985d4d9434a856700b4aafd1395 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 17 Jan 2019 09:54:34 -0800 Subject: [PATCH 12/29] Added data collection and some initial data --- compiler/tests/delay_data_collection.py | 137 ++++++++++++++++++ .../tests/model_data/data_sae_measures.csv | 3 + compiler/tests/model_data/data_sae_model.csv | 3 + .../tests/model_data/data_wl_measures.csv | 3 + compiler/tests/model_data/data_wl_model.csv | 3 + 5 files changed, 149 insertions(+) create mode 100644 compiler/tests/delay_data_collection.py create mode 100644 compiler/tests/model_data/data_sae_measures.csv create mode 100644 compiler/tests/model_data/data_sae_model.csv create mode 100644 compiler/tests/model_data/data_wl_measures.csv create mode 100644 compiler/tests/model_data/data_wl_model.csv diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py new file mode 100644 index 00000000..b5af25fc --- /dev/null +++ b/compiler/tests/delay_data_collection.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug +import csv +from sram import sram +from sram_config import sram_config + +MODEL_DIR = "model_data/" + +class data_collection(openram_test): + + def runTest(self): + self.init_data_gen() + + #Run on one size to initialize CSV writing (csv names come from return value). Strange, but it is okay for now. + sram_data = self.get_sram_data(1,16,1) + self.initialize_csv_file(sram_data) + + self.add_sram_data_to_csv(sram_data, 1, 16, 1) + + #Run openRAM for several size configurations + #word_size_list, num_word_list, words_per_row_list = self.get_sram_configs() + word_size_list, num_word_list, words_per_row_list = [4], [16], [1] #for quick testing. + for word_size in word_size_list: + for num_word in num_word_list: + for words_per_row in words_per_row_list: + #Unfortunately, init needs to be called everytime + self.init_data_gen() + sram_data = self.get_sram_data(word_size, num_word, words_per_row) + self.add_sram_data_to_csv(sram_data, word_size, num_word, words_per_row) + + self.close_files() + debug.info(1,"Data Generated") + globals.end_openram() + + def init_data_gen(self): + """Initialization for the data test to run""" + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.spice_name="hspice" #Much faster than ngspice. + OPTS.trim_netlist = False + OPTS.netlist_only = True + OPTS.analytical_delay = False + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + + def close_files(self): + """Closes all files stored in the file dict""" + for key,file in self.csv_files.items(): + file.close() + + def get_sram_configs(self): + """Generate lists of wordsizes, number of words, and column mux size (words per row) to be tested.""" + min_word_size = 1 + max_word_size = 16 + min_num_words_log2 = 4 + max_num_words_log2 = 8 + word_sizes = [i for i in range(min_word_size,max_word_size+1)] + num_words = [2**i for i in range(min_num_words_log2,max_num_words_log2+1)] + words_per_row = [1] + return word_sizes, num_words, words_per_row + + def add_sram_data_to_csv(self, sram_data, word_size, num_words, words_per_row): + """Writes data to its respective CSV file. There is a CSV for each measurement target (wordline, sense amp enable, and models)""" + sram_specs = [word_size,num_words,words_per_row] + for data_name, data_values in sram_data.items(): + self.csv_writers[data_name].writerow(sram_specs+sram_data[data_name]) + debug.info(2,"Data Added to CSV file.") + + def initialize_csv_file(self, sram_data): + """Opens a CSV file and writer for every data set being written (wl/sae measurements and model values)""" + #CSV File writing + header_dict = self.delay_obj.get_all_signal_names() + self.csv_files = {} + self.csv_writers = {} + for data_name, header_list in header_dict.items(): + self.csv_files[data_name] = open('{}data_{}.csv'.format(MODEL_DIR,data_name), 'w') + fields = ('word_size', 'num_words', 'words_per_row', *header_list) + self.csv_writers[data_name] = csv.writer(self.csv_files[data_name], lineterminator = '\n') + self.csv_writers[data_name].writerow(fields) + + def get_sram_data(self, word_size, num_words, words_per_row): + """Generates the SRAM based on input configuration and returns the data.""" + from characterizer import model_check + c = sram_config(word_size=word_size, + num_words=num_words, + num_banks=1) + #minimum 16 rows. Most sizes below 16*16 will try to automatically use less rows unless enforced. + #if word_size*num_words < 256: + c.words_per_row=words_per_row #Force no column mux until incorporated into analytical delay. + + debug.info(1, "Getting data for {} bit, {} words SRAM with 1 bank".format(word_size, num_words)) + s = sram(c, name="sram_{}ws_{}words".format(word_size, num_words)) + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + self.delay_obj = model_check(s.s, tempspice, corner) + + import tech + #Only 1 at a time + probe_address = "1" * s.s.addr_size + probe_data = s.s.word_size - 1 + loads = [tech.spice["msflop_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + + sram_data = self.delay_obj.analyze(probe_address,probe_data,slews,loads) + return sram_data + + def remove_lists_from_dict(self, dict): + """Check all the values in the dict and replaces the list items with its first value.""" + #This is useful because the tests performed here only generate 1 value but a list + #with 1 item makes writing it to a csv later harder. + for key in dict.keys(): + if type(dict[key]) is list: + if len(dict[key]) > 0: + dict[key] = dict[key][0] + else: + del dict[key] + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/model_data/data_sae_measures.csv b/compiler/tests/model_data/data_sae_measures.csv new file mode 100644 index 00000000..29b9ee07 --- /dev/null +++ b/compiler/tests/model_data/data_sae_measures.csv @@ -0,0 +1,3 @@ +word_size,num_words,words_per_row,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 +1,16,1,0.1073407,0.010667,0.1317698,0.0910598,0.04802989999999999,0.1304522,0.0164942 +4,16,1,0.10635089999999998,0.010598499999999999,0.1315219,0.0911604,0.048601,0.12349109999999999,0.05874360000000001 diff --git a/compiler/tests/model_data/data_sae_model.csv b/compiler/tests/model_data/data_sae_model.csv new file mode 100644 index 00000000..25a56d9e --- /dev/null +++ b/compiler/tests/model_data/data_sae_model.csv @@ -0,0 +1,3 @@ +word_size,num_words,words_per_row,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 +1,16,1,5.0,0.75,4.5,5.5,2.5,4.5,1.09375 +4,16,1,5.0,0.75,4.5,5.5,2.5,4.5,1.09375 diff --git a/compiler/tests/model_data/data_wl_measures.csv b/compiler/tests/model_data/data_wl_measures.csv new file mode 100644 index 00000000..95aa4044 --- /dev/null +++ b/compiler/tests/model_data/data_wl_measures.csv @@ -0,0 +1,3 @@ +word_size,num_words,words_per_row,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +1,16,1,0.1257435,0.0364958,0.051171100000000004,0.020759299999999998 +4,16,1,0.124175,0.03654059999999999,0.0507837,0.0433494 diff --git a/compiler/tests/model_data/data_wl_model.csv b/compiler/tests/model_data/data_wl_model.csv new file mode 100644 index 00000000..ae601f49 --- /dev/null +++ b/compiler/tests/model_data/data_wl_model.csv @@ -0,0 +1,3 @@ +word_size,num_words,words_per_row,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +1,16,1,1.5,21.833333333333332,2.0,1.1666666666666665 +4,16,1,2.5,11.166666666666666,2.0,3.1666666666666665 From 5885e3b635302d10e0d2f1bb527014cb99f44ec8 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Fri, 18 Jan 2019 00:23:50 -0800 Subject: [PATCH 13/29] Removed carriage returns, adjusted signal names generation for variable delay chain size. --- compiler/characterizer/model_check.py | 14 +- compiler/tests/delay_data_collection.py | 274 ++++++++++++------------ 2 files changed, 147 insertions(+), 141 deletions(-) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 03d7982a..059227e1 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -33,8 +33,10 @@ class model_check(delay): self.wl_delay_meas_names = ["delay_wl_en_bar", "delay_wl_en", "delay_dvr_en_bar", "delay_wl"] self.wl_slew_meas_names = ["slew_wl_gated_clk_bar","slew_wl_en_bar", "slew_wl_en", "slew_drv_en_bar", "slew_wl"] - self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in", "delay_delay_chain_stage_1", "delay_delay_chain_stage_2"] - self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in", "slew_delay_chain_stage_1", "slew_delay_chain_stage_2"] + dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] + self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names + dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] + self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names self.sae_delay_meas_names = ["delay_pre_sen", "delay_sen_bar", "delay_sen"] self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen", "slew_sen_bar", "slew_sen"] @@ -42,7 +44,8 @@ class model_check(delay): delay.create_signal_names(self) #Signal names are all hardcoded, need to update to make it work for probe address and different configurations. self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xbuf_wl_en.zb_int", "Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_15", "Xsram.Xbank0.wl_15"] - self.rbl_en_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in", "Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1", "Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] + delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())] + ["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] + self.rbl_en_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"] + delay_chain_signal_names self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en", "Xsram.Xcontrol0.Xbuf_s_en.zb_int", "Xsram.s_en0"] def get_all_signal_names(self): @@ -178,7 +181,10 @@ class model_check(delay): def get_model_delays(self, port): """Get model delays based on port. Currently assumes single RW port.""" return self.sram.control_logic_rw.get_wl_sen_delays() - + + def get_num_delay_stages(self): + return len(self.sram.control_logic_rw.replica_bitline.delay_fanout_list) + def scale_delays(self, delay_list): """Takes in a list of measured delays and convert it to simple units to easily compare to model values.""" converted_values = [] diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index b5af25fc..ac10b98c 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -1,137 +1,137 @@ -#!/usr/bin/env python3 -""" -Run a regression test on various srams -""" - -import unittest -from testutils import header,openram_test -import sys,os -sys.path.append(os.path.join(sys.path[0],"..")) -import globals -from globals import OPTS -import debug -import csv -from sram import sram -from sram_config import sram_config - -MODEL_DIR = "model_data/" - -class data_collection(openram_test): - - def runTest(self): - self.init_data_gen() - - #Run on one size to initialize CSV writing (csv names come from return value). Strange, but it is okay for now. - sram_data = self.get_sram_data(1,16,1) - self.initialize_csv_file(sram_data) - - self.add_sram_data_to_csv(sram_data, 1, 16, 1) - - #Run openRAM for several size configurations - #word_size_list, num_word_list, words_per_row_list = self.get_sram_configs() - word_size_list, num_word_list, words_per_row_list = [4], [16], [1] #for quick testing. - for word_size in word_size_list: - for num_word in num_word_list: - for words_per_row in words_per_row_list: - #Unfortunately, init needs to be called everytime - self.init_data_gen() - sram_data = self.get_sram_data(word_size, num_word, words_per_row) - self.add_sram_data_to_csv(sram_data, word_size, num_word, words_per_row) - - self.close_files() - debug.info(1,"Data Generated") - globals.end_openram() - - def init_data_gen(self): - """Initialization for the data test to run""" - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - OPTS.spice_name="hspice" #Much faster than ngspice. - OPTS.trim_netlist = False - OPTS.netlist_only = True - OPTS.analytical_delay = False - # This is a hack to reload the characterizer __init__ with the spice version - from importlib import reload - import characterizer - reload(characterizer) - - def close_files(self): - """Closes all files stored in the file dict""" - for key,file in self.csv_files.items(): - file.close() - - def get_sram_configs(self): - """Generate lists of wordsizes, number of words, and column mux size (words per row) to be tested.""" - min_word_size = 1 - max_word_size = 16 - min_num_words_log2 = 4 - max_num_words_log2 = 8 - word_sizes = [i for i in range(min_word_size,max_word_size+1)] - num_words = [2**i for i in range(min_num_words_log2,max_num_words_log2+1)] - words_per_row = [1] - return word_sizes, num_words, words_per_row - - def add_sram_data_to_csv(self, sram_data, word_size, num_words, words_per_row): - """Writes data to its respective CSV file. There is a CSV for each measurement target (wordline, sense amp enable, and models)""" - sram_specs = [word_size,num_words,words_per_row] - for data_name, data_values in sram_data.items(): - self.csv_writers[data_name].writerow(sram_specs+sram_data[data_name]) - debug.info(2,"Data Added to CSV file.") - - def initialize_csv_file(self, sram_data): - """Opens a CSV file and writer for every data set being written (wl/sae measurements and model values)""" - #CSV File writing - header_dict = self.delay_obj.get_all_signal_names() - self.csv_files = {} - self.csv_writers = {} - for data_name, header_list in header_dict.items(): - self.csv_files[data_name] = open('{}data_{}.csv'.format(MODEL_DIR,data_name), 'w') - fields = ('word_size', 'num_words', 'words_per_row', *header_list) - self.csv_writers[data_name] = csv.writer(self.csv_files[data_name], lineterminator = '\n') - self.csv_writers[data_name].writerow(fields) - - def get_sram_data(self, word_size, num_words, words_per_row): - """Generates the SRAM based on input configuration and returns the data.""" - from characterizer import model_check - c = sram_config(word_size=word_size, - num_words=num_words, - num_banks=1) - #minimum 16 rows. Most sizes below 16*16 will try to automatically use less rows unless enforced. - #if word_size*num_words < 256: - c.words_per_row=words_per_row #Force no column mux until incorporated into analytical delay. - - debug.info(1, "Getting data for {} bit, {} words SRAM with 1 bank".format(word_size, num_words)) - s = sram(c, name="sram_{}ws_{}words".format(word_size, num_words)) - - tempspice = OPTS.openram_temp + "temp.sp" - s.sp_write(tempspice) - - corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - self.delay_obj = model_check(s.s, tempspice, corner) - - import tech - #Only 1 at a time - probe_address = "1" * s.s.addr_size - probe_data = s.s.word_size - 1 - loads = [tech.spice["msflop_in_cap"]*4] - slews = [tech.spice["rise_time"]*2] - - sram_data = self.delay_obj.analyze(probe_address,probe_data,slews,loads) - return sram_data - - def remove_lists_from_dict(self, dict): - """Check all the values in the dict and replaces the list items with its first value.""" - #This is useful because the tests performed here only generate 1 value but a list - #with 1 item makes writing it to a csv later harder. - for key in dict.keys(): - if type(dict[key]) is list: - if len(dict[key]) > 0: - dict[key] = dict[key][0] - else: - del dict[key] - -# instantiate a copdsay of the class to actually run the test -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main() +#!/usr/bin/env python3 +""" +Run a regression test on various srams +""" + +import unittest +from testutils import header,openram_test +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +from globals import OPTS +import debug +import csv +from sram import sram +from sram_config import sram_config + +MODEL_DIR = "model_data/" + +class data_collection(openram_test): + + def runTest(self): + self.init_data_gen() + + #Run on one size to initialize CSV writing (csv names come from return value). Strange, but it is okay for now. + sram_data = self.get_sram_data(1,16,1) + self.initialize_csv_file(sram_data) + + self.add_sram_data_to_csv(sram_data, 1, 16, 1) + + #Run openRAM for several size configurations + #word_size_list, num_word_list, words_per_row_list = self.get_sram_configs() + word_size_list, num_word_list, words_per_row_list = [4], [16], [1] #for quick testing. + for word_size in word_size_list: + for num_word in num_word_list: + for words_per_row in words_per_row_list: + #Unfortunately, init needs to be called everytime + self.init_data_gen() + sram_data = self.get_sram_data(word_size, num_word, words_per_row) + self.add_sram_data_to_csv(sram_data, word_size, num_word, words_per_row) + + self.close_files() + debug.info(1,"Data Generated") + globals.end_openram() + + def init_data_gen(self): + """Initialization for the data test to run""" + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + OPTS.spice_name="hspice" #Much faster than ngspice. + OPTS.trim_netlist = False + OPTS.netlist_only = True + OPTS.analytical_delay = False + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + + def close_files(self): + """Closes all files stored in the file dict""" + for key,file in self.csv_files.items(): + file.close() + + def get_sram_configs(self): + """Generate lists of wordsizes, number of words, and column mux size (words per row) to be tested.""" + min_word_size = 1 + max_word_size = 16 + min_num_words_log2 = 4 + max_num_words_log2 = 8 + word_sizes = [i for i in range(min_word_size,max_word_size+1)] + num_words = [2**i for i in range(min_num_words_log2,max_num_words_log2+1)] + words_per_row = [1] + return word_sizes, num_words, words_per_row + + def add_sram_data_to_csv(self, sram_data, word_size, num_words, words_per_row): + """Writes data to its respective CSV file. There is a CSV for each measurement target (wordline, sense amp enable, and models)""" + sram_specs = [word_size,num_words,words_per_row] + for data_name, data_values in sram_data.items(): + self.csv_writers[data_name].writerow(sram_specs+sram_data[data_name]) + debug.info(2,"Data Added to CSV file.") + + def initialize_csv_file(self, sram_data): + """Opens a CSV file and writer for every data set being written (wl/sae measurements and model values)""" + #CSV File writing + header_dict = self.delay_obj.get_all_signal_names() + self.csv_files = {} + self.csv_writers = {} + for data_name, header_list in header_dict.items(): + self.csv_files[data_name] = open('{}data_{}.csv'.format(MODEL_DIR,data_name), 'w') + fields = ('word_size', 'num_words', 'words_per_row', *header_list) + self.csv_writers[data_name] = csv.writer(self.csv_files[data_name], lineterminator = '\n') + self.csv_writers[data_name].writerow(fields) + + def get_sram_data(self, word_size, num_words, words_per_row): + """Generates the SRAM based on input configuration and returns the data.""" + from characterizer import model_check + c = sram_config(word_size=word_size, + num_words=num_words, + num_banks=1) + #minimum 16 rows. Most sizes below 16*16 will try to automatically use less rows unless enforced. + #if word_size*num_words < 256: + c.words_per_row=words_per_row #Force no column mux until incorporated into analytical delay. + + debug.info(1, "Getting data for {} bit, {} words SRAM with 1 bank".format(word_size, num_words)) + s = sram(c, name="sram_{}ws_{}words".format(word_size, num_words)) + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + self.delay_obj = model_check(s.s, tempspice, corner) + + import tech + #Only 1 at a time + probe_address = "1" * s.s.addr_size + probe_data = s.s.word_size - 1 + loads = [tech.spice["msflop_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + + sram_data = self.delay_obj.analyze(probe_address,probe_data,slews,loads) + return sram_data + + def remove_lists_from_dict(self, dict): + """Check all the values in the dict and replaces the list items with its first value.""" + #This is useful because the tests performed here only generate 1 value but a list + #with 1 item makes writing it to a csv later harder. + for key in dict.keys(): + if type(dict[key]) is list: + if len(dict[key]) > 0: + dict[key] = dict[key][0] + else: + del dict[key] + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() From 6d3884d60ddb6ac96c9ce57e11e87f538b138dda Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 22 Jan 2019 16:40:46 -0800 Subject: [PATCH 14/29] Added corner data collection. --- compiler/characterizer/model_check.py | 2 +- compiler/tests/config_data.py | 9 ++ compiler/tests/delay_data_collection.py | 92 +++++++++++-------- .../data_4b_16word_1way_sae_measures.csv | 3 + .../data_4b_16word_1way_sae_model.csv | 3 + .../data_4b_16word_1way_wl_measures.csv | 3 + .../data_4b_16word_1way_wl_model.csv | 3 + .../tests/model_data/data_sae_measures.csv | 3 - compiler/tests/model_data/data_sae_model.csv | 3 - .../tests/model_data/data_wl_measures.csv | 3 - compiler/tests/model_data/data_wl_model.csv | 3 - technology/freepdk45/tech/tech.py | 7 +- technology/scn4m_subm/tech/tech.py | 7 +- 13 files changed, 90 insertions(+), 51 deletions(-) create mode 100755 compiler/tests/config_data.py create mode 100644 compiler/tests/model_data/data_4b_16word_1way_sae_measures.csv create mode 100644 compiler/tests/model_data/data_4b_16word_1way_sae_model.csv create mode 100644 compiler/tests/model_data/data_4b_16word_1way_wl_measures.csv create mode 100644 compiler/tests/model_data/data_4b_16word_1way_wl_model.csv delete mode 100644 compiler/tests/model_data/data_sae_measures.csv delete mode 100644 compiler/tests/model_data/data_sae_model.csv delete mode 100644 compiler/tests/model_data/data_wl_measures.csv delete mode 100644 compiler/tests/model_data/data_wl_model.csv diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 059227e1..12174a4b 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -235,7 +235,7 @@ class model_check(delay): read_port = self.read_ports[0] #only test the first read port self.targ_read_ports = [read_port] self.targ_write_ports = [self.write_ports[0]] - debug.info(1,"Bitline swing test: corner {}".format(self.corner)) + debug.info(1,"Model test: corner {}".format(self.corner)) (success, wl_delays, sae_delays, wl_slews, sae_slews)=self.run_delay_simulation() debug.check(success, "Model measurements Failed: period={}".format(self.period)) wl_model_delays, sae_model_delays = self.get_model_delays(read_port) diff --git a/compiler/tests/config_data.py b/compiler/tests/config_data.py new file mode 100755 index 00000000..31a28dfe --- /dev/null +++ b/compiler/tests/config_data.py @@ -0,0 +1,9 @@ +word_size = 1 +num_words = 16 + +tech_name = "freepdk45" +process_corners = ["TT", "FF"] +supply_voltages = [1.0] +temperatures = [25] + + diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index ac10b98c..d3906d39 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -21,30 +21,33 @@ class data_collection(openram_test): def runTest(self): self.init_data_gen() + word_size, num_words, words_per_row = 4, 16, 1 + self.evalulate_sram_on_corners(word_size, num_words, words_per_row) + globals.end_openram() + + def evalulate_sram_on_corners(self, word_size, num_words, words_per_row): + """Performs corner analysis on a single SRAM configuration""" + self.create_sram(word_size, num_words, words_per_row) #Run on one size to initialize CSV writing (csv names come from return value). Strange, but it is okay for now. - sram_data = self.get_sram_data(1,16,1) - self.initialize_csv_file(sram_data) - - self.add_sram_data_to_csv(sram_data, 1, 16, 1) - - #Run openRAM for several size configurations - #word_size_list, num_word_list, words_per_row_list = self.get_sram_configs() - word_size_list, num_word_list, words_per_row_list = [4], [16], [1] #for quick testing. - for word_size in word_size_list: - for num_word in num_word_list: - for words_per_row in words_per_row_list: - #Unfortunately, init needs to be called everytime - self.init_data_gen() - sram_data = self.get_sram_data(word_size, num_word, words_per_row) - self.add_sram_data_to_csv(sram_data, word_size, num_word, words_per_row) + corner_gen = self.corner_combination_generator() + init_corner = next(corner_gen) + sram_data = self.get_sram_data(init_corner) + self.initialize_csv_file(sram_data, word_size, num_words, words_per_row) + self.add_sram_data_to_csv(sram_data, word_size, num_words, words_per_row, init_corner) + #Run openRAM for all corners + for corner in corner_gen: + sram_data = self.get_sram_data(corner) + self.add_sram_data_to_csv(sram_data, word_size, num_words, words_per_row, corner) + self.close_files() debug.info(1,"Data Generated") - globals.end_openram() def init_data_gen(self): """Initialization for the data test to run""" - globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + globals.init_openram("config_data") + if OPTS.tech_name == "scmos": + debug.warning("Device models not up to date with scn4m technology.") OPTS.spice_name="hspice" #Much faster than ngspice. OPTS.trim_netlist = False OPTS.netlist_only = True @@ -58,7 +61,18 @@ class data_collection(openram_test): """Closes all files stored in the file dict""" for key,file in self.csv_files.items(): file.close() - + + def corner_combination_generator(self): + """Generates corner using a combination of values from config file""" + processes = OPTS.process_corners + voltages = OPTS.supply_voltages + temperatures = OPTS.temperatures + for proc in processes: + for volt in voltages: + for temp in temperatures: + yield (proc, volt, temp) + + def get_sram_configs(self): """Generate lists of wordsizes, number of words, and column mux size (words per row) to be tested.""" min_word_size = 1 @@ -70,28 +84,32 @@ class data_collection(openram_test): words_per_row = [1] return word_sizes, num_words, words_per_row - def add_sram_data_to_csv(self, sram_data, word_size, num_words, words_per_row): + def add_sram_data_to_csv(self, sram_data, word_size, num_words, words_per_row, corner): """Writes data to its respective CSV file. There is a CSV for each measurement target (wordline, sense amp enable, and models)""" - sram_specs = [word_size,num_words,words_per_row] + sram_specs = [word_size,num_words,words_per_row,*corner] for data_name, data_values in sram_data.items(): self.csv_writers[data_name].writerow(sram_specs+sram_data[data_name]) debug.info(2,"Data Added to CSV file.") - def initialize_csv_file(self, sram_data): + def initialize_csv_file(self, sram_data, word_size, num_words, words_per_row): """Opens a CSV file and writer for every data set being written (wl/sae measurements and model values)""" #CSV File writing header_dict = self.delay_obj.get_all_signal_names() self.csv_files = {} self.csv_writers = {} for data_name, header_list in header_dict.items(): - self.csv_files[data_name] = open('{}data_{}.csv'.format(MODEL_DIR,data_name), 'w') - fields = ('word_size', 'num_words', 'words_per_row', *header_list) + file_name = '{}data_{}b_{}word_{}way_{}.csv'.format(MODEL_DIR, + word_size, + num_words, + words_per_row, + data_name) + self.csv_files[data_name] = open(file_name, 'w') + fields = ('word_size', 'num_words', 'words_per_row', 'process', 'voltage', 'temp', *header_list) self.csv_writers[data_name] = csv.writer(self.csv_files[data_name], lineterminator = '\n') self.csv_writers[data_name].writerow(fields) - - def get_sram_data(self, word_size, num_words, words_per_row): - """Generates the SRAM based on input configuration and returns the data.""" - from characterizer import model_check + + def create_sram(self, word_size, num_words, words_per_row): + """Generates the SRAM based on input configuration.""" c = sram_config(word_size=word_size, num_words=num_words, num_banks=1) @@ -99,19 +117,21 @@ class data_collection(openram_test): #if word_size*num_words < 256: c.words_per_row=words_per_row #Force no column mux until incorporated into analytical delay. - debug.info(1, "Getting data for {} bit, {} words SRAM with 1 bank".format(word_size, num_words)) - s = sram(c, name="sram_{}ws_{}words".format(word_size, num_words)) + debug.info(1, "Creating SRAM: {} bit, {} words, with 1 bank".format(word_size, num_words)) + self.sram = sram(c, name="sram_{}ws_{}words".format(word_size, num_words)) - tempspice = OPTS.openram_temp + "temp.sp" - s.sp_write(tempspice) - - corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - self.delay_obj = model_check(s.s, tempspice, corner) + self.sram_spice = OPTS.openram_temp + "temp.sp" + self.sram.sp_write(self.sram_spice) + + def get_sram_data(self, corner): + """Generates the delay object using the corner and runs a simulation for data.""" + from characterizer import model_check + self.delay_obj = model_check(self.sram.s, self.sram_spice, corner) import tech #Only 1 at a time - probe_address = "1" * s.s.addr_size - probe_data = s.s.word_size - 1 + probe_address = "1" * self.sram.s.addr_size + probe_data = self.sram.s.word_size - 1 loads = [tech.spice["msflop_in_cap"]*4] slews = [tech.spice["rise_time"]*2] diff --git a/compiler/tests/model_data/data_4b_16word_1way_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_sae_measures.csv new file mode 100644 index 00000000..5b11f78a --- /dev/null +++ b/compiler/tests/model_data/data_4b_16word_1way_sae_measures.csv @@ -0,0 +1,3 @@ +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 +4,16,1,TT,1.0,25,0.021103999999999998,0.0061908,0.018439,0.017329999999999998,0.0094258,0.018392000000000002,0.011755000000000002 +4,16,1,FF,1.0,25,0.019583,0.005128,0.017439,0.015281,0.008443599999999999,0.017213000000000003,0.010389 diff --git a/compiler/tests/model_data/data_4b_16word_1way_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_sae_model.csv new file mode 100644 index 00000000..ed1b6dfb --- /dev/null +++ b/compiler/tests/model_data/data_4b_16word_1way_sae_model.csv @@ -0,0 +1,3 @@ +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 +4,16,1,TT,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 +4,16,1,FF,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 diff --git a/compiler/tests/model_data/data_4b_16word_1way_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_wl_measures.csv new file mode 100644 index 00000000..64329d3a --- /dev/null +++ b/compiler/tests/model_data/data_4b_16word_1way_wl_measures.csv @@ -0,0 +1,3 @@ +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +4,16,1,TT,1.0,25,0.018438,0.0092547,0.013922,0.008679300000000001 +4,16,1,FF,1.0,25,0.017261,0.008002500000000001,0.012757,0.0077545 diff --git a/compiler/tests/model_data/data_4b_16word_1way_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_wl_model.csv new file mode 100644 index 00000000..91363e48 --- /dev/null +++ b/compiler/tests/model_data/data_4b_16word_1way_wl_model.csv @@ -0,0 +1,3 @@ +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +4,16,1,TT,1.0,25,4.4,12.4,5.8,5.4 +4,16,1,FF,1.0,25,4.4,12.4,5.8,5.4 diff --git a/compiler/tests/model_data/data_sae_measures.csv b/compiler/tests/model_data/data_sae_measures.csv deleted file mode 100644 index 29b9ee07..00000000 --- a/compiler/tests/model_data/data_sae_measures.csv +++ /dev/null @@ -1,3 +0,0 @@ -word_size,num_words,words_per_row,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 -1,16,1,0.1073407,0.010667,0.1317698,0.0910598,0.04802989999999999,0.1304522,0.0164942 -4,16,1,0.10635089999999998,0.010598499999999999,0.1315219,0.0911604,0.048601,0.12349109999999999,0.05874360000000001 diff --git a/compiler/tests/model_data/data_sae_model.csv b/compiler/tests/model_data/data_sae_model.csv deleted file mode 100644 index 25a56d9e..00000000 --- a/compiler/tests/model_data/data_sae_model.csv +++ /dev/null @@ -1,3 +0,0 @@ -word_size,num_words,words_per_row,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 -1,16,1,5.0,0.75,4.5,5.5,2.5,4.5,1.09375 -4,16,1,5.0,0.75,4.5,5.5,2.5,4.5,1.09375 diff --git a/compiler/tests/model_data/data_wl_measures.csv b/compiler/tests/model_data/data_wl_measures.csv deleted file mode 100644 index 95aa4044..00000000 --- a/compiler/tests/model_data/data_wl_measures.csv +++ /dev/null @@ -1,3 +0,0 @@ -word_size,num_words,words_per_row,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -1,16,1,0.1257435,0.0364958,0.051171100000000004,0.020759299999999998 -4,16,1,0.124175,0.03654059999999999,0.0507837,0.0433494 diff --git a/compiler/tests/model_data/data_wl_model.csv b/compiler/tests/model_data/data_wl_model.csv deleted file mode 100644 index ae601f49..00000000 --- a/compiler/tests/model_data/data_wl_model.csv +++ /dev/null @@ -1,3 +0,0 @@ -word_size,num_words,words_per_row,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -1,16,1,1.5,21.833333333333332,2.0,1.1666666666666665 -4,16,1,2.5,11.166666666666666,2.0,3.1666666666666665 diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index ff9b5169..a0ba8cbc 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -271,7 +271,12 @@ spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/models_nom/PMOS_VTG.inc",SPICE "FF" : [SPICE_MODEL_DIR+"/models_ff/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_ff/NMOS_VTG.inc"], "SF" : [SPICE_MODEL_DIR+"/models_ss/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_ff/NMOS_VTG.inc"], "FS" : [SPICE_MODEL_DIR+"/models_ff/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_ss/NMOS_VTG.inc"], - "SS" : [SPICE_MODEL_DIR+"/models_ss/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_ss/NMOS_VTG.inc"]} + "SS" : [SPICE_MODEL_DIR+"/models_ss/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_ss/NMOS_VTG.inc"], + "ST" : [SPICE_MODEL_DIR+"/models_ss/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_nom/NMOS_VTG.inc"], + "TS" : [SPICE_MODEL_DIR+"/models_nom/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_ss/NMOS_VTG.inc"], + "FT" : [SPICE_MODEL_DIR+"/models_ff/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_nom/NMOS_VTG.inc"], + "TF" : [SPICE_MODEL_DIR+"/models_nom/PMOS_VTG.inc",SPICE_MODEL_DIR+"/models_ff/NMOS_VTG.inc"], + } #spice stimulus related variables spice["feasible_period"] = 5 # estimated feasible period in ns diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index fe2d686a..1add9937 100755 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -236,7 +236,12 @@ spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+" "FF" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], "FS" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], "SF" : [SPICE_MODEL_DIR+"/ss/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], - "SS" : [SPICE_MODEL_DIR+"/ss/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"] } + "SS" : [SPICE_MODEL_DIR+"/ss/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], + "ST" : [SPICE_MODEL_DIR+"/ss/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"], + "TS" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], + "FT" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"], + "TF" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], + } #spice stimulus related variables From d527b7da62d3a85047632d8e7072d8be42ce724b Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 23 Jan 2019 13:19:35 -0800 Subject: [PATCH 15/29] Added delay error calculations --- compiler/tests/delay_data_collection.py | 43 +++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index d3906d39..6a8c9fa8 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -13,6 +13,7 @@ import debug import csv from sram import sram from sram_config import sram_config +import pandas as pd MODEL_DIR = "model_data/" @@ -22,10 +23,43 @@ class data_collection(openram_test): self.init_data_gen() word_size, num_words, words_per_row = 4, 16, 1 - self.evalulate_sram_on_corners(word_size, num_words, words_per_row) + #Get data and write to CSV + self.save_data_sram_corners(word_size, num_words, words_per_row) + + #Only care about the measured data for now, select from all file names. Names are defined in model_check + wl_dataframe, sae_dataframe = self.get_csv_data() + self.evaluate_data(wl_dataframe, sae_dataframe) + globals.end_openram() - def evalulate_sram_on_corners(self, word_size, num_words, words_per_row): + def get_csv_data(self): + """Hardcoded Hack to get the measurement data from the csv into lists. """ + wl_files_name = [file_name for file_name in self.file_names if "wl_measures" in file_name][0] + sae_files_name = [file_name for file_name in self.file_names if "sae_measures" in file_name][0] + wl_dataframe = pd.read_csv(wl_files_name,encoding='utf-8') + sae_dataframe = pd.read_csv(sae_files_name,encoding='utf-8') + return wl_dataframe,sae_dataframe + + def evaluate_data(self, wl_dataframe, sae_dataframe): + """Analyze the delay error and variation error""" + delay_error = self.calculate_delay_error(wl_dataframe, sae_dataframe) + debug.info(1, "Delay errors:\n{}".format(delay_error)) + + def calculate_delay_error(self, wl_dataframe, sae_dataframe): + """Calculates the percentage difference in delays between the wordline and sense amp enable""" + start_data_pos = len(self.config_fields) #items before this point are configuration related + error_list = [] + row_count = 0 + for wl_row, sae_row in zip(wl_dataframe.itertuples(), sae_dataframe.itertuples()): + debug.info(1, "wl_row:\n{}".format(wl_row)) + wl_sum = sum(wl_row[start_data_pos+1:]) + debug.info(1, "wl_sum:\n{}".format(wl_sum)) + sae_sum = sum(sae_row[start_data_pos+1:]) + error_list.append(abs((wl_sum-sae_sum)/wl_sum)) + + return error_list + + def save_data_sram_corners(self, word_size, num_words, words_per_row): """Performs corner analysis on a single SRAM configuration""" self.create_sram(word_size, num_words, words_per_row) #Run on one size to initialize CSV writing (csv names come from return value). Strange, but it is okay for now. @@ -97,14 +131,17 @@ class data_collection(openram_test): header_dict = self.delay_obj.get_all_signal_names() self.csv_files = {} self.csv_writers = {} + self.file_names = [] for data_name, header_list in header_dict.items(): file_name = '{}data_{}b_{}word_{}way_{}.csv'.format(MODEL_DIR, word_size, num_words, words_per_row, data_name) + self.file_names.append(file_name) self.csv_files[data_name] = open(file_name, 'w') - fields = ('word_size', 'num_words', 'words_per_row', 'process', 'voltage', 'temp', *header_list) + self.config_fields = ['word_size', 'num_words', 'words_per_row', 'process', 'voltage', 'temp'] + fields = (*self.config_fields, *header_list) self.csv_writers[data_name] = csv.writer(self.csv_files[data_name], lineterminator = '\n') self.csv_writers[data_name].writerow(fields) From ee03b4ecb80255f52a4f0c9a86b99810ea1ef8d6 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 24 Jan 2019 09:25:09 -0800 Subject: [PATCH 16/29] Added some data variation checking --- compiler/modules/control_logic.py | 12 ++-- compiler/tests/config_data.py | 2 +- compiler/tests/delay_data_collection.py | 68 +++++++++++++++---- ...ata_4b_16word_1way_dc2x3_sae_measures.csv} | 3 + ...> data_4b_16word_1way_dc2x3_sae_model.csv} | 3 + ...data_4b_16word_1way_dc2x3_wl_measures.csv} | 3 + ...=> data_4b_16word_1way_dc2x3_wl_model.csv} | 3 + ...data_4b_16word_1way_dc4x2_sae_measures.csv | 6 ++ .../data_4b_16word_1way_dc4x2_sae_model.csv | 6 ++ .../data_4b_16word_1way_dc4x2_wl_measures.csv | 6 ++ .../data_4b_16word_1way_dc4x2_wl_model.csv | 6 ++ 11 files changed, 96 insertions(+), 22 deletions(-) rename compiler/tests/model_data/{data_4b_16word_1way_sae_measures.csv => data_4b_16word_1way_dc2x3_sae_measures.csv} (63%) rename compiler/tests/model_data/{data_4b_16word_1way_sae_model.csv => data_4b_16word_1way_dc2x3_sae_model.csv} (71%) rename compiler/tests/model_data/{data_4b_16word_1way_wl_measures.csv => data_4b_16word_1way_dc2x3_wl_measures.csv} (59%) rename compiler/tests/model_data/{data_4b_16word_1way_wl_model.csv => data_4b_16word_1way_dc2x3_wl_model.csv} (69%) create mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv create mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv create mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv create mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 9a945383..fbea31fb 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -112,6 +112,7 @@ class control_logic(design.design): c = reload(__import__(OPTS.replica_bitline)) replica_bitline = getattr(c, OPTS.replica_bitline) bitcell_loads = int(math.ceil(self.num_rows * parameter["rbl_height_percentage"])) + #Use a model to determine the delays with that heuristic if OPTS.use_tech_delay_chain_size: #Use tech parameters if set. delay_stages = parameter["static_delay_stages"] delay_fanout = parameter["static_fanout_per_stage"] @@ -119,6 +120,8 @@ class control_logic(design.design): self.replica_bitline = replica_bitline([delay_fanout]*delay_stages, bitcell_loads, name="replica_bitline_"+self.port_type) + if self.sram != None: #Calculate model value even for specified sizes + self.set_sen_wl_delays() else: #Otherwise, use a heuristic and/or model based sizing. #First use a heuristic @@ -126,13 +129,10 @@ class control_logic(design.design): self.replica_bitline = replica_bitline([delay_fanout_heuristic]*delay_stages_heuristic, bitcell_loads, name="replica_bitline_"+self.port_type) - - #Use a model to determine the delays with that heuristic - if self.sram != None: + if self.sram != None: #Calculate delays for potential re-sizing self.set_sen_wl_delays() - - #Resize if necessary - if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #check condition based on resizing method + #Resize if necessary, condition depends on resizing method + if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #This resizes to match fall and rise delays, can make the delay chain weird sizes. # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) # self.replica_bitline = replica_bitline(stage_list, diff --git a/compiler/tests/config_data.py b/compiler/tests/config_data.py index 31a28dfe..e605ee8c 100755 --- a/compiler/tests/config_data.py +++ b/compiler/tests/config_data.py @@ -2,7 +2,7 @@ word_size = 1 num_words = 16 tech_name = "freepdk45" -process_corners = ["TT", "FF"] +process_corners = ["TT", "FF", "SS", "SF", "FS"] supply_voltages = [1.0] temperatures = [25] diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index 6a8c9fa8..ff23a297 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -2,31 +2,35 @@ """ Run a regression test on various srams """ +import csv,sys,os +import pandas as pd import unittest from testutils import header,openram_test -import sys,os sys.path.append(os.path.join(sys.path[0],"..")) import globals from globals import OPTS import debug -import csv from sram import sram from sram_config import sram_config -import pandas as pd MODEL_DIR = "model_data/" class data_collection(openram_test): def runTest(self): - self.init_data_gen() - - word_size, num_words, words_per_row = 4, 16, 1 - #Get data and write to CSV - self.save_data_sram_corners(word_size, num_words, words_per_row) - #Only care about the measured data for now, select from all file names. Names are defined in model_check + word_size, num_words, words_per_row = 4, 16, 1 + self.init_data_gen() + self.set_delay_chain(2,3) + self.save_data_sram_corners(word_size, num_words, words_per_row) + wl_dataframe, sae_dataframe = self.get_csv_data() + self.evaluate_data(wl_dataframe, sae_dataframe) + + #Run again but with different delay chain sizes + self.init_data_gen() + self.set_delay_chain(4,2) + self.save_data_sram_corners(word_size, num_words, words_per_row) wl_dataframe, sae_dataframe = self.get_csv_data() self.evaluate_data(wl_dataframe, sae_dataframe) @@ -43,7 +47,9 @@ class data_collection(openram_test): def evaluate_data(self, wl_dataframe, sae_dataframe): """Analyze the delay error and variation error""" delay_error = self.calculate_delay_error(wl_dataframe, sae_dataframe) - debug.info(1, "Delay errors:\n{}".format(delay_error)) + debug.info(1, "Delay errors:{}".format(delay_error)) + variation_error = self.calculate_delay_variation_error(wl_dataframe, sae_dataframe) + debug.info(1, "Variation errors:{}".format(variation_error)) def calculate_delay_error(self, wl_dataframe, sae_dataframe): """Calculates the percentage difference in delays between the wordline and sense amp enable""" @@ -51,14 +57,36 @@ class data_collection(openram_test): error_list = [] row_count = 0 for wl_row, sae_row in zip(wl_dataframe.itertuples(), sae_dataframe.itertuples()): - debug.info(1, "wl_row:\n{}".format(wl_row)) + debug.info(2, "wl_row:{}".format(wl_row)) wl_sum = sum(wl_row[start_data_pos+1:]) - debug.info(1, "wl_sum:\n{}".format(wl_sum)) + debug.info(2, "wl_sum:{}".format(wl_sum)) sae_sum = sum(sae_row[start_data_pos+1:]) error_list.append(abs((wl_sum-sae_sum)/wl_sum)) - return error_list + def calculate_delay_variation_error(self, wl_dataframe, sae_dataframe): + """Measures a base delay from the first corner then the variations from that base""" + start_data_pos = len(self.config_fields) + variation_error_list = [] + count = 0 + for wl_row, sae_row in zip(wl_dataframe.itertuples(), sae_dataframe.itertuples()): + if count == 0: + #Create a base delay, variation is defined as the difference between this base + wl_base = sum(wl_row[start_data_pos+1:]) + debug.info(1, "wl_sum base:{}".format(wl_base)) + sae_base = sum(sae_row[start_data_pos+1:]) + variation_error_list.append(0.0) + else: + #Calculate the variation from the respective base and then difference between the variations + wl_sum = sum(wl_row[start_data_pos+1:]) + wl_base_diff = abs((wl_base-wl_sum)/wl_base) + sae_sum = sum(sae_row[start_data_pos+1:]) + sae_base_diff = abs((sae_base-sae_sum)/sae_base) + variation_diff = abs((wl_base_diff-sae_base_diff)/wl_base_diff) + variation_error_list.append(variation_diff) + count+=1 + return variation_error_list + def save_data_sram_corners(self, word_size, num_words, words_per_row): """Performs corner analysis on a single SRAM configuration""" self.create_sram(word_size, num_words, words_per_row) @@ -80,17 +108,25 @@ class data_collection(openram_test): def init_data_gen(self): """Initialization for the data test to run""" globals.init_openram("config_data") + from tech import parameter + global parameter if OPTS.tech_name == "scmos": debug.warning("Device models not up to date with scn4m technology.") OPTS.spice_name="hspice" #Much faster than ngspice. OPTS.trim_netlist = False OPTS.netlist_only = True OPTS.analytical_delay = False + OPTS.use_tech_delay_chain_size = True # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer reload(characterizer) + def set_delay_chain(self, stages, fanout): + """Force change the parameter in the tech file to specify a delay chain configuration""" + parameter["static_delay_stages"] = stages + parameter["static_fanout_per_stage"] = fanout + def close_files(self): """Closes all files stored in the file dict""" for key,file in self.csv_files.items(): @@ -133,10 +169,12 @@ class data_collection(openram_test): self.csv_writers = {} self.file_names = [] for data_name, header_list in header_dict.items(): - file_name = '{}data_{}b_{}word_{}way_{}.csv'.format(MODEL_DIR, + file_name = '{}data_{}b_{}word_{}way_dc{}x{}_{}.csv'.format(MODEL_DIR, word_size, num_words, - words_per_row, + words_per_row, + parameter["static_delay_stages"], + parameter["static_fanout_per_stage"], data_name) self.file_names.append(file_name) self.csv_files[data_name] = open(file_name, 'w') diff --git a/compiler/tests/model_data/data_4b_16word_1way_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv similarity index 63% rename from compiler/tests/model_data/data_4b_16word_1way_sae_measures.csv rename to compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv index 5b11f78a..696761c8 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_sae_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv @@ -1,3 +1,6 @@ word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 4,16,1,TT,1.0,25,0.021103999999999998,0.0061908,0.018439,0.017329999999999998,0.0094258,0.018392000000000002,0.011755000000000002 4,16,1,FF,1.0,25,0.019583,0.005128,0.017439,0.015281,0.008443599999999999,0.017213000000000003,0.010389 +4,16,1,SS,1.0,25,0.022932,0.0074386999999999995,0.019891000000000002,0.019466,0.010501,0.019849,0.013432 +4,16,1,SF,1.0,25,0.019301,0.007507700000000001,0.016878999999999998,0.018834,0.010293,0.017156,0.01299 +4,16,1,FS,1.0,25,0.023601,0.0045925,0.020515,0.015586,0.0085521,0.019967,0.010449 diff --git a/compiler/tests/model_data/data_4b_16word_1way_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv similarity index 71% rename from compiler/tests/model_data/data_4b_16word_1way_sae_model.csv rename to compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv index ed1b6dfb..01c2e0e4 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_sae_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv @@ -1,3 +1,6 @@ word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 4,16,1,TT,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 4,16,1,FF,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 +4,16,1,SS,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 +4,16,1,SF,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 +4,16,1,FS,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 diff --git a/compiler/tests/model_data/data_4b_16word_1way_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv similarity index 59% rename from compiler/tests/model_data/data_4b_16word_1way_wl_measures.csv rename to compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv index 64329d3a..b3b65af3 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_wl_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv @@ -1,3 +1,6 @@ word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 4,16,1,TT,1.0,25,0.018438,0.0092547,0.013922,0.008679300000000001 4,16,1,FF,1.0,25,0.017261,0.008002500000000001,0.012757,0.0077545 +4,16,1,SS,1.0,25,0.019962,0.010683,0.015394,0.009734999999999999 +4,16,1,SF,1.0,25,0.017044,0.010483999999999999,0.012825,0.0094333 +4,16,1,FS,1.0,25,0.020398,0.0078018,0.015243999999999999,0.0079892 diff --git a/compiler/tests/model_data/data_4b_16word_1way_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv similarity index 69% rename from compiler/tests/model_data/data_4b_16word_1way_wl_model.csv rename to compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv index 91363e48..696f9952 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_wl_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv @@ -1,3 +1,6 @@ word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 4,16,1,TT,1.0,25,4.4,12.4,5.8,5.4 4,16,1,FF,1.0,25,4.4,12.4,5.8,5.4 +4,16,1,SS,1.0,25,4.4,12.4,5.8,5.4 +4,16,1,SF,1.0,25,4.4,12.4,5.8,5.4 +4,16,1,FS,1.0,25,4.4,12.4,5.8,5.4 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv new file mode 100644 index 00000000..66366afd --- /dev/null +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv @@ -0,0 +1,6 @@ +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 +4,16,1,TT,1.0,25,0.021141999999999998,0.006257400000000001,0.015622,0.014144,0.017741,0.013434,0.009287,0.018439999999999998,0.011276 +4,16,1,FF,1.0,25,0.019646,0.0052736,0.014601,0.012589,0.016537,0.011916999999999999,0.0083483,0.017246,0.0099574 +4,16,1,SS,1.0,25,0.022917,0.0074182,0.016901,0.015895,0.019267,0.015147,0.010306000000000001,0.01986,0.012834 +4,16,1,SF,1.0,25,0.019208,0.007500799999999999,0.014421999999999999,0.015359999999999999,0.016408,0.014695999999999999,0.010128,0.017086999999999998,0.012516000000000001 +4,16,1,FS,1.0,25,0.023644000000000002,0.0046118,0.017239,0.01283,0.019428,0.012081999999999999,0.0085141,0.020073,0.009944 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv new file mode 100644 index 00000000..c45a8df4 --- /dev/null +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv @@ -0,0 +1,6 @@ +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 +4,16,1,TT,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 +4,16,1,FF,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 +4,16,1,SS,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 +4,16,1,SF,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 +4,16,1,FS,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv new file mode 100644 index 00000000..e08c6fa5 --- /dev/null +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv @@ -0,0 +1,6 @@ +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +4,16,1,TT,1.0,25,0.018481,0.0093054,0.013848,0.008683999999999999 +4,16,1,FF,1.0,25,0.017331,0.0080465,0.012700999999999999,0.0077613000000000005 +4,16,1,SS,1.0,25,0.019895,0.010660000000000001,0.015356999999999999,0.009745 +4,16,1,SF,1.0,25,0.016984000000000003,0.010501,0.012796,0.009405700000000001 +4,16,1,FS,1.0,25,0.020445,0.007772300000000001,0.015284,0.0079428 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv new file mode 100644 index 00000000..696f9952 --- /dev/null +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv @@ -0,0 +1,6 @@ +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +4,16,1,TT,1.0,25,4.4,12.4,5.8,5.4 +4,16,1,FF,1.0,25,4.4,12.4,5.8,5.4 +4,16,1,SS,1.0,25,4.4,12.4,5.8,5.4 +4,16,1,SF,1.0,25,4.4,12.4,5.8,5.4 +4,16,1,FS,1.0,25,4.4,12.4,5.8,5.4 From 242a63accba79707646e3a19394cdb339d5898e9 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 29 Jan 2019 16:43:30 -0800 Subject: [PATCH 17/29] Fixed issues introduced by pdriver additions in model unit test --- compiler/characterizer/delay.py | 3 +- compiler/characterizer/model_check.py | 50 ++++++++++++++++++--------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index a327c548..7bd36cd1 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -904,7 +904,8 @@ class delay(simulation): port_data[port][mname].append(bank_delay[port].slew/1e3) else: debug.error("Measurement name not recognized: {}".format(mname),1) - sram_data = { "min_period": 0, + period_margin = 0.1 + sram_data = { "min_period": bank_delay[0]*2*period_margin, "leakage_power": power.leakage} return (sram_data,port_data) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 12174a4b..e76f5427 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -22,41 +22,44 @@ class model_check(delay): delay.__init__(self,sram,spfile,corner) self.period = tech.spice["feasible_period"] self.create_data_names() - + def create_data_names(self): self.wl_meas_name, self.wl_model_name = "wl_measures", "wl_model" self.sae_meas_name, self.sae_model_name = "sae_measures", "sae_model" def create_measurement_names(self): """Create measurement names. The names themselves currently define the type of measurement""" - #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements. - self.wl_delay_meas_names = ["delay_wl_en_bar", "delay_wl_en", "delay_dvr_en_bar", "delay_wl"] - self.wl_slew_meas_names = ["slew_wl_gated_clk_bar","slew_wl_en_bar", "slew_wl_en", "slew_drv_en_bar", "slew_wl"] + wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] + wl_driver_delay_names = ["delay_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] + sen_driver_delay_names = ["delay_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] + + wl_en_driver_slew_names = ["slew_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] + wl_driver_slew_names = ["slew_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] + sen_driver_slew_names = ["slew_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] + + self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"] + self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"] dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names - self.sae_delay_meas_names = ["delay_pre_sen", "delay_sen_bar", "delay_sen"] - self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen", "slew_sen_bar", "slew_sen"] + self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"] + self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"]+sen_driver_slew_names+["slew_sen"] def create_signal_names(self): + """Creates list of the signal names used in the spice file along the wl and sen paths.""" delay.create_signal_names(self) #Signal names are all hardcoded, need to update to make it work for probe address and different configurations. - self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xbuf_wl_en.zb_int", "Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_15", "Xsram.Xbank0.wl_15"] + wl_en_driver_signals = ["Xsram.Xcontrol0.Xbuf_wl_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] + wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver0.Xwl_driver_inv0.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] + sen_driver_signals = ["Xsram.Xcontrol0.Xbuf_s_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())] + ["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] + + self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar"]+wl_en_driver_signals+["Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_15"]+wl_driver_signals+["Xsram.Xbank0.wl_15"] self.rbl_en_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"] + delay_chain_signal_names - self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en", "Xsram.Xcontrol0.Xbuf_s_en.zb_int", "Xsram.s_en0"] + self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+sen_driver_signals+["Xsram.s_en0"] - def get_all_signal_names(self): - """Returns all signals names as a dict indexed by hardcoded names. Useful for writing the head of the CSV.""" - name_dict = {} - #Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names. - name_dict[self.wl_meas_name] = self.wl_signal_names[1:] - name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured. - name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:]+self.sae_signal_names[1:] - name_dict[self.sae_model_name] = name_dict["sae_measures"] - return name_dict def create_measurement_objects(self): """Create the measurements used for read and write ports""" @@ -183,8 +186,21 @@ class model_check(delay): return self.sram.control_logic_rw.get_wl_sen_delays() def get_num_delay_stages(self): + """Gets the number of stages in the delay chain from the control logic""" return len(self.sram.control_logic_rw.replica_bitline.delay_fanout_list) + def get_num_wl_en_driver_stages(self): + """Gets the number of stages in the wl_en driver from the control logic""" + return self.sram.control_logic_rw.wl_en_driver.num_stages + + def get_num_sen_driver_stages(self): + """Gets the number of stages in the sen driver from the control logic""" + return self.sram.control_logic_rw.s_en_driver.num_stages + + def get_num_wl_driver_stages(self): + """Gets the number of stages in the wordline driver from the control logic""" + return self.sram.bank.wordline_driver.inv.num_stages + def scale_delays(self, delay_list): """Takes in a list of measured delays and convert it to simple units to easily compare to model values.""" converted_values = [] From c10c9e400940c7fe72f5665eb0e687c453b4870e Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 29 Jan 2019 23:02:28 -0800 Subject: [PATCH 18/29] Refactored some code and other additional improvements. --- compiler/characterizer/delay.py | 14 ++-- compiler/characterizer/model_check.py | 97 +++++++++++++++++++-------- 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 7bd36cd1..14bdcd11 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -36,10 +36,6 @@ class delay(simulation): self.period = 0 self.set_load_slew(0,0) self.set_corner(corner) - self.create_signal_names() - - #Create global measure names. Should maybe be an input at some point. - self.create_measurement_names() def create_measurement_names(self): """Create measurement names. The names themselves currently define the type of measurement""" @@ -660,7 +656,7 @@ class delay(simulation): self.probe_address = probe_address self.probe_data = probe_data self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data) - + self.wordline_row = self.get_address_row_number(probe_address) self.prepare_netlist() def get_data_bit_column_number(self, probe_address, probe_data): @@ -670,7 +666,11 @@ class delay(simulation): else: col_address = 0 bl_column = int(self.sram.words_per_row*probe_data + col_address) - return bl_column + return bl_column + + def get_address_row_number(self, probe_address): + """Calculates wordline row number of data bit under test using address and column mux size""" + return int(probe_address[self.sram.col_addr_size:],2) def prepare_netlist(self): """ Prepare a trimmed netlist and regular netlist. """ @@ -703,6 +703,8 @@ class delay(simulation): char_sram_data = {} self.set_probe(probe_address, probe_data) + self.create_signal_names() + self.create_measurement_names() self.create_measurement_objects() self.load=max(loads) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index e76f5427..2cf0d25d 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -29,22 +29,22 @@ class model_check(delay): def create_measurement_names(self): """Create measurement names. The names themselves currently define the type of measurement""" - wl_en_driver_delay_names = ["delay_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] - wl_driver_delay_names = ["delay_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] - sen_driver_delay_names = ["delay_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] - - wl_en_driver_slew_names = ["slew_wl_en_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] - wl_driver_slew_names = ["slew_wl_dvr_{}".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] - sen_driver_slew_names = ["slew_sen_dvr_{}".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] - + #Create delay measurement names + wl_en_driver_delay_names = ["delay_wl_en_dvr{}_".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] + wl_driver_delay_names = ["delay_wl_dvr{}_".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] + sen_driver_delay_names = ["delay_sen_dvr{}_".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] + dc_delay_names = ["delay_delay_chain_stage{}_".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] self.wl_delay_meas_names = wl_en_driver_delay_names+["delay_wl_en", "delay_wl_bar"]+wl_driver_delay_names+["delay_wl"] - self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"] - - dc_delay_names = ["delay_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names - dc_slew_names = ["slew_delay_chain_stage_{}".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] - self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"] + + #Create slew measurement names + wl_en_driver_slew_names = ["slew_wl_en_dvr{}_".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] + wl_driver_slew_names = ["slew_wl_dvr{}_".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] + sen_driver_slew_names = ["slew_sen_dvr{}_".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] + dc_slew_names = ["slew_delay_chain_stage{}_".format(stage) for stage in range(1,self.get_num_delay_stages()+1)] + self.wl_slew_meas_names = ["slew_wl_gated_clk_bar"]+wl_en_driver_slew_names+["slew_wl_en", "slew_wl_bar"]+wl_driver_slew_names+["slew_wl"] + self.rbl_slew_meas_names = ["slew_rbl_gated_clk_bar","slew_gated_clk_nand", "slew_delay_chain_in"]+dc_slew_names self.sae_slew_meas_names = ["slew_replica_bl0", "slew_pre_sen"]+sen_driver_slew_names+["slew_sen"] def create_signal_names(self): @@ -52,13 +52,21 @@ class model_check(delay): delay.create_signal_names(self) #Signal names are all hardcoded, need to update to make it work for probe address and different configurations. wl_en_driver_signals = ["Xsram.Xcontrol0.Xbuf_wl_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] - wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver0.Xwl_driver_inv0.Zb{}_int".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] + wl_driver_signals = ["Xsram.Xbank0.Xwordline_driver0.Xwl_driver_inv{}.Zb{}_int".format(self.wordline_row, stage) for stage in range(1,self.get_num_wl_driver_stages())] sen_driver_signals = ["Xsram.Xcontrol0.Xbuf_s_en.Zb{}_int".format(stage) for stage in range(1,self.get_num_sen_driver_stages())] - delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())] + ["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] + delay_chain_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_{}".format(stage) for stage in range(1,self.get_num_delay_stages())] - self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar"]+wl_en_driver_signals+["Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_15"]+wl_driver_signals+["Xsram.Xbank0.wl_15"] - self.rbl_en_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"] + delay_chain_signal_names - self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+sen_driver_signals+["Xsram.s_en0"] + self.wl_signal_names = ["Xsram.Xcontrol0.gated_clk_bar"]+\ + wl_en_driver_signals+\ + ["Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_{}".format(self.wordline_row)]+\ + wl_driver_signals+\ + ["Xsram.Xbank0.wl_{}".format(self.wordline_row)] + self.rbl_en_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"]+\ + delay_chain_signal_names+\ + ["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] + self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+\ + sen_driver_signals+\ + ["Xsram.s_en0"] def create_measurement_objects(self): @@ -74,8 +82,16 @@ class model_check(delay): targ_dir = "FALL" for i in range(1, len(self.wl_signal_names)): - self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1], self.wl_signal_names[i-1], self.wl_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) - self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1], self.wl_signal_names[i-1], trig_dir, measure_scale=1e9)) + self.wl_meas_objs.append(delay_measure(self.wl_delay_meas_names[i-1], + self.wl_signal_names[i-1], + self.wl_signal_names[i], + trig_dir, + targ_dir, + measure_scale=1e9)) + self.wl_meas_objs.append(slew_measure(self.wl_slew_meas_names[i-1], + self.wl_signal_names[i-1], + trig_dir, + measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir @@ -89,24 +105,46 @@ class model_check(delay): targ_dir = "FALL" #Add measurements from gated_clk_bar to RBL for i in range(1, len(self.rbl_en_signal_names)): - self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1], self.rbl_en_signal_names[i-1], self.rbl_en_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) - self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1], self.rbl_en_signal_names[i-1], trig_dir, measure_scale=1e9)) + self.sae_meas_objs.append(delay_measure(self.rbl_delay_meas_names[i-1], + self.rbl_en_signal_names[i-1], + self.rbl_en_signal_names[i], + trig_dir, + targ_dir, + measure_scale=1e9)) + self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[i-1], + self.rbl_en_signal_names[i-1], + trig_dir, + measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir - self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1], self.rbl_en_signal_names[-1], trig_dir, measure_scale=1e9)) + self.sae_meas_objs.append(slew_measure(self.rbl_slew_meas_names[-1], + self.rbl_en_signal_names[-1], + trig_dir, + measure_scale=1e9)) #Add measurements from rbl_out to sae. Trigger directions do not invert from previous stage due to RBL. trig_dir = "FALL" targ_dir = "RISE" #Add measurements from gated_clk_bar to RBL for i in range(1, len(self.sae_signal_names)): - self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1], self.sae_signal_names[i-1], self.sae_signal_names[i], trig_dir, targ_dir, measure_scale=1e9)) - self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1], self.sae_signal_names[i-1], trig_dir, measure_scale=1e9)) + self.sae_meas_objs.append(delay_measure(self.sae_delay_meas_names[i-1], + self.sae_signal_names[i-1], + self.sae_signal_names[i], + trig_dir, + targ_dir, + measure_scale=1e9)) + self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[i-1], + self.sae_signal_names[i-1], + trig_dir, + measure_scale=1e9)) temp_dir = trig_dir trig_dir = targ_dir targ_dir = temp_dir - self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1], self.sae_signal_names[-1], trig_dir, measure_scale=1e9)) + self.sae_meas_objs.append(slew_measure(self.sae_slew_meas_names[-1], + self.sae_signal_names[-1], + trig_dir, + measure_scale=1e9)) def write_delay_measures(self): """ @@ -122,7 +160,8 @@ class model_check(delay): self.write_measures_read_port(read_port) def get_delay_measure_variants(self, port, measure_obj): - """Get the measurement values that can either vary from simulation to simulation (vdd, address) or port to port (time delays)""" + """Get the measurement values that can either vary from simulation to simulation (vdd, address) + or port to port (time delays)""" #Return value is intended to match the delay measure format: trig_td, targ_td, vdd, port #Assuming only read 0 for now if not (type(measure_obj) is delay_measure or type(measure_obj) is slew_measure): @@ -242,9 +281,11 @@ class model_check(delay): def analyze(self, probe_address, probe_data, slews, loads): """Measures entire delay path along the wordline and sense amp enable and compare it to the model delays.""" - self.set_probe(probe_address, probe_data) self.load=max(loads) self.slew=max(slews) + self.set_probe(probe_address, probe_data) + self.create_signal_names() + self.create_measurement_names() self.create_measurement_objects() data_dict = {} From 45fceb1f4eeab794109c7b3b4852cd751af47298 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 30 Jan 2019 11:43:47 -0800 Subject: [PATCH 19/29] Added word per row to sram config with a default arguement to fix test. --- compiler/characterizer/model_check.py | 11 ++++++++++- compiler/modules/control_logic.py | 18 +++++++++--------- compiler/sram_config.py | 4 ++-- compiler/tests/delay_data_collection.py | 8 ++++---- .../data_4b_16word_1way_dc2x3_sae_measures.csv | 12 ++++++------ .../data_4b_16word_1way_dc2x3_sae_model.csv | 12 ++++++------ .../data_4b_16word_1way_dc2x3_wl_measures.csv | 12 ++++++------ .../data_4b_16word_1way_dc2x3_wl_model.csv | 12 ++++++------ .../data_4b_16word_1way_dc4x2_sae_measures.csv | 12 ++++++------ .../data_4b_16word_1way_dc4x2_sae_model.csv | 12 ++++++------ .../data_4b_16word_1way_dc4x2_wl_measures.csv | 12 ++++++------ .../data_4b_16word_1way_dc4x2_wl_model.csv | 12 ++++++------ 12 files changed, 73 insertions(+), 64 deletions(-) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index 2cf0d25d..b3e8452f 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -317,6 +317,15 @@ class model_check(delay): return data_dict - + def get_all_signal_names(self): + """Returns all signals names as a dict indexed by hardcoded names. Useful for writing the head of the CSV.""" + name_dict = {} + #Signal names are more descriptive than the measurement names, first value trimmed to match size of measurements names. + name_dict[self.wl_meas_name] = self.wl_signal_names[1:] + name_dict[self.wl_model_name] = name_dict["wl_measures"] #model uses same names as measured. + name_dict[self.sae_meas_name] = self.rbl_en_signal_names[1:]+self.sae_signal_names[1:] + name_dict[self.sae_model_name] = name_dict["sae_measures"] + return name_dict + \ No newline at end of file diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 59aa25b9..948bf33a 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -141,9 +141,9 @@ class control_logic(design.design): delay_stages = parameter["static_delay_stages"] delay_fanout = parameter["static_fanout_per_stage"] debug.info(1, "Using tech parameters to size delay chain: stages={}, fanout={}".format(delay_stages,delay_fanout)) - self.replica_bitline = replica_bitline([delay_fanout]*delay_stages, - bitcell_loads, - name="replica_bitline_"+self.port_type) + self.replica_bitline = replica_bitline("replica_bitline_"+self.port_type, + [delay_fanout]*delay_stages, + bitcell_loads) if self.sram != None: #Calculate model value even for specified sizes self.set_sen_wl_delays() @@ -159,15 +159,15 @@ class control_logic(design.design): if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #This resizes to match fall and rise delays, can make the delay chain weird sizes. # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) - # self.replica_bitline = replica_bitline(stage_list, - # bitcell_loads, - # name="replica_bitline_resized_"+self.port_type) + # self.replica_bitline = replica_bitline( "replica_bitline_resized_"+self.port_type + # stage_list, + # bitcell_loads) #This resizes based on total delay. delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) - self.replica_bitline = replica_bitline([delay_fanout]*delay_stages, - bitcell_loads, - name="replica_bitline_resized_"+self.port_type) + self.replica_bitline = replica_bitline("replica_bitline_resized_"+self.port_type, + [delay_fanout]*delay_stages, + bitcell_loads) self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing diff --git a/compiler/sram_config.py b/compiler/sram_config.py index 24e3cbc9..5edf9282 100644 --- a/compiler/sram_config.py +++ b/compiler/sram_config.py @@ -7,13 +7,13 @@ from sram_factory import factory class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, num_banks=1): + def __init__(self, word_size, num_words, num_banks=1, words_per_row=None): self.word_size = word_size self.num_words = num_words self.num_banks = num_banks # This will get over-written when we determine the organization - self.words_per_row = None + self.words_per_row = words_per_row self.compute_sizes() diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index ff23a297..39f59141 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -187,10 +187,8 @@ class data_collection(openram_test): """Generates the SRAM based on input configuration.""" c = sram_config(word_size=word_size, num_words=num_words, - num_banks=1) - #minimum 16 rows. Most sizes below 16*16 will try to automatically use less rows unless enforced. - #if word_size*num_words < 256: - c.words_per_row=words_per_row #Force no column mux until incorporated into analytical delay. + num_banks=1, + words_per_row=words_per_row) debug.info(1, "Creating SRAM: {} bit, {} words, with 1 bank".format(word_size, num_words)) self.sram = sram(c, name="sram_{}ws_{}words".format(word_size, num_words)) @@ -198,6 +196,8 @@ class data_collection(openram_test): self.sram_spice = OPTS.openram_temp + "temp.sp" self.sram.sp_write(self.sram_spice) + debug.info(1, "SRAM column address size={}".format(self.sram.s.col_addr_size)) + def get_sram_data(self, corner): """Generates the delay object using the corner and runs a simulation for data.""" from characterizer import model_check diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv index 696761c8..25f5359d 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv @@ -1,6 +1,6 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 -4,16,1,TT,1.0,25,0.021103999999999998,0.0061908,0.018439,0.017329999999999998,0.0094258,0.018392000000000002,0.011755000000000002 -4,16,1,FF,1.0,25,0.019583,0.005128,0.017439,0.015281,0.008443599999999999,0.017213000000000003,0.010389 -4,16,1,SS,1.0,25,0.022932,0.0074386999999999995,0.019891000000000002,0.019466,0.010501,0.019849,0.013432 -4,16,1,SF,1.0,25,0.019301,0.007507700000000001,0.016878999999999998,0.018834,0.010293,0.017156,0.01299 -4,16,1,FS,1.0,25,0.023601,0.0045925,0.020515,0.015586,0.0085521,0.019967,0.010449 +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0 +4,16,1,TT,1.0,25,0.020618,0.0062215,0.018563000000000003,0.017233000000000002,0.007710799999999999,0.0099965,0.045221000000000004 +4,16,1,FF,1.0,25,0.019135,0.0052523000000000005,0.017398,0.015280000000000002,0.0067718,0.009288300000000001,0.042180999999999996 +4,16,1,SS,1.0,25,0.022393999999999997,0.0074892,0.019906,0.019521999999999998,0.0087409,0.010967000000000001,0.04836 +4,16,1,SF,1.0,25,0.01874,0.007554100000000001,0.016919,0.018821,0.0086205,0.0094092,0.049122 +4,16,1,FS,1.0,25,0.022926,0.0046388,0.02054,0.015555000000000001,0.0067794,0.010772,0.041583 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv index 01c2e0e4..b79d0fbf 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv @@ -1,6 +1,6 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 -4,16,1,TT,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 -4,16,1,FF,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 -4,16,1,SS,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 -4,16,1,SF,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 -4,16,1,FS,1.0,25,8.8,2.65,6.4,7.4,4.4,6.4,2.99375 +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0 +4,16,1,TT,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 +4,16,1,FF,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 +4,16,1,SS,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 +4,16,1,SF,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 +4,16,1,FS,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv index b3b65af3..043fc729 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv @@ -1,6 +1,6 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -4,16,1,TT,1.0,25,0.018438,0.0092547,0.013922,0.008679300000000001 -4,16,1,FF,1.0,25,0.017261,0.008002500000000001,0.012757,0.0077545 -4,16,1,SS,1.0,25,0.019962,0.010683,0.015394,0.009734999999999999 -4,16,1,SF,1.0,25,0.017044,0.010483999999999999,0.012825,0.0094333 -4,16,1,FS,1.0,25,0.020398,0.0078018,0.015243999999999999,0.0079892 +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +4,16,1,TT,1.0,25,0.010512,0.0089216,0.014109,0.013643,0.014564,0.0086745 +4,16,1,FF,1.0,25,0.0096952,0.008041900000000001,0.013129,0.012268999999999999,0.013255999999999999,0.007759599999999999 +4,16,1,SS,1.0,25,0.011505,0.009873400000000001,0.015371,0.015194000000000001,0.016210000000000002,0.0097529 +4,16,1,SF,1.0,25,0.0097161,0.0096343,0.013210000000000001,0.014750000000000001,0.013464,0.0094366 +4,16,1,FS,1.0,25,0.011368999999999999,0.0082136,0.015231000000000001,0.012545,0.015907,0.0079376 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv index 696f9952..c323597a 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv @@ -1,6 +1,6 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -4,16,1,TT,1.0,25,4.4,12.4,5.8,5.4 -4,16,1,FF,1.0,25,4.4,12.4,5.8,5.4 -4,16,1,SS,1.0,25,4.4,12.4,5.8,5.4 -4,16,1,SF,1.0,25,4.4,12.4,5.8,5.4 -4,16,1,FS,1.0,25,4.4,12.4,5.8,5.4 +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +4,16,1,TT,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +4,16,1,FF,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +4,16,1,SS,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +4,16,1,SF,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +4,16,1,FS,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv index 66366afd..efc32260 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv @@ -1,6 +1,6 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 -4,16,1,TT,1.0,25,0.021141999999999998,0.006257400000000001,0.015622,0.014144,0.017741,0.013434,0.009287,0.018439999999999998,0.011276 -4,16,1,FF,1.0,25,0.019646,0.0052736,0.014601,0.012589,0.016537,0.011916999999999999,0.0083483,0.017246,0.0099574 -4,16,1,SS,1.0,25,0.022917,0.0074182,0.016901,0.015895,0.019267,0.015147,0.010306000000000001,0.01986,0.012834 -4,16,1,SF,1.0,25,0.019208,0.007500799999999999,0.014421999999999999,0.015359999999999999,0.016408,0.014695999999999999,0.010128,0.017086999999999998,0.012516000000000001 -4,16,1,FS,1.0,25,0.023644000000000002,0.0046118,0.017239,0.01283,0.019428,0.012081999999999999,0.0085141,0.020073,0.009944 +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0 +4,16,1,TT,1.0,25,0.020572,0.006256,0.015678,0.014110000000000001,0.017755,0.013415,0.0076344,0.010162,0.04153 +4,16,1,FF,1.0,25,0.01907,0.0052328,0.014619000000000002,0.0126,0.016572,0.011862,0.0067348,0.0093334,0.03898 +4,16,1,SS,1.0,25,0.022189,0.0074532999999999995,0.016968,0.015854,0.019286,0.015139,0.0087264,0.011029,0.04439 +4,16,1,SF,1.0,25,0.018689,0.007557800000000001,0.014395999999999999,0.015354,0.016425000000000002,0.014665,0.0084936,0.009411300000000001,0.044714000000000004 +4,16,1,FS,1.0,25,0.022942,0.0046639,0.017356,0.012745999999999999,0.01949,0.012069,0.006808099999999999,0.010822,0.038710999999999995 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv index c45a8df4..12a0c9e8 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv @@ -1,6 +1,6 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.zb_int,Xsram.s_en0 -4,16,1,TT,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 -4,16,1,FF,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 -4,16,1,SS,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 -4,16,1,SF,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 -4,16,1,FS,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,4.4,6.4,2.99375 +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0 +4,16,1,TT,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 +4,16,1,FF,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 +4,16,1,SS,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 +4,16,1,SF,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 +4,16,1,FS,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv index e08c6fa5..565bb307 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv @@ -1,6 +1,6 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -4,16,1,TT,1.0,25,0.018481,0.0093054,0.013848,0.008683999999999999 -4,16,1,FF,1.0,25,0.017331,0.0080465,0.012700999999999999,0.0077613000000000005 -4,16,1,SS,1.0,25,0.019895,0.010660000000000001,0.015356999999999999,0.009745 -4,16,1,SF,1.0,25,0.016984000000000003,0.010501,0.012796,0.009405700000000001 -4,16,1,FS,1.0,25,0.020445,0.007772300000000001,0.015284,0.0079428 +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +4,16,1,TT,1.0,25,0.010463,0.0089004,0.014107,0.013630000000000001,0.0146,0.0086902 +4,16,1,FF,1.0,25,0.0096519,0.008039899999999999,0.013115,0.012295,0.013235,0.0077621 +4,16,1,SS,1.0,25,0.011424,0.0098775,0.015351999999999998,0.015194000000000001,0.016227,0.0097375 +4,16,1,SF,1.0,25,0.009697500000000001,0.009592,0.013206,0.014738000000000001,0.013432,0.0094217 +4,16,1,FS,1.0,25,0.011432000000000001,0.0081985,0.015220000000000001,0.012544,0.015973,0.0079455 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv index 696f9952..c323597a 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv @@ -1,6 +1,6 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.zb_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -4,16,1,TT,1.0,25,4.4,12.4,5.8,5.4 -4,16,1,FF,1.0,25,4.4,12.4,5.8,5.4 -4,16,1,SS,1.0,25,4.4,12.4,5.8,5.4 -4,16,1,SF,1.0,25,4.4,12.4,5.8,5.4 -4,16,1,FS,1.0,25,4.4,12.4,5.8,5.4 +word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 +4,16,1,TT,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +4,16,1,FF,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +4,16,1,SS,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +4,16,1,SF,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +4,16,1,FS,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 From 8d7823e4dd83b6039731ef139e79765ca7950bf2 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 31 Jan 2019 00:26:27 -0800 Subject: [PATCH 20/29] Added delay ratio comparisons between model and measurements --- compiler/modules/control_logic.py | 26 +++--- compiler/tests/config_data.py | 3 +- compiler/tests/delay_data_collection.py | 79 +++++++++++++++---- ...data_4b_16word_1way_dc2x3_sae_measures.csv | 8 +- .../data_4b_16word_1way_dc2x3_sae_model.csv | 8 +- .../data_4b_16word_1way_dc2x3_wl_measures.csv | 8 +- .../data_4b_16word_1way_dc2x3_wl_model.csv | 8 +- ...data_4b_16word_1way_dc4x2_sae_measures.csv | 8 +- .../data_4b_16word_1way_dc4x2_sae_model.csv | 8 +- .../data_4b_16word_1way_dc4x2_wl_measures.csv | 8 +- .../data_4b_16word_1way_dc4x2_wl_model.csv | 8 +- 11 files changed, 95 insertions(+), 77 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 948bf33a..295bbca8 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -133,6 +133,7 @@ class control_logic(design.design): if (self.port_type == "rw") or (self.port_type == "r"): from importlib import reload + self.delay_chain_resized = False c = reload(__import__(OPTS.replica_bitline)) replica_bitline = getattr(c, OPTS.replica_bitline) bitcell_loads = int(math.ceil(self.num_rows * parameter["rbl_height_percentage"])) @@ -141,35 +142,36 @@ class control_logic(design.design): delay_stages = parameter["static_delay_stages"] delay_fanout = parameter["static_fanout_per_stage"] debug.info(1, "Using tech parameters to size delay chain: stages={}, fanout={}".format(delay_stages,delay_fanout)) - self.replica_bitline = replica_bitline("replica_bitline_"+self.port_type, - [delay_fanout]*delay_stages, - bitcell_loads) + self.replica_bitline = factory.create(module_type="replica_bitline", + delay_fanout_list=[delay_fanout]*delay_stages, + bitcell_loads=bitcell_loads) if self.sram != None: #Calculate model value even for specified sizes self.set_sen_wl_delays() else: #Otherwise, use a heuristic and/or model based sizing. #First use a heuristic delay_stages_heuristic, delay_fanout_heuristic = self.get_heuristic_delay_chain_size() - self.replica_bitline = replica_bitline("replica_bitline_"+self.port_type, - [delay_fanout_heuristic]*delay_stages_heuristic, - bitcell_loads) + self.replica_bitline = factory.create(module_type="replica_bitline", + delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic, + bitcell_loads=bitcell_loads) if self.sram != None: #Calculate delays for potential re-sizing self.set_sen_wl_delays() #Resize if necessary, condition depends on resizing method if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #This resizes to match fall and rise delays, can make the delay chain weird sizes. # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) - # self.replica_bitline = replica_bitline( "replica_bitline_resized_"+self.port_type - # stage_list, - # bitcell_loads) + # self.replica_bitline = factory.create(module_type="replica_bitline", + # delay_fanout_list=stage_list, + # bitcell_loads=bitcell_loads) #This resizes based on total delay. delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) - self.replica_bitline = replica_bitline("replica_bitline_resized_"+self.port_type, - [delay_fanout]*delay_stages, - bitcell_loads) + self.replica_bitline = factory.create(module_type="replica_bitline", + delay_fanout_list=[delay_fanout]*delay_stages, + bitcell_loads=bitcell_loads) self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing + self.delay_chain_resized = True self.add_mod(self.replica_bitline) diff --git a/compiler/tests/config_data.py b/compiler/tests/config_data.py index e605ee8c..08168bc3 100755 --- a/compiler/tests/config_data.py +++ b/compiler/tests/config_data.py @@ -2,7 +2,8 @@ word_size = 1 num_words = 16 tech_name = "freepdk45" -process_corners = ["TT", "FF", "SS", "SF", "FS"] +#process_corners = ["TT", "FF", "SS", "SF", "FS"] +process_corners = ["TT"] supply_voltages = [1.0] temperatures = [25] diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index 39f59141..bd2a61f7 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -28,12 +28,16 @@ class data_collection(openram_test): self.evaluate_data(wl_dataframe, sae_dataframe) #Run again but with different delay chain sizes - self.init_data_gen() - self.set_delay_chain(4,2) - self.save_data_sram_corners(word_size, num_words, words_per_row) - wl_dataframe, sae_dataframe = self.get_csv_data() - self.evaluate_data(wl_dataframe, sae_dataframe) + # self.init_data_gen() + # self.set_delay_chain(4,2) + # self.save_data_sram_corners(word_size, num_words, words_per_row) + # wl_dataframe, sae_dataframe = self.get_csv_data() + # self.evaluate_data(wl_dataframe, sae_dataframe) + model_delay_ratios, meas_delay_ratios, ratio_error = self.compare_model_to_measure() + debug.info(1, "model_delay_ratios={}".format(model_delay_ratios)) + debug.info(1, "meas_delay_ratios={}".format(meas_delay_ratios)) + debug.info(1, "ratio_error={}".format(ratio_error)) globals.end_openram() def get_csv_data(self): @@ -50,7 +54,36 @@ class data_collection(openram_test): debug.info(1, "Delay errors:{}".format(delay_error)) variation_error = self.calculate_delay_variation_error(wl_dataframe, sae_dataframe) debug.info(1, "Variation errors:{}".format(variation_error)) + + def compare_model_to_measure(self): + """Uses the last 4 recent data sets (wl_meas, sen_meas, wl_model, sen_model) + and compare the wl-sen delay ratio between model and measured. + """ + model_delay_ratios = {} + meas_delay_ratios = {} + ratio_error = {} + #The full file name contains unrelated portions, separate them into the four that are needed + wl_meas_df = [pd.read_csv(file_name,encoding='utf-8') for file_name in self.file_names if "wl_measures" in file_name][0] + sae_meas_df = [pd.read_csv(file_name,encoding='utf-8') for file_name in self.file_names if "sae_measures" in file_name][0] + wl_model_df = [pd.read_csv(file_name,encoding='utf-8') for file_name in self.file_names if "wl_model" in file_name][0] + sae_model_df = [pd.read_csv(file_name,encoding='utf-8') for file_name in self.file_names if "sae_model" in file_name][0] + #Assume each csv has the same corners (and the same row order), use one of the dfs for corners + proc_pos, volt_pos, temp_pos = wl_meas_df.columns.get_loc('process'), wl_meas_df.columns.get_loc('voltage'), wl_meas_df.columns.get_loc('temp') + wl_sum_pos = wl_meas_df.columns.get_loc('sum') + sae_sum_pos = sae_meas_df.columns.get_loc('sum') + + df_zip = zip(wl_meas_df.itertuples(),sae_meas_df.itertuples(),wl_model_df.itertuples(),sae_model_df.itertuples()) + for wl_meas,sae_meas,wl_model,sae_model in df_zip: + #Use previously calculated position to index the df row. + corner = (wl_meas[proc_pos+1], wl_meas[volt_pos+1], wl_meas[temp_pos+1]) + meas_delay_ratios[corner] = wl_meas[wl_sum_pos+1]/sae_meas[sae_sum_pos+1] + model_delay_ratios[corner] = wl_model[wl_sum_pos+1]/sae_model[sae_sum_pos+1] + debug.info(1,"wl_model sum={}, sae_model_sum={}".format(wl_model[wl_sum_pos+1], sae_model[sae_sum_pos+1])) + ratio_error[corner] = 100*abs(model_delay_ratios[corner]-meas_delay_ratios[corner])/meas_delay_ratios[corner] + + return model_delay_ratios, meas_delay_ratios, ratio_error + def calculate_delay_error(self, wl_dataframe, sae_dataframe): """Calculates the percentage difference in delays between the wordline and sense amp enable""" start_data_pos = len(self.config_fields) #items before this point are configuration related @@ -94,13 +127,14 @@ class data_collection(openram_test): corner_gen = self.corner_combination_generator() init_corner = next(corner_gen) sram_data = self.get_sram_data(init_corner) - self.initialize_csv_file(sram_data, word_size, num_words, words_per_row) - self.add_sram_data_to_csv(sram_data, word_size, num_words, words_per_row, init_corner) + dc_resized = self.was_delay_chain_resized() + self.initialize_csv_file(word_size, num_words, words_per_row) + self.add_sram_data_to_csv(sram_data, word_size, num_words, words_per_row, dc_resized, init_corner) #Run openRAM for all corners for corner in corner_gen: sram_data = self.get_sram_data(corner) - self.add_sram_data_to_csv(sram_data, word_size, num_words, words_per_row, corner) + self.add_sram_data_to_csv(sram_data, word_size, num_words, words_per_row, dc_resized, corner) self.close_files() debug.info(1,"Data Generated") @@ -133,10 +167,10 @@ class data_collection(openram_test): file.close() def corner_combination_generator(self): - """Generates corner using a combination of values from config file""" processes = OPTS.process_corners voltages = OPTS.supply_voltages temperatures = OPTS.temperatures + """Generates corner using a combination of values from config file""" for proc in processes: for volt in voltages: for temp in temperatures: @@ -154,14 +188,21 @@ class data_collection(openram_test): words_per_row = [1] return word_sizes, num_words, words_per_row - def add_sram_data_to_csv(self, sram_data, word_size, num_words, words_per_row, corner): - """Writes data to its respective CSV file. There is a CSV for each measurement target (wordline, sense amp enable, and models)""" - sram_specs = [word_size,num_words,words_per_row,*corner] + def add_sram_data_to_csv(self, sram_data, word_size, num_words, words_per_row, dc_resized, corner): + """Writes data to its respective CSV file. There is a CSV for each measurement target + (wordline, sense amp enable, and models)""" + sram_specs = [word_size,num_words,words_per_row,dc_resized,*corner] for data_name, data_values in sram_data.items(): - self.csv_writers[data_name].writerow(sram_specs+sram_data[data_name]) + other_values = self.calculate_other_data_values(data_values) + self.csv_writers[data_name].writerow(sram_specs+sram_data[data_name]+other_values) debug.info(2,"Data Added to CSV file.") - def initialize_csv_file(self, sram_data, word_size, num_words, words_per_row): + def calculate_other_data_values(self, sram_data_list): + """A function to calculate extra values related to the data. Only does the sum for now""" + data_sum = sum(sram_data_list) + return [data_sum] + + def initialize_csv_file(self, word_size, num_words, words_per_row): """Opens a CSV file and writer for every data set being written (wl/sae measurements and model values)""" #CSV File writing header_dict = self.delay_obj.get_all_signal_names() @@ -178,8 +219,9 @@ class data_collection(openram_test): data_name) self.file_names.append(file_name) self.csv_files[data_name] = open(file_name, 'w') - self.config_fields = ['word_size', 'num_words', 'words_per_row', 'process', 'voltage', 'temp'] - fields = (*self.config_fields, *header_list) + self.config_fields = ['word_size', 'num_words', 'words_per_row', 'dc_resized', 'process', 'voltage', 'temp'] + self.other_data_fields = ['sum'] + fields = (*self.config_fields, *header_list, *self.other_data_fields) self.csv_writers[data_name] = csv.writer(self.csv_files[data_name], lineterminator = '\n') self.csv_writers[data_name].writerow(fields) @@ -223,6 +265,11 @@ class data_collection(openram_test): dict[key] = dict[key][0] else: del dict[key] + + def was_delay_chain_resized(self): + """Accesses the dc resize boolean in the control logic module.""" + #FIXME:assumes read/write port only + return self.sram.s.control_logic_rw.delay_chain_resized # instantiate a copdsay of the class to actually run the test if __name__ == "__main__": diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv index 25f5359d..e8016aa1 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv @@ -1,6 +1,2 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0 -4,16,1,TT,1.0,25,0.020618,0.0062215,0.018563000000000003,0.017233000000000002,0.007710799999999999,0.0099965,0.045221000000000004 -4,16,1,FF,1.0,25,0.019135,0.0052523000000000005,0.017398,0.015280000000000002,0.0067718,0.009288300000000001,0.042180999999999996 -4,16,1,SS,1.0,25,0.022393999999999997,0.0074892,0.019906,0.019521999999999998,0.0087409,0.010967000000000001,0.04836 -4,16,1,SF,1.0,25,0.01874,0.007554100000000001,0.016919,0.018821,0.0086205,0.0094092,0.049122 -4,16,1,FS,1.0,25,0.022926,0.0046388,0.02054,0.015555000000000001,0.0067794,0.010772,0.041583 +word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0,sum +4,16,1,False,TT,1.0,25,0.020618,0.0062215,0.018563000000000003,0.017233000000000002,0.007710799999999999,0.0099965,0.045221000000000004,0.12556380000000003 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv index b79d0fbf..3ed42e60 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv @@ -1,6 +1,2 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0 -4,16,1,TT,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 -4,16,1,FF,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 -4,16,1,SS,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 -4,16,1,SF,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 -4,16,1,FS,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15 +word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0,sum +4,16,1,False,TT,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15,42.949999999999996 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv index 043fc729..4c0ca0f3 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv @@ -1,6 +1,2 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -4,16,1,TT,1.0,25,0.010512,0.0089216,0.014109,0.013643,0.014564,0.0086745 -4,16,1,FF,1.0,25,0.0096952,0.008041900000000001,0.013129,0.012268999999999999,0.013255999999999999,0.007759599999999999 -4,16,1,SS,1.0,25,0.011505,0.009873400000000001,0.015371,0.015194000000000001,0.016210000000000002,0.0097529 -4,16,1,SF,1.0,25,0.0097161,0.0096343,0.013210000000000001,0.014750000000000001,0.013464,0.0094366 -4,16,1,FS,1.0,25,0.011368999999999999,0.0082136,0.015231000000000001,0.012545,0.015907,0.0079376 +word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15,sum +4,16,1,False,TT,1.0,25,0.010512,0.0089216,0.014109,0.013643,0.014564,0.0086745,0.0704241 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv index c323597a..e9faaeb9 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv @@ -1,6 +1,2 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -4,16,1,TT,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 -4,16,1,FF,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 -4,16,1,SS,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 -4,16,1,SF,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 -4,16,1,FS,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15,sum +4,16,1,False,TT,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4,32.13333333333334 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv index efc32260..fe82d4e4 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv @@ -1,6 +1,2 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0 -4,16,1,TT,1.0,25,0.020572,0.006256,0.015678,0.014110000000000001,0.017755,0.013415,0.0076344,0.010162,0.04153 -4,16,1,FF,1.0,25,0.01907,0.0052328,0.014619000000000002,0.0126,0.016572,0.011862,0.0067348,0.0093334,0.03898 -4,16,1,SS,1.0,25,0.022189,0.0074532999999999995,0.016968,0.015854,0.019286,0.015139,0.0087264,0.011029,0.04439 -4,16,1,SF,1.0,25,0.018689,0.007557800000000001,0.014395999999999999,0.015354,0.016425000000000002,0.014665,0.0084936,0.009411300000000001,0.044714000000000004 -4,16,1,FS,1.0,25,0.022942,0.0046639,0.017356,0.012745999999999999,0.01949,0.012069,0.006808099999999999,0.010822,0.038710999999999995 +word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0,sum +4,16,1,False,TT,1.0,25,0.020572,0.006256,0.015678,0.014110000000000001,0.017755,0.013415,0.0076344,0.010162,0.04153,0.14711239999999998 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv index 12a0c9e8..7cac207f 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv @@ -1,6 +1,2 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0 -4,16,1,TT,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 -4,16,1,FF,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 -4,16,1,SS,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 -4,16,1,SF,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 -4,16,1,FS,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15 +word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0,sum +4,16,1,False,TT,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15,51.74999999999999 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv index 565bb307..97844e6d 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv @@ -1,6 +1,2 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -4,16,1,TT,1.0,25,0.010463,0.0089004,0.014107,0.013630000000000001,0.0146,0.0086902 -4,16,1,FF,1.0,25,0.0096519,0.008039899999999999,0.013115,0.012295,0.013235,0.0077621 -4,16,1,SS,1.0,25,0.011424,0.0098775,0.015351999999999998,0.015194000000000001,0.016227,0.0097375 -4,16,1,SF,1.0,25,0.009697500000000001,0.009592,0.013206,0.014738000000000001,0.013432,0.0094217 -4,16,1,FS,1.0,25,0.011432000000000001,0.0081985,0.015220000000000001,0.012544,0.015973,0.0079455 +word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15,sum +4,16,1,False,TT,1.0,25,0.010463,0.0089004,0.014107,0.013630000000000001,0.0146,0.0086902,0.0703906 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv index c323597a..e9faaeb9 100644 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv +++ b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv @@ -1,6 +1,2 @@ -word_size,num_words,words_per_row,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15 -4,16,1,TT,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 -4,16,1,FF,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 -4,16,1,SS,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 -4,16,1,SF,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 -4,16,1,FS,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4 +word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15,sum +4,16,1,False,TT,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4,32.13333333333334 From 12723adb0cdf9f311054d5164d3086e02bf2f5eb Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Mon, 4 Feb 2019 23:38:26 -0800 Subject: [PATCH 21/29] Modified some testing and initial delay chain sizes. --- .gitignore | 1 + compiler/characterizer/model_check.py | 6 +- compiler/modules/control_logic.py | 40 ++++----- compiler/tests/delay_data_collection.py | 83 ++++++++++++++----- ...data_4b_16word_1way_dc2x3_sae_measures.csv | 2 - .../data_4b_16word_1way_dc2x3_sae_model.csv | 2 - .../data_4b_16word_1way_dc2x3_wl_measures.csv | 2 - .../data_4b_16word_1way_dc2x3_wl_model.csv | 2 - ...data_4b_16word_1way_dc4x2_sae_measures.csv | 2 - .../data_4b_16word_1way_dc4x2_sae_model.csv | 2 - .../data_4b_16word_1way_dc4x2_wl_measures.csv | 2 - .../data_4b_16word_1way_dc4x2_wl_model.csv | 2 - 12 files changed, 88 insertions(+), 58 deletions(-) delete mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv delete mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv delete mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv delete mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv delete mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv delete mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv delete mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv delete mode 100644 compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv diff --git a/.gitignore b/.gitignore index c4e15e9d..b16e3d0b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ *.out *.toc *.synctex.gz +**/model_data \ No newline at end of file diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index b3e8452f..b791e520 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -227,7 +227,11 @@ class model_check(delay): def get_num_delay_stages(self): """Gets the number of stages in the delay chain from the control logic""" return len(self.sram.control_logic_rw.replica_bitline.delay_fanout_list) - + + def get_num_delay_stage_fanout(self): + """Gets fanout in each stage in the delay chain. Assumes each stage is the same""" + return self.sram.control_logic_rw.replica_bitline.delay_fanout_list[0] + def get_num_wl_en_driver_stages(self): """Gets the number of stages in the wl_en driver from the control logic""" return self.sram.control_logic_rw.wl_en_driver.num_stages diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 295bbca8..e81e44ea 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -30,13 +30,13 @@ class control_logic(design.design): self.port_type = port_type self.num_cols = word_size*words_per_row - self.num_words = num_rows * words_per_row + self.num_words = num_rows*words_per_row - self.enable_delay_chain_resizing = False + self.enable_delay_chain_resizing = True - #self.sram=None #disable re-sizing for debugging, FIXME: resizing is not working, needs to be adjusted for new control logic. - self.wl_timing_tolerance = 1 #Determines how much larger the sen delay should be. Accounts for possible error in model. - self.parasitic_inv_delay = parameter["min_inv_para_delay"] #Keeping 0 for now until further testing. + #Determines how much larger the sen delay should be. Accounts for possible error in model. + self.wl_timing_tolerance = 1 + self.parasitic_inv_delay = parameter["min_inv_para_delay"] self.wl_stage_efforts = None self.sen_stage_efforts = None @@ -159,16 +159,16 @@ class control_logic(design.design): #Resize if necessary, condition depends on resizing method if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): #This resizes to match fall and rise delays, can make the delay chain weird sizes. - # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) - # self.replica_bitline = factory.create(module_type="replica_bitline", - # delay_fanout_list=stage_list, - # bitcell_loads=bitcell_loads) + stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) + self.replica_bitline = factory.create(module_type="replica_bitline", + delay_fanout_list=stage_list, + bitcell_loads=bitcell_loads) #This resizes based on total delay. - delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) - self.replica_bitline = factory.create(module_type="replica_bitline", - delay_fanout_list=[delay_fanout]*delay_stages, - bitcell_loads=bitcell_loads) + # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) + # self.replica_bitline = factory.create(module_type="replica_bitline", + # delay_fanout_list=[delay_fanout]*delay_stages, + # bitcell_loads=bitcell_loads) self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing self.delay_chain_resized = True @@ -177,13 +177,10 @@ class control_logic(design.design): def get_heuristic_delay_chain_size(self): """Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """ - # FIXME: These should be tuned according to the additional size parameters - delay_fanout = 3 # This can be anything >=2 + delay_fanout = 2 # This can be anything >=2 # Delay stages Must be non-inverting - if self.words_per_row >= 4: - delay_stages = 8 - elif self.words_per_row == 2: - delay_stages = 6 + if self.words_per_row >= 2: + delay_stages = 4 else: delay_stages = 2 @@ -247,6 +244,8 @@ class control_logic(design.design): required_delay_rise = self.wl_delay_rise*self.wl_timing_tolerance - (self.sen_delay_rise-previous_delay_chain_delay/2) debug.info(2,"Required delays from chain: fall={}, rise={}".format(required_delay_fall,required_delay_rise)) + #If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic. + WARNING_FANOUT_DIFF = 5 #The stages need to be equal (or at least a even number of stages with matching rise/fall delays) while True: stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall) @@ -254,7 +253,8 @@ class control_logic(design.design): debug.info(1,"Fall stages={}, rise stages={}".format(stages_fall,stages_rise)) if stages_fall == stages_rise: break - elif abs(stages_fall-stages_rise) == 1: + elif abs(stages_fall-stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall-fanout_rise): + debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.") break #There should also be a condition to make sure the fanout does not get too large. #Otherwise, increase the fanout of delay with the most stages, calculate new stages diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index bd2a61f7..7c98bccf 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -4,6 +4,7 @@ Run a regression test on various srams """ import csv,sys,os import pandas as pd +import matplotlib.pyplot as plt import unittest from testutils import header,openram_test @@ -19,13 +20,14 @@ MODEL_DIR = "model_data/" class data_collection(openram_test): def runTest(self): - - word_size, num_words, words_per_row = 4, 16, 1 - self.init_data_gen() - self.set_delay_chain(2,3) - self.save_data_sram_corners(word_size, num_words, words_per_row) - wl_dataframe, sae_dataframe = self.get_csv_data() - self.evaluate_data(wl_dataframe, sae_dataframe) + ratio_data = self.calculate_delay_ratios_of_srams() + self.display_data(ratio_data) + # word_size, num_words, words_per_row = 4, 16, 1 + # self.init_data_gen() + # self.set_delay_chain(2,3) + # self.save_data_sram_corners(word_size, num_words, words_per_row) + # wl_dataframe, sae_dataframe = self.get_csv_data() + # self.evaluate_data(wl_dataframe, sae_dataframe) #Run again but with different delay chain sizes # self.init_data_gen() @@ -34,12 +36,27 @@ class data_collection(openram_test): # wl_dataframe, sae_dataframe = self.get_csv_data() # self.evaluate_data(wl_dataframe, sae_dataframe) - model_delay_ratios, meas_delay_ratios, ratio_error = self.compare_model_to_measure() - debug.info(1, "model_delay_ratios={}".format(model_delay_ratios)) - debug.info(1, "meas_delay_ratios={}".format(meas_delay_ratios)) - debug.info(1, "ratio_error={}".format(ratio_error)) - globals.end_openram() + # model_delay_ratios, meas_delay_ratios, ratio_error = self.compare_model_to_measure() + # debug.info(1, "model_delay_ratios={}".format(model_delay_ratios)) + # debug.info(1, "meas_delay_ratios={}".format(meas_delay_ratios)) + # debug.info(1, "ratio_error={}".format(ratio_error)) + # globals.end_openram() + def calculate_delay_ratios_of_srams(self): + """Runs delay measurements on several sram configurations. + Computes the delay ratio for each one.""" + delay_ratio_data = {} + config_tuple_list = [(32, 1024, None)] + #config_tuple_list = [(1, 16, 1),(4, 16, 1), (16, 16, 1), (32, 32, 1)] + for sram_config in config_tuple_list: + word_size, num_words, words_per_row = sram_config + self.init_data_gen() + self.save_data_sram_corners(word_size, num_words, words_per_row) + model_delay_ratios, meas_delay_ratios, ratio_error = self.compare_model_to_measure() + delay_ratio_data[sram_config] = ratio_error + debug.info(1, "Ratio percentage error={}".format(ratio_error)) + return delay_ratio_data + def get_csv_data(self): """Hardcoded Hack to get the measurement data from the csv into lists. """ wl_files_name = [file_name for file_name in self.file_names if "wl_measures" in file_name][0] @@ -79,11 +96,30 @@ class data_collection(openram_test): corner = (wl_meas[proc_pos+1], wl_meas[volt_pos+1], wl_meas[temp_pos+1]) meas_delay_ratios[corner] = wl_meas[wl_sum_pos+1]/sae_meas[sae_sum_pos+1] model_delay_ratios[corner] = wl_model[wl_sum_pos+1]/sae_model[sae_sum_pos+1] - debug.info(1,"wl_model sum={}, sae_model_sum={}".format(wl_model[wl_sum_pos+1], sae_model[sae_sum_pos+1])) - ratio_error[corner] = 100*abs(model_delay_ratios[corner]-meas_delay_ratios[corner])/meas_delay_ratios[corner] + #Not using absolute error, positive error means model was larger, negative error means it was smaller. + ratio_error[corner] = 100*(model_delay_ratios[corner]-meas_delay_ratios[corner])/meas_delay_ratios[corner] return model_delay_ratios, meas_delay_ratios, ratio_error - + + def display_data(self, data): + """Displays the ratio data using matplotlib (requires graphics)""" + config_data = [] + xticks = [] + #Organize data + #First key level if the sram configuration (wordsize, num words, words per row) + for config,corner_data_dict in data.items(): + #Second level is the corner data for that configuration. + for corner, corner_data in corner_data_dict.items(): + #Right now I am only testing with a single corner, will not work with more than 1 corner + config_data.append(corner_data) + xticks.append("{}b,{}w,{}wpr".format(*config)) + #plot data + data_range = [i+1 for i in range(len(data))] + shapes = ['ro', 'bo', 'go', 'co', 'mo'] + plt.xticks(data_range, xticks) + plt.plot(data_range, config_data, 'ro') + plt.show() + def calculate_delay_error(self, wl_dataframe, sae_dataframe): """Calculates the percentage difference in delays between the wordline and sense amp enable""" start_data_pos = len(self.config_fields) #items before this point are configuration related @@ -123,6 +159,9 @@ class data_collection(openram_test): def save_data_sram_corners(self, word_size, num_words, words_per_row): """Performs corner analysis on a single SRAM configuration""" self.create_sram(word_size, num_words, words_per_row) + #Setting to none forces SRAM to determine the value. Must be checked after sram creation + if not words_per_row: + words_per_row = self.sram.s.words_per_row #Run on one size to initialize CSV writing (csv names come from return value). Strange, but it is okay for now. corner_gen = self.corner_combination_generator() init_corner = next(corner_gen) @@ -150,7 +189,7 @@ class data_collection(openram_test): OPTS.trim_netlist = False OPTS.netlist_only = True OPTS.analytical_delay = False - OPTS.use_tech_delay_chain_size = True + #OPTS.use_tech_delay_chain_size = True # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload import characterizer @@ -209,13 +248,16 @@ class data_collection(openram_test): self.csv_files = {} self.csv_writers = {} self.file_names = [] + delay_stages = self.delay_obj.get_num_delay_stages() + delay_stage_fanout = self.delay_obj.get_num_delay_stage_fanout() + for data_name, header_list in header_dict.items(): file_name = '{}data_{}b_{}word_{}way_dc{}x{}_{}.csv'.format(MODEL_DIR, word_size, num_words, words_per_row, - parameter["static_delay_stages"], - parameter["static_fanout_per_stage"], + delay_stages, + delay_stage_fanout, data_name) self.file_names.append(file_name) self.csv_files[data_name] = open(file_name, 'w') @@ -238,13 +280,11 @@ class data_collection(openram_test): self.sram_spice = OPTS.openram_temp + "temp.sp" self.sram.sp_write(self.sram_spice) - debug.info(1, "SRAM column address size={}".format(self.sram.s.col_addr_size)) - def get_sram_data(self, corner): """Generates the delay object using the corner and runs a simulation for data.""" from characterizer import model_check self.delay_obj = model_check(self.sram.s, self.sram_spice, corner) - + import tech #Only 1 at a time probe_address = "1" * self.sram.s.addr_size @@ -253,6 +293,7 @@ class data_collection(openram_test): slews = [tech.spice["rise_time"]*2] sram_data = self.delay_obj.analyze(probe_address,probe_data,slews,loads) + return sram_data def remove_lists_from_dict(self, dict): diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv deleted file mode 100644 index e8016aa1..00000000 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_measures.csv +++ /dev/null @@ -1,2 +0,0 @@ -word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0,sum -4,16,1,False,TT,1.0,25,0.020618,0.0062215,0.018563000000000003,0.017233000000000002,0.007710799999999999,0.0099965,0.045221000000000004,0.12556380000000003 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv deleted file mode 100644 index 3ed42e60..00000000 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_sae_model.csv +++ /dev/null @@ -1,2 +0,0 @@ -word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0,sum -4,16,1,False,TT,1.0,25,8.8,2.65,6.4,7.4,3.4,7.15,7.15,42.949999999999996 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv deleted file mode 100644 index 4c0ca0f3..00000000 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_measures.csv +++ /dev/null @@ -1,2 +0,0 @@ -word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15,sum -4,16,1,False,TT,1.0,25,0.010512,0.0089216,0.014109,0.013643,0.014564,0.0086745,0.0704241 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv deleted file mode 100644 index e9faaeb9..00000000 --- a/compiler/tests/model_data/data_4b_16word_1way_dc2x3_wl_model.csv +++ /dev/null @@ -1,2 +0,0 @@ -word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15,sum -4,16,1,False,TT,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4,32.13333333333334 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv deleted file mode 100644 index fe82d4e4..00000000 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_measures.csv +++ /dev/null @@ -1,2 +0,0 @@ -word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0,sum -4,16,1,False,TT,1.0,25,0.020572,0.006256,0.015678,0.014110000000000001,0.017755,0.013415,0.0076344,0.010162,0.04153,0.14711239999999998 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv deleted file mode 100644 index 7cac207f..00000000 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_sae_model.csv +++ /dev/null @@ -1,2 +0,0 @@ -word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xand2_rbl_in.zb_int,Xsram.Xcontrol0.rbl_in,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_1,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_2,Xsram.Xcontrol0.Xreplica_bitline.Xdelay_chain.dout_3,Xsram.Xcontrol0.Xreplica_bitline.delayed_en,Xsram.Xcontrol0.pre_s_en,Xsram.Xcontrol0.Xbuf_s_en.Zb1_int,Xsram.s_en0,sum -4,16,1,False,TT,1.0,25,8.8,2.65,5.4,5.4,5.4,6.4,3.4,7.15,7.15,51.74999999999999 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv deleted file mode 100644 index 97844e6d..00000000 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_measures.csv +++ /dev/null @@ -1,2 +0,0 @@ -word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15,sum -4,16,1,False,TT,1.0,25,0.010463,0.0089004,0.014107,0.013630000000000001,0.0146,0.0086902,0.0703906 diff --git a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv b/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv deleted file mode 100644 index e9faaeb9..00000000 --- a/compiler/tests/model_data/data_4b_16word_1way_dc4x2_wl_model.csv +++ /dev/null @@ -1,2 +0,0 @@ -word_size,num_words,words_per_row,dc_resized,process,voltage,temp,Xsram.Xcontrol0.Xbuf_wl_en.Zb1_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb2_int,Xsram.Xcontrol0.Xbuf_wl_en.Zb3_int,Xsram.wl_en0,Xsram.Xbank0.Xwordline_driver0.wl_bar_15,Xsram.Xbank0.wl_15,sum -4,16,1,False,TT,1.0,25,5.4,5.4,5.733333333333333,4.4,5.8,5.4,32.13333333333334 From e3d003d410137f48f01c68dae28b4210221d2c6f Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 5 Feb 2019 00:43:16 -0800 Subject: [PATCH 22/29] Adjusted test values to account for recent changes. --- compiler/tests/21_hspice_delay_test.py | 57 +++++++++++++------------ compiler/tests/21_ngspice_delay_test.py | 55 ++++++++++++------------ compiler/tests/config_data.py | 1 + compiler/tests/testutils.py | 2 + 4 files changed, 60 insertions(+), 55 deletions(-) diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 8be7152c..a2c40a22 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -51,35 +51,36 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [0.2011], - 'delay_lh': [0.2011], - 'leakage_power': 0.002, - 'min_period': 0.41, - 'read0_power': [0.63604], - 'read1_power': [0.6120599999999999], - 'slew_hl': [0.07078999999999999], - 'slew_lh': [0.07078999999999999], - 'write0_power': [0.51742], - 'write1_power': [0.51095], - 'volt_bl': [0.2017], - 'volt_br': [1.0765], - 'delay_bl': [0.18114999999999998], - 'delay_br': [0.17763]} + golden_data = {'delay_bl': [0.19683], + 'delay_br': [0.19474], + 'delay_hl': [0.20646], + 'delay_lh': [0.20646], + 'leakage_power': 0.0013155, + 'min_period': 0.43, + 'read0_power': [0.50758], + 'read1_power': [0.48225999999999997], + 'slew_hl': [0.045869], + 'slew_lh': [0.045869], + 'volt_bl': [0.6563], + 'volt_br': [1.117], + 'write0_power': [0.46124000000000004], + 'write1_power': [0.48225999999999997]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [1.3911], - 'delay_lh': [1.3911], - 'leakage_power': 0.0278488, - 'min_period': 2.812, - 'read0_power': [22.1183], - 'read1_power': [21.4388], - 'slew_hl': [0.6], - 'slew_lh': [0.6], - 'write0_power': [19.4103], - 'write1_power': [20.1167], - 'volt_bl': [3.1763], - 'volt_br': [5.5731], - 'delay_bl': [1.1133000000000002], - 'delay_br': [0.9958395]} + golden_data = {'delay_bl': [1.1029], + 'delay_br': [0.9656455999999999], + 'delay_hl': [1.288], + 'delay_lh': [1.288], + 'leakage_power': 0.0273896, + 'min_period': 2.578, + 'read0_power': [16.9996], + 'read1_power': [16.2616], + 'slew_hl': [0.47891700000000004], + 'slew_lh': [0.47891700000000004], + 'volt_bl': [4.2155], + 'volt_br': [5.8142], + 'write0_power': [16.0656], + 'write1_power': [16.2616]} + else: self.assertTrue(False) # other techs fail # Check if no too many or too few results diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 98f01e53..5af44e69 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -51,35 +51,36 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_bl': [0.1840938], - 'delay_br': [0.1804373], - 'delay_hl': [0.2130831], - 'delay_lh': [0.2130831], - 'leakage_power': 0.001595639, - 'min_period': 0.527, - 'read0_power': [0.4852456], - 'read1_power': [0.46341889999999997], - 'slew_hl': [0.07351041999999999], - 'slew_lh': [0.07351041999999999], - 'volt_bl': [0.1954744], - 'volt_br': [1.058266], - 'write0_power': [0.4065201], - 'write1_power': [0.46341889999999997]} + golden_data = {'delay_bl': [0.2003652], + 'delay_br': [0.198698], + 'delay_hl': [0.2108836], + 'delay_lh': [0.2108836], + 'leakage_power': 0.001564799, + 'min_period': 0.508, + 'read0_power': [0.43916689999999997], + 'read1_power': [0.4198608], + 'slew_hl': [0.0455126], + 'slew_lh': [0.0455126], + 'volt_bl': [0.6472883], + 'volt_br': [1.114024], + 'write0_power': [0.40681890000000004], + 'write1_power': [0.4198608]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [1.610911], - 'delay_lh': [1.610911], - 'leakage_power': 0.0023593859999999998, + golden_data = {'delay_bl': [1.3937359999999999], + 'delay_br': [1.2596429999999998], + 'delay_hl': [1.5747600000000002], + 'delay_lh': [1.5747600000000002], + 'leakage_power': 0.00195795, 'min_period': 3.281, - 'read0_power': [20.763569999999998], - 'read1_power': [20.32745], - 'slew_hl': [0.7986348999999999], - 'slew_lh': [0.7986348999999999], - 'write0_power': [17.58272], - 'write1_power': [18.523419999999998], - 'volt_bl': [3.1763], - 'volt_br': [5.5731], - 'delay_bl': [1.1133000000000002], - 'delay_br': [0.9958395]} + 'read0_power': [14.92874], + 'read1_power': [14.369810000000001], + 'slew_hl': [0.49631959999999997], + 'slew_lh': [0.49631959999999997], + 'volt_bl': [4.132618], + 'volt_br': [5.573099], + 'write0_power': [13.79953], + 'write1_power': [14.369810000000001]} + else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/config_data.py b/compiler/tests/config_data.py index 08168bc3..f81a2451 100755 --- a/compiler/tests/config_data.py +++ b/compiler/tests/config_data.py @@ -1,3 +1,4 @@ +#Config file used for collecting data. word_size = 1 num_words = 16 diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 74d97960..ee7f1412 100755 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -62,6 +62,8 @@ class openram_test(unittest.TestCase): delay_obj.set_load_slew(load, slew) delay_obj.set_probe(probe_address="1"*sram.addr_size, probe_data=(sram.word_size-1)) test_port = delay_obj.read_ports[0] #Only test one port, assumes other ports have similar period. + delay_obj.create_signal_names() + delay_obj.create_measurement_names() delay_obj.create_measurement_objects() delay_obj.find_feasible_period_one_port(test_port) return delay_obj.period From 5f01a52113de8a5d987ff58ec76c88e5e6fcdd92 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Tue, 5 Feb 2019 21:15:12 -0800 Subject: [PATCH 23/29] Fixed some delay model bugs. --- compiler/characterizer/delay.py | 6 ++++-- compiler/modules/control_logic.py | 15 +++++++++++---- compiler/modules/sense_amp.py | 1 + compiler/modules/sense_amp_array.py | 2 +- compiler/pgates/pdriver.py | 13 ++++++------- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 14bdcd11..9230be55 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -889,7 +889,8 @@ class delay(simulation): """ if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: debug.warning("Analytical characterization results are not supported for multiport.") - + self.create_signal_names() + self.create_measurement_names() power = self.analytical_power(slews, loads) port_data = self.get_empty_measure_data_dict() for slew in slews: @@ -907,7 +908,8 @@ class delay(simulation): else: debug.error("Measurement name not recognized: {}".format(mname),1) period_margin = 0.1 - sram_data = { "min_period": bank_delay[0]*2*period_margin, + risefall_delay = bank_delay[self.read_ports[0]].delay/1e3 + sram_data = { "min_period":risefall_delay*2*period_margin, "leakage_power": power.leakage} return (sram_data,port_data) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index e81e44ea..9d022324 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -154,10 +154,8 @@ class control_logic(design.design): self.replica_bitline = factory.create(module_type="replica_bitline", delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic, bitcell_loads=bitcell_loads) - if self.sram != None: #Calculate delays for potential re-sizing - self.set_sen_wl_delays() #Resize if necessary, condition depends on resizing method - if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_total_timing_match(): + if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match(): #This resizes to match fall and rise delays, can make the delay chain weird sizes. stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) self.replica_bitline = factory.create(module_type="replica_bitline", @@ -177,7 +175,8 @@ class control_logic(design.design): def get_heuristic_delay_chain_size(self): """Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """ - delay_fanout = 2 # This can be anything >=2 + #FIXME: The minimum was 2 fanout, now it will not pass DRC unless it is 3. Why? + delay_fanout = 3 # This can be anything >=3 # Delay stages Must be non-inverting if self.words_per_row >= 2: delay_stages = 4 @@ -246,15 +245,23 @@ class control_logic(design.design): #If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic. WARNING_FANOUT_DIFF = 5 + stages_close = False #The stages need to be equal (or at least a even number of stages with matching rise/fall delays) while True: stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall) stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise) debug.info(1,"Fall stages={}, rise stages={}".format(stages_fall,stages_rise)) + if abs(stages_fall-stages_rise) == 1 and not stages_close: + stages_close = True + safe_fanout_rise = fanout_rise + safe_fanout_fall = fanout_fall + if stages_fall == stages_rise: break elif abs(stages_fall-stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall-fanout_rise): debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.") + fanout_rise = safe_fanout_rise + fanout_fall = safe_fanout_fall break #There should also be a condition to make sure the fanout does not get too large. #Otherwise, increase the fanout of delay with the most stages, calculate new stages diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index 8553e42c..9601259c 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -48,4 +48,5 @@ class sense_amp(design.design): """Get the relative capacitance of sense amp enable gate cin""" pmos_cin = parameter["sa_en_pmos_size"]/drc("minwidth_tx") nmos_cin = parameter["sa_en_nmos_size"]/drc("minwidth_tx") + #sen is connected to 2 pmos isolation TX and 1 nmos per sense amp. return 2*pmos_cin + nmos_cin \ No newline at end of file diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index ebf95c95..dc80a13b 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -142,4 +142,4 @@ class sense_amp_array(design.design): def get_en_cin(self): """Get the relative capacitance of all the sense amp enable connections in the array""" sense_amp_en_cin = self.amp.get_en_cin() - return sense_amp_en_cin * self.words_per_row + return sense_amp_en_cin * self.word_size diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 3a0e4e78..23f3f4f8 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -196,17 +196,16 @@ class pdriver(pgate.pgate): def get_stage_efforts(self, external_cout, inp_is_rise=False): """Get the stage efforts of the A -> Z path""" - - cout_list = {} + cout_list = [] for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]): - cout_list[prev_inv]=inv.get_cin() - - cout_list[self.inv_list[-1]]=external_cout + cout_list.append(inv.get_cin()) + + cout_list.append(external_cout) stage_effort_list = [] last_inp_is_rise = inp_is_rise - for inv in self.inv_list: - stage = inv.get_stage_effort(cout_list[inv], last_inp_is_rise) + for inv,cout in zip(self.inv_list,cout_list): + stage = inv.get_stage_effort(cout, last_inp_is_rise) stage_effort_list.append(stage) last_inp_is_rise = stage.is_rise From 01c8405d12cd5a613005a57db6ed755c16e1e7db Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 6 Feb 2019 00:46:25 -0800 Subject: [PATCH 24/29] Fix bitline measurement delays and adjusted default delay chain for column mux srams --- compiler/characterizer/delay.py | 17 ++++++++++++++--- compiler/characterizer/measurements.py | 5 +++-- compiler/modules/control_logic.py | 4 ++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 9230be55..7aa9641d 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -70,6 +70,11 @@ class delay(simulation): self.read_meas_objs[-1].meta_str = "read1" self.read_meas_objs.append(power_measure("read0_power", "FALL", measure_scale=1e3)) self.read_meas_objs[-1].meta_str = "read0" + + #This will later add a half-period to the spice time delay. Only for reading 0. + for obj in self.read_meas_objs: + if obj.meta_str is "read0": + obj.meta_add_delay = True trig_name = "Xsram.s_en{}" #Sense amp enable if len(self.all_ports) == 1: #special naming case for single port sram bitlines which does not include the port in name @@ -104,13 +109,16 @@ class delay(simulation): self.bitline_delay_objs[-1].meta_str = "read0" self.bitline_delay_objs.append(delay_measure(self.bitline_delay_names[1], trig_name, br_name, "FALL", "FALL", targ_vdd=targ_val, measure_scale=1e9)) self.bitline_delay_objs[-1].meta_str = "read1" - + #Enforces the time delay on the bitline measurements for read0 or read1 + for obj in self.bitline_delay_objs: + obj.meta_add_delay = True + def create_write_port_measurement_objects(self): """Create the measurements used for read ports: delays, slews, powers""" self.write_meas_objs = [] self.write_meas_objs.append(power_measure("write1_power", "RISE", measure_scale=1e3)) - self.write_meas_objs[-1].meta_str = "read1" + self.write_meas_objs[-1].meta_str = "write1" self.write_meas_objs.append(power_measure("write0_power", "FALL", measure_scale=1e3)) self.write_meas_objs[-1].meta_str = "write0" @@ -284,12 +292,15 @@ class delay(simulation): #vdd is arguably constant as that is true for a single lib file. if delay_obj.meta_str == "read0": #Falling delay are measured starting from neg. clk edge. Delay adjusted to that. - meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] + self.period/2 + meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] elif delay_obj.meta_str == "read1": meas_cycle_delay = self.cycle_times[self.measure_cycles[port][delay_obj.meta_str]] else: debug.error("Unrecognised delay Index={}".format(delay_obj.meta_str),1) + if delay_obj.meta_add_delay: + meas_cycle_delay += self.period/2 + return (meas_cycle_delay, meas_cycle_delay, self.vdd_voltage, port) def get_power_measure_variants(self, port, power_obj, operation): diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py index aec4d769..e3d16584 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -10,8 +10,9 @@ class spice_measurement(ABC): #Names must be unique for correct spice simulation, but not enforced here. self.name = measure_name self.measure_scale = measure_scale - self.meta_str = None #Some measurements set this, set here to be clear on existence - + #Some meta values used externally. variables are added here for consistency accross the objects + self.meta_str = None + self.meta_add_delay = False @abstractmethod def get_measure_function(self): return None diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 9d022324..d83cbd0d 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -177,9 +177,9 @@ class control_logic(design.design): """Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """ #FIXME: The minimum was 2 fanout, now it will not pass DRC unless it is 3. Why? delay_fanout = 3 # This can be anything >=3 - # Delay stages Must be non-inverting + # Model poorly captures delay of the column mux. Be pessismistic for column mux if self.words_per_row >= 2: - delay_stages = 4 + delay_stages = 8 else: delay_stages = 2 From 56e79c050b133014955b7b2d01730c3028cab5f8 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 6 Feb 2019 15:27:29 -0800 Subject: [PATCH 25/29] Changed test values to fix tests. --- compiler/modules/control_logic.py | 7 ++++++- compiler/tests/21_hspice_delay_test.py | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index d83cbd0d..57e578f5 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -182,7 +182,12 @@ class control_logic(design.design): delay_stages = 8 else: delay_stages = 2 - + + #Read ports have a shorter s_en delay. The model is not accurate enough to catch this difference + #on certain sram configs. + if self.port_type == "r": + delay_stages+=2 + return (delay_stages, delay_fanout) def set_sen_wl_delays(self): diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index a2c40a22..fa7fc35f 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -51,20 +51,20 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_bl': [0.19683], - 'delay_br': [0.19474], - 'delay_hl': [0.20646], - 'delay_lh': [0.20646], - 'leakage_power': 0.0013155, + golden_data = {'delay_bl': [0.1980959], + 'delay_br': [0.1946091], + 'delay_hl': [0.2121267], + 'delay_lh': [0.2121267], + 'leakage_power': 0.0023761999999999998, 'min_period': 0.43, - 'read0_power': [0.50758], - 'read1_power': [0.48225999999999997], - 'slew_hl': [0.045869], - 'slew_lh': [0.045869], - 'volt_bl': [0.6563], - 'volt_br': [1.117], - 'write0_power': [0.46124000000000004], - 'write1_power': [0.48225999999999997]} + 'read0_power': [0.5139368], + 'read1_power': [0.48940979999999995], + 'slew_hl': [0.0516745], + 'slew_lh': [0.0516745], + 'volt_bl': [0.5374525], + 'volt_br': [1.1058], + 'write0_power': [0.46267169999999996], + 'write1_power': [0.4670826]} elif OPTS.tech_name == "scn4m_subm": golden_data = {'delay_bl': [1.1029], 'delay_br': [0.9656455999999999], From 690055174d8f9df37118031244f626ca7b060350 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 6 Feb 2019 20:09:01 -0800 Subject: [PATCH 26/29] Fixed bug in control logic test with port configs. --- compiler/tests/16_control_logic_test.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/tests/16_control_logic_test.py b/compiler/tests/16_control_logic_test.py index 91ad0500..e0545af4 100755 --- a/compiler/tests/16_control_logic_test.py +++ b/compiler/tests/16_control_logic_test.py @@ -36,17 +36,21 @@ class control_logic_test(openram_test): # Check port specific control logic OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 1 - OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 debug.info(1, "Testing sample for control_logic for multiport, only write control logic") a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="rw") self.local_check(a) + OPTS.num_rw_ports = 0 + OPTS.num_w_ports = 1 debug.info(1, "Testing sample for control_logic for multiport, only write control logic") a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="w") self.local_check(a) + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 1 debug.info(1, "Testing sample for control_logic for multiport, only read control logic") a = control_logic.control_logic(num_rows=128, words_per_row=1, word_size=8, port_type="r") self.local_check(a) From d0edda93adeed7e8fd5c23a09b0cee86ddfe5745 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 7 Feb 2019 02:27:22 -0800 Subject: [PATCH 27/29] Added more variance analysis for the delay data --- compiler/characterizer/model_check.py | 5 +- compiler/tests/config_data.py | 2 +- compiler/tests/delay_data_collection.py | 74 ++++++++++++++++++------- 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/compiler/characterizer/model_check.py b/compiler/characterizer/model_check.py index b791e520..88e0b96a 100644 --- a/compiler/characterizer/model_check.py +++ b/compiler/characterizer/model_check.py @@ -38,6 +38,7 @@ class model_check(delay): self.rbl_delay_meas_names = ["delay_gated_clk_nand", "delay_delay_chain_in"]+dc_delay_names self.sae_delay_meas_names = ["delay_pre_sen"]+sen_driver_delay_names+["delay_sen"] + self.delay_chain_indices = (len(self.rbl_delay_meas_names)-len(dc_delay_names), len(self.rbl_delay_meas_names)) #Create slew measurement names wl_en_driver_slew_names = ["slew_wl_en_dvr{}_".format(stage) for stage in range(1,self.get_num_wl_en_driver_stages())] wl_driver_slew_names = ["slew_wl_dvr{}_".format(stage) for stage in range(1,self.get_num_wl_driver_stages())] @@ -61,14 +62,14 @@ class model_check(delay): ["Xsram.wl_en0", "Xsram.Xbank0.Xwordline_driver0.wl_bar_{}".format(self.wordline_row)]+\ wl_driver_signals+\ ["Xsram.Xbank0.wl_{}".format(self.wordline_row)] - self.rbl_en_signal_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"]+\ + pre_delay_chain_names = ["Xsram.Xcontrol0.gated_clk_bar", "Xsram.Xcontrol0.Xand2_rbl_in.zb_int", "Xsram.Xcontrol0.rbl_in"] + self.rbl_en_signal_names = pre_delay_chain_names+\ delay_chain_signal_names+\ ["Xsram.Xcontrol0.Xreplica_bitline.delayed_en"] self.sae_signal_names = ["Xsram.Xcontrol0.Xreplica_bitline.bl0_0", "Xsram.Xcontrol0.pre_s_en"]+\ sen_driver_signals+\ ["Xsram.s_en0"] - def create_measurement_objects(self): """Create the measurements used for read and write ports""" self.create_wordline_measurement_objects() diff --git a/compiler/tests/config_data.py b/compiler/tests/config_data.py index f81a2451..849d441d 100755 --- a/compiler/tests/config_data.py +++ b/compiler/tests/config_data.py @@ -4,7 +4,7 @@ num_words = 16 tech_name = "freepdk45" #process_corners = ["TT", "FF", "SS", "SF", "FS"] -process_corners = ["TT"] +process_corners = ["TT", "FF", "SS"] supply_voltages = [1.0] temperatures = [25] diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index 7c98bccf..3b1fe938 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -20,28 +20,60 @@ MODEL_DIR = "model_data/" class data_collection(openram_test): def runTest(self): - ratio_data = self.calculate_delay_ratios_of_srams() - self.display_data(ratio_data) - # word_size, num_words, words_per_row = 4, 16, 1 - # self.init_data_gen() - # self.set_delay_chain(2,3) - # self.save_data_sram_corners(word_size, num_words, words_per_row) - # wl_dataframe, sae_dataframe = self.get_csv_data() - # self.evaluate_data(wl_dataframe, sae_dataframe) - - #Run again but with different delay chain sizes - # self.init_data_gen() - # self.set_delay_chain(4,2) - # self.save_data_sram_corners(word_size, num_words, words_per_row) - # wl_dataframe, sae_dataframe = self.get_csv_data() - # self.evaluate_data(wl_dataframe, sae_dataframe) - - # model_delay_ratios, meas_delay_ratios, ratio_error = self.compare_model_to_measure() - # debug.info(1, "model_delay_ratios={}".format(model_delay_ratios)) - # debug.info(1, "meas_delay_ratios={}".format(meas_delay_ratios)) - # debug.info(1, "ratio_error={}".format(ratio_error)) - # globals.end_openram() + #Uncomment this for model evaluation + # ratio_data = self.calculate_delay_ratios_of_srams() + # self.display_data(ratio_data) + + self.run_delay_chain_variation_analysis() + + globals.end_openram() + + def run_delay_chain_variation_analysis(self): + """Generates sram with different delay chain configs over different corners and + analyzes delay variation.""" + OPTS.use_tech_delay_chain_size = True + #Constant sram config for this test + word_size, num_words, words_per_row = 1, 16, 1 + #Only change delay chain + dc_config_list = [(2,3), (3,3), (3,4), (4,2)] + dc_vars = [] + for stages,fanout in dc_config_list: + self.init_data_gen() + self.set_delay_chain(stages,fanout) + self.save_data_sram_corners(word_size, num_words, words_per_row) + wl_dataframe, sae_dataframe = self.get_csv_data() + var = self.calculate_delay_chain_variance(sae_dataframe) + dc_vars.append(var) + debug.info(1,"DC config={}, variance={}".format((stages,fanout), var)) + + self.plot_data(dc_config_list, dc_vars) + #display data + + def calculate_delay_chain_variance(self, sae_dataframe): + """Determines the delay variance of the delay chain over corners""" + (start_dc, end_dc) = self.delay_obj.delay_chain_indices + start_data_pos = len(self.config_fields)+1 #items before this point are configuration related + delay_sums = [] + row_count = 0 + #Get delay sums over different corners + for sae_row in sae_dataframe.itertuples(): + dc_delays = sae_row[start_data_pos+start_dc:start_data_pos+end_dc] + delay_sums.append(sum(dc_delays)) + + #calculate mean then variance + m = sum(delay_sums) / len(delay_sums) + delay_variance = sum((xi - m) ** 2 for xi in delay_sums) / len(delay_sums) + return delay_variance + + def plot_data(self, x_labels, y_values): + """Display a plot using matplot lib. + Assumes input x values are just labels and y values are actual data.""" + data_range = [i+1 for i in range(len(x_labels))] + plt.xticks(data_range, x_labels) + plt.plot(data_range, y_values, 'ro') + plt.show() + def calculate_delay_ratios_of_srams(self): """Runs delay measurements on several sram configurations. Computes the delay ratio for each one.""" From ebf43298c05097122ac347ea36955778933de8f8 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 7 Feb 2019 14:21:23 -0800 Subject: [PATCH 28/29] Added mean/variance plotting --- compiler/tests/delay_data_collection.py | 60 +++++++++++++++++++------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index 3b1fe938..ce173b45 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -25,33 +25,38 @@ class data_collection(openram_test): # ratio_data = self.calculate_delay_ratios_of_srams() # self.display_data(ratio_data) - self.run_delay_chain_variation_analysis() + self.run_delay_chain_analysis() globals.end_openram() - def run_delay_chain_variation_analysis(self): + def run_delay_chain_analysis(self): """Generates sram with different delay chain configs over different corners and - analyzes delay variation.""" + analyzes delay average and variation.""" OPTS.use_tech_delay_chain_size = True #Constant sram config for this test word_size, num_words, words_per_row = 1, 16, 1 #Only change delay chain dc_config_list = [(2,3), (3,3), (3,4), (4,2)] + #dc_config_list = [(2,3), (3,3)] + dc_avgs = [] dc_vars = [] for stages,fanout in dc_config_list: self.init_data_gen() self.set_delay_chain(stages,fanout) self.save_data_sram_corners(word_size, num_words, words_per_row) wl_dataframe, sae_dataframe = self.get_csv_data() - var = self.calculate_delay_chain_variance(sae_dataframe) - dc_vars.append(var) - debug.info(1,"DC config={}, variance={}".format((stages,fanout), var)) + delay_sums = self.get_delay_chain_sums(sae_dataframe) + dc_avgs.append(self.get_average(delay_sums)) + dc_vars.append(self.get_variance(delay_sums)) + debug.info(1,"DC config={}: avg={} variance={}".format((stages,fanout), dc_avgs[-1], dc_vars[-1])) - self.plot_data(dc_config_list, dc_vars) - #display data + #plot data + self.plot_two_data_sets(dc_config_list, dc_avgs, dc_vars) + #self.plot_data(dc_config_list, dc_avgs) + #self.plot_data(dc_config_list, dc_vars) - def calculate_delay_chain_variance(self, sae_dataframe): - """Determines the delay variance of the delay chain over corners""" + def get_delay_chain_sums(self, sae_dataframe): + """Calculate the total delay of the delay chain over different corners""" (start_dc, end_dc) = self.delay_obj.delay_chain_indices start_data_pos = len(self.config_fields)+1 #items before this point are configuration related delay_sums = [] @@ -60,11 +65,15 @@ class data_collection(openram_test): for sae_row in sae_dataframe.itertuples(): dc_delays = sae_row[start_data_pos+start_dc:start_data_pos+end_dc] delay_sums.append(sum(dc_delays)) + return delay_sums - #calculate mean then variance - m = sum(delay_sums) / len(delay_sums) - delay_variance = sum((xi - m) ** 2 for xi in delay_sums) / len(delay_sums) + def get_variance(self, nums): + avg = self.get_average(nums) + delay_variance = sum((xi - avg) ** 2 for xi in nums) / len(nums) return delay_variance + + def get_average(self,nums): + return sum(nums) / len(nums) def plot_data(self, x_labels, y_values): """Display a plot using matplot lib. @@ -73,7 +82,30 @@ class data_collection(openram_test): plt.xticks(data_range, x_labels) plt.plot(data_range, y_values, 'ro') plt.show() - + + def plot_two_data_sets(self, x_labels, y1_values, y2_values): + """Plots two data sets on the same x-axis.""" + data_range = [i for i in range(len(x_labels))] + fig, ax1 = plt.subplots() + + color = 'tab:red' + ax1.set_xlabel('DC (Stages,Fanout)') + ax1.set_ylabel('Average Delay (ns)', color=color) + ax1.plot(data_range, y1_values, marker='o', color=color) + ax1.tick_params(axis='y', labelcolor=color) + + ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis + + color = 'tab:blue' + #ax2.set_xticks(data_range, x_labels) + ax2.set_ylabel('Delay Variance (ns)', color=color) # we already handled the x-label with ax1 + ax2.plot(data_range, y2_values, marker='*', color=color) + ax2.tick_params(axis='y', labelcolor=color) + + fig.tight_layout() # otherwise the right y-label is slightly clipped + plt.xticks(data_range, x_labels) + plt.show() + def calculate_delay_ratios_of_srams(self): """Runs delay measurements on several sram configurations. Computes the delay ratio for each one.""" From 9e23e6584aede75d74a2778268985341db02b290 Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Thu, 7 Feb 2019 15:30:47 -0800 Subject: [PATCH 29/29] Made variance plot look slightly better. --- compiler/tests/delay_data_collection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/tests/delay_data_collection.py b/compiler/tests/delay_data_collection.py index ce173b45..e893d8ec 100644 --- a/compiler/tests/delay_data_collection.py +++ b/compiler/tests/delay_data_collection.py @@ -36,7 +36,7 @@ class data_collection(openram_test): #Constant sram config for this test word_size, num_words, words_per_row = 1, 16, 1 #Only change delay chain - dc_config_list = [(2,3), (3,3), (3,4), (4,2)] + dc_config_list = [(2,3), (3,3), (3,4), (4,2), (4,3), (4,4), (2,4), (2,5)] #dc_config_list = [(2,3), (3,3)] dc_avgs = [] dc_vars = [] @@ -91,7 +91,7 @@ class data_collection(openram_test): color = 'tab:red' ax1.set_xlabel('DC (Stages,Fanout)') ax1.set_ylabel('Average Delay (ns)', color=color) - ax1.plot(data_range, y1_values, marker='o', color=color) + ax1.plot(data_range, y1_values, marker='o', color=color, linestyle='') ax1.tick_params(axis='y', labelcolor=color) ax2 = ax1.twinx() # instantiate a second axes that shares the same x-axis @@ -99,7 +99,7 @@ class data_collection(openram_test): color = 'tab:blue' #ax2.set_xticks(data_range, x_labels) ax2.set_ylabel('Delay Variance (ns)', color=color) # we already handled the x-label with ax1 - ax2.plot(data_range, y2_values, marker='*', color=color) + ax2.plot(data_range, y2_values, marker='*', color=color, linestyle='') ax2.tick_params(axis='y', labelcolor=color) fig.tight_layout() # otherwise the right y-label is slightly clipped