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