mirror of https://github.com/VLSIDA/OpenRAM.git
Added data parsing to measurement objects and adding power measurements.
This commit is contained in:
parent
b10ef3fb7e
commit
66b2fcdc91
|
|
@ -80,3 +80,10 @@ def convert_to_float(number):
|
||||||
debug.error("Invalid number: {0}".format(number),1)
|
debug.error("Invalid number: {0}".format(number),1)
|
||||||
|
|
||||||
return float_value
|
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
|
||||||
|
|
@ -48,14 +48,30 @@ class delay(simulation):
|
||||||
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"]
|
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"]
|
||||||
|
|
||||||
def create_measurement_objects(self):
|
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}"
|
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
|
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.read_meas_objs.append(delay_measure("delay_lh", trig_delay_name, targ_name, "RISE", "RISE", 1e9))
|
||||||
self.meas_objs.append(delay_measure("delay_hl{}", trig_delay_name, targ_name, "FALL", "FALL"))
|
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.read_meas_objs.append(slew_measure("slew_lh", targ_name, "RISE", 1e9))
|
||||||
self.meas_objs.append(slew_measure("slew_hl{}", targ_name, "FALL"))
|
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):
|
def create_signal_names(self):
|
||||||
self.addr_name = "A"
|
self.addr_name = "A"
|
||||||
|
|
@ -209,99 +225,68 @@ class delay(simulation):
|
||||||
|
|
||||||
self.sf.close()
|
self.sf.close()
|
||||||
|
|
||||||
def get_delay_meas_values(self, delay_name, port):
|
def get_read_measure_variants(self, port, measure_obj):
|
||||||
"""Get the values needed to generate a Spice measurement statement based on the name of the measurement."""
|
"""Checks the measurement object and calls respective function for related measurement inputs."""
|
||||||
debug.check('lh' in delay_name or 'hl' in delay_name, "Measure command {0} does not contain direction (lh/hl)")
|
meas_type = type(measure_obj)
|
||||||
trig_clk_name = "clk{0}".format(port)
|
if meas_type is delay_measure or meas_type is slew_measure:
|
||||||
meas_name="{0}{1}".format(delay_name, port)
|
return self.get_delay_measure_variants(port, measure_obj)
|
||||||
targ_name = "{0}".format("{0}{1}_{2}".format(self.dout_name,port,self.probe_data))
|
elif meas_type is power_measure:
|
||||||
half_vdd = 0.5 * self.vdd_voltage
|
return self.get_power_measure_variants(port, measure_obj, "read")
|
||||||
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"]]
|
|
||||||
else:
|
else:
|
||||||
debug.error(1, "Measure command {0} not recognized".format(delay_name))
|
debug.error("Input function not defined for measurement type={}".format(meas_type))
|
||||||
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):
|
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)"""
|
"""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
|
#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.
|
#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"]]
|
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"]]
|
meas_cycle_delay = self.cycle_times[self.measure_cycles[port]["read1"]]
|
||||||
else:
|
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)
|
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):
|
def write_delay_measures_read_port(self, port):
|
||||||
"""
|
"""
|
||||||
Write the measure statements to quantify the delay and power results for a read port.
|
Write the measure statements to quantify the delay and power results for a read port.
|
||||||
"""
|
"""
|
||||||
# add measure statements for delays/slews
|
# add measure statements for delays/slews
|
||||||
for measure in self.meas_objs:
|
for measure in self.read_meas_objs:
|
||||||
measure_variant_tuple = self.get_measure_variants(port, measure)
|
measure_variant_inp_tuple = self.get_read_measure_variants(port, measure)
|
||||||
measure.write_measure(self.stim, measure_variant_tuple)
|
measure.write_measure(self.stim, measure_variant_inp_tuple)
|
||||||
|
|
||||||
# add measure statements for power
|
def get_write_measure_variants(self, port, measure_obj):
|
||||||
for pname in self.power_meas_names:
|
"""Checks the measurement object and calls respective function for related measurement inputs."""
|
||||||
if "read" not in pname:
|
meas_type = type(measure_obj)
|
||||||
continue
|
if meas_type is power_measure:
|
||||||
#Different naming schemes are used for the measure cycle dict and measurement names.
|
return self.get_power_measure_variants(port, measure_obj, "write")
|
||||||
#TODO: make them the same so they can be indexed the same.
|
else:
|
||||||
if '1' in pname:
|
debug.error("Input function not defined for measurement type={}".format(meas_type))
|
||||||
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):
|
def write_delay_measures_write_port(self, port):
|
||||||
"""
|
"""
|
||||||
Write the measure statements to quantify the power results for a write port.
|
Write the measure statements to quantify the power results for a write port.
|
||||||
"""
|
"""
|
||||||
# add measure statements for power
|
# add measure statements for power
|
||||||
for pname in self.power_meas_names:
|
for measure in self.write_meas_objs:
|
||||||
if "write" not in pname:
|
measure_variant_inp_tuple = self.get_read_measure_variants(port, measure)
|
||||||
continue
|
measure.write_measure(self.stim, measure_variant_inp_tuple)
|
||||||
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)
|
|
||||||
|
|
||||||
def write_delay_measures(self):
|
def write_delay_measures(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -451,28 +436,27 @@ class delay(simulation):
|
||||||
#Too much duplicate code here. Try reducing
|
#Too much duplicate code here. Try reducing
|
||||||
for port in self.targ_read_ports:
|
for port in self.targ_read_ports:
|
||||||
debug.info(2, "Check delay values for port {}".format(port))
|
debug.info(2, "Check delay values for port {}".format(port))
|
||||||
delay_names = [mname for mname in self.delay_meas_names]
|
read_port_dict = {}
|
||||||
delays = self.parse_values(delay_names, port, 1e9) # scale delays to ns
|
#Get measurements from output file
|
||||||
if not self.check_valid_delays(delays):
|
for measure in self.read_meas_objs:
|
||||||
return (False,{})
|
read_port_dict[measure.name] = measure.retrieve_measure(port=port)
|
||||||
result[port].update(delays)
|
|
||||||
|
|
||||||
power_names = [mname for mname in self.power_meas_names if 'read' in mname]
|
#Check timing for read ports. Power is only checked if it was read correctly
|
||||||
powers = self.parse_values(power_names, port, 1e3) # scale power to mw
|
if not self.check_valid_delays(read_port_dict):
|
||||||
#Check that power parsing worked.
|
return (False,{})
|
||||||
for name, power in powers.items():
|
if not check_dict_values_is_float(read_port_dict):
|
||||||
if type(power)!=float:
|
debug.error("Failed to Measure Read Port Values:\n\t\t{0}".format(read_port_dict),1) #Printing the entire dict looks bad.
|
||||||
debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad.
|
|
||||||
result[port].update(powers)
|
result[port].update(read_port_dict)
|
||||||
|
|
||||||
for port in self.targ_write_ports:
|
for port in self.targ_write_ports:
|
||||||
power_names = [mname for mname in self.power_meas_names if 'write' in mname]
|
write_port_dict = {}
|
||||||
powers = self.parse_values(power_names, port, 1e3) # scale power to mw
|
for measure in self.read_meas_objs:
|
||||||
#Check that power parsing worked.
|
write_port_dict[measure.name] = measure.retrieve_measure(port=port)
|
||||||
for name, power in powers.items():
|
|
||||||
if type(power)!=float:
|
if not check_dict_values_is_float(write_port_dict):
|
||||||
debug.error("Failed to Parse Power Values:\n\t\t{0}".format(powers),1) #Printing the entire dict looks bad.
|
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(powers)
|
result[port].update(write_port_dict)
|
||||||
|
|
||||||
# The delay is from the negative edge for our SRAM
|
# The delay is from the negative edge for our SRAM
|
||||||
return (True,result)
|
return (True,result)
|
||||||
|
|
@ -502,13 +486,13 @@ class delay(simulation):
|
||||||
#key=raw_input("press return to continue")
|
#key=raw_input("press return to continue")
|
||||||
return (leakage_power*1e3, trim_leakage_power*1e3)
|
return (leakage_power*1e3, trim_leakage_power*1e3)
|
||||||
|
|
||||||
def check_valid_delays(self, delay_dict):
|
def check_valid_delays(self, result_dict):
|
||||||
""" Check if the measurements are defined and if they are valid. """
|
""" Check if the measurements are defined and if they are valid. """
|
||||||
#Hard coded names currently
|
#Hard coded names currently
|
||||||
delay_hl = delay_dict["delay_hl"]
|
delay_hl = result_dict["delay_hl"]
|
||||||
delay_lh = delay_dict["delay_lh"]
|
delay_lh = result_dict["delay_lh"]
|
||||||
slew_hl = delay_dict["slew_hl"]
|
slew_hl = result_dict["slew_hl"]
|
||||||
slew_lh = delay_dict["slew_lh"]
|
slew_lh = result_dict["slew_lh"]
|
||||||
period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew)
|
period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew)
|
||||||
|
|
||||||
# if it failed or the read was longer than a period
|
# if it failed or the read was longer than a period
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@ import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from .stimuli import *
|
from .stimuli import *
|
||||||
|
from .charutils import *
|
||||||
|
|
||||||
class spice_measurement(ABC):
|
class spice_measurement(ABC):
|
||||||
"""Base class for spice stimulus measurements."""
|
"""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.
|
#Names must be unique for correct spice simulation, but not enforced here.
|
||||||
self.name = measure_name
|
self.name = measure_name
|
||||||
|
self.measure_scale = measure_scale
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_measure_function(self):
|
def get_measure_function(self):
|
||||||
|
|
@ -24,12 +26,18 @@ class spice_measurement(ABC):
|
||||||
measure_vals = self.get_measure_values(*input_tuple)
|
measure_vals = self.get_measure_values(*input_tuple)
|
||||||
measure_func(stim_obj, *measure_vals)
|
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):
|
class delay_measure(spice_measurement):
|
||||||
"""Generates a spice measurement for the delay of 50%-to-50% points of two signals."""
|
"""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):
|
def __init__(self, measure_name, trig_name, targ_name, trig_dir_str, targ_dir_str, measure_scale=None):
|
||||||
spice_measurement.__init__(self, measure_name)
|
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)
|
||||||
|
|
||||||
def get_measure_function(self):
|
def get_measure_function(self):
|
||||||
|
|
@ -54,7 +62,8 @@ class delay_measure(spice_measurement):
|
||||||
targ_val = self.targ_val_of_vdd * vdd_voltage
|
targ_val = self.targ_val_of_vdd * vdd_voltage
|
||||||
|
|
||||||
if port != None:
|
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)
|
trig_name = self.trig_name_no_port.format(port)
|
||||||
targ_name = self.targ_name_no_port.format(port)
|
targ_name = self.targ_name_no_port.format(port)
|
||||||
else:
|
else:
|
||||||
|
|
@ -64,11 +73,10 @@ class delay_measure(spice_measurement):
|
||||||
|
|
||||||
return (meas_name,trig_name,targ_name,trig_val,targ_val,self.trig_dir_str,self.targ_dir_str,trig_td,targ_td)
|
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):
|
class slew_measure(delay_measure):
|
||||||
|
|
||||||
def __init__(self, measure_name, signal_name, slew_dir_str):
|
def __init__(self, measure_name, signal_name, slew_dir_str, measure_scale=None):
|
||||||
spice_measurement.__init__(self, measure_name)
|
spice_measurement.__init__(self, measure_name, measure_scale)
|
||||||
self.set_meas_constants(signal_name, slew_dir_str)
|
self.set_meas_constants(signal_name, slew_dir_str)
|
||||||
|
|
||||||
def set_meas_constants(self, signal_name, slew_dir_str):
|
def set_meas_constants(self, signal_name, slew_dir_str):
|
||||||
|
|
@ -90,3 +98,25 @@ class slew_measure(delay_measure):
|
||||||
|
|
||||||
#Time delays and ports are variant and needed as inputs when writing the measurement
|
#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."""
|
||||||
|
|
||||||
|
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)
|
||||||
Loading…
Reference in New Issue