mirror of https://github.com/VLSIDA/OpenRAM.git
215 lines
9.1 KiB
Python
215 lines
9.1 KiB
Python
# See LICENSE for licensing information.
|
|
#
|
|
# Copyright (c) 2016-2023 Regents of the University of California and The Board
|
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
# (acting for and on behalf of Oklahoma State University)
|
|
# All rights reserved.
|
|
#
|
|
from abc import ABC, abstractmethod
|
|
from openram import debug
|
|
from openram.tech import drc, parameter, spice
|
|
from .stimuli import *
|
|
from .charutils import *
|
|
|
|
|
|
class spice_measurement(ABC):
|
|
"""Base class for spice stimulus measurements."""
|
|
def __init__(self, measure_name, measure_scale=None, has_port=True):
|
|
#Names must be unique for correct spice simulation, but not enforced here.
|
|
self.name = measure_name
|
|
self.measure_scale = measure_scale
|
|
self.has_port = has_port #Needed for error checking
|
|
#Some meta values used externally. variables are added here for consistency accross the objects
|
|
self.meta_str = None
|
|
self.meta_add_delay = False
|
|
@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)
|
|
|
|
def retrieve_measure(self, port=None):
|
|
self.port_error_check(port)
|
|
if port != None:
|
|
value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port))
|
|
else:
|
|
value = parse_spice_list("timing", "{0}".format(self.name.lower()))
|
|
if type(value)!=float or self.measure_scale == None:
|
|
return value
|
|
else:
|
|
return value*self.measure_scale
|
|
|
|
def port_error_check(self, port):
|
|
if self.has_port and port == None:
|
|
debug.error("Cannot retrieve measurement, port input was expected.",1)
|
|
elif not self.has_port and port != None:
|
|
debug.error("Unexpected port input received during measure retrieval.",1)
|
|
|
|
|
|
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,
|
|
trig_vdd=0.5,
|
|
targ_vdd=0.5,
|
|
measure_scale=None,
|
|
has_port=True):
|
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
|
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, 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 = trig_vdd
|
|
self.targ_val_of_vdd = targ_vdd
|
|
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."""
|
|
self.port_error_check(port)
|
|
trig_val = self.trig_val_of_vdd * vdd_voltage
|
|
targ_val = self.targ_val_of_vdd * vdd_voltage
|
|
|
|
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
|
|
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, measure_scale=None, has_port=True):
|
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
|
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
|
|
|
|
|
|
class power_measure(spice_measurement):
|
|
"""Generates a spice measurement for the average power between two time points."""
|
|
|
|
def __init__(self, measure_name, power_type="", measure_scale=None, has_port=True):
|
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
|
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."""
|
|
self.port_error_check(port)
|
|
if port != None:
|
|
meas_name = "{}{}".format(self.name, port)
|
|
else:
|
|
meas_name = self.name
|
|
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, has_port=True):
|
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
|
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."""
|
|
self.port_error_check(port)
|
|
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)
|
|
|
|
|
|
class voltage_at_measure(spice_measurement):
|
|
"""Generates a spice measurement to measure the voltage at a specific time.
|
|
The time is considered variant with different periods."""
|
|
|
|
def __init__(self, measure_name, targ_name, measure_scale=None, has_port=True):
|
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
|
self.set_meas_constants(targ_name)
|
|
|
|
def get_measure_function(self):
|
|
return stimuli.gen_meas_find_voltage_at_time
|
|
|
|
def set_meas_constants(self, targ_name):
|
|
"""Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)"""
|
|
self.targ_name_no_port = targ_name
|
|
|
|
def get_measure_values(self, time_at, port=None):
|
|
"""Constructs inputs to stimulus measurement function. Variant values are inputs here."""
|
|
self.port_error_check(port)
|
|
if port != None:
|
|
# For dictionary indexing reasons, the name is formatted differently than the signals
|
|
meas_name = "{}{}".format(self.name, port)
|
|
targ_name = self.targ_name_no_port.format(port)
|
|
else:
|
|
meas_name = self.name
|
|
targ_name = self.targ_name_no_port
|
|
return (meas_name, targ_name, time_at)
|