From b10ef3fb7e5038a67c5ad9f187fe1099ec4ba11e Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Dec 2018 18:33:06 -0800 Subject: [PATCH] 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