Moved all bitline delay measurements to delay class. Added measurements to check delay model.

This commit is contained in:
Hunter Nichols 2019-01-03 05:51:28 -08:00
parent 66b2fcdc91
commit 272267358f
10 changed files with 456 additions and 101 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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