Added bitline threshold delay checks to delay tests.

This commit is contained in:
Hunter Nichols 2018-12-13 22:21:30 -08:00
parent 97fc37aec1
commit e4065929c2
2 changed files with 92 additions and 34 deletions

View File

@ -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

View File

@ -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