2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 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.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2018-12-20 03:33:06 +01:00
|
|
|
import debug
|
|
|
|
|
from tech import drc, parameter, spice
|
|
|
|
|
from abc import ABC, abstractmethod
|
|
|
|
|
from .stimuli import *
|
2018-12-21 00:54:56 +01:00
|
|
|
from .charutils import *
|
2018-12-20 03:33:06 +01:00
|
|
|
|
|
|
|
|
class spice_measurement(ABC):
|
|
|
|
|
"""Base class for spice stimulus measurements."""
|
2019-05-21 03:35:52 +02:00
|
|
|
def __init__(self, measure_name, measure_scale=None, has_port=True):
|
2018-12-20 03:33:06 +01:00
|
|
|
#Names must be unique for correct spice simulation, but not enforced here.
|
|
|
|
|
self.name = measure_name
|
2018-12-21 00:54:56 +01:00
|
|
|
self.measure_scale = measure_scale
|
2019-05-21 03:35:52 +02:00
|
|
|
self.has_port = has_port #Needed for error checking
|
2019-02-06 09:46:25 +01:00
|
|
|
#Some meta values used externally. variables are added here for consistency accross the objects
|
|
|
|
|
self.meta_str = None
|
|
|
|
|
self.meta_add_delay = False
|
2018-12-20 03:33:06 +01:00
|
|
|
@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)
|
2018-12-21 00:54:56 +01:00
|
|
|
|
2019-05-21 03:35:52 +02:00
|
|
|
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()))
|
2018-12-21 00:54:56 +01:00
|
|
|
if type(value)!=float or self.measure_scale == None:
|
|
|
|
|
return value
|
|
|
|
|
else:
|
|
|
|
|
return value*self.measure_scale
|
2019-05-21 03:35:52 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
2018-12-20 03:33:06 +01:00
|
|
|
class delay_measure(spice_measurement):
|
|
|
|
|
"""Generates a spice measurement for the delay of 50%-to-50% points of two signals."""
|
|
|
|
|
|
2019-05-21 03:35:52 +02:00
|
|
|
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)
|
2019-01-03 14:51:28 +01:00
|
|
|
self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd)
|
2018-12-20 03:33:06 +01:00
|
|
|
|
|
|
|
|
def get_measure_function(self):
|
|
|
|
|
return stimuli.gen_meas_delay
|
|
|
|
|
|
2019-01-03 14:51:28 +01:00
|
|
|
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"""
|
2018-12-20 03:33:06 +01:00
|
|
|
self.trig_dir_str = trig_dir_str
|
|
|
|
|
self.targ_dir_str = targ_dir_str
|
2019-01-03 14:51:28 +01:00
|
|
|
self.trig_val_of_vdd = trig_vdd
|
|
|
|
|
self.targ_val_of_vdd = targ_vdd
|
2018-12-20 03:33:06 +01:00
|
|
|
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."""
|
2019-05-21 03:35:52 +02:00
|
|
|
self.port_error_check(port)
|
2018-12-20 03:33:06 +01:00
|
|
|
trig_val = self.trig_val_of_vdd * vdd_voltage
|
|
|
|
|
targ_val = self.targ_val_of_vdd * vdd_voltage
|
2019-05-21 03:35:52 +02:00
|
|
|
|
2018-12-20 03:33:06 +01:00
|
|
|
if port != None:
|
2018-12-21 00:54:56 +01:00
|
|
|
#For dictionary indexing reasons, the name is formatted differently than the signals
|
|
|
|
|
meas_name = "{}{}".format(self.name, port)
|
2018-12-20 03:33:06 +01:00
|
|
|
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)
|
2018-12-21 00:54:56 +01:00
|
|
|
|
2018-12-20 03:33:06 +01:00
|
|
|
class slew_measure(delay_measure):
|
|
|
|
|
|
2019-05-21 03:35:52 +02:00
|
|
|
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)
|
2018-12-20 03:33:06 +01:00
|
|
|
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
|
2018-12-21 00:54:56 +01:00
|
|
|
|
|
|
|
|
class power_measure(spice_measurement):
|
2019-01-03 14:51:28 +01:00
|
|
|
"""Generates a spice measurement for the average power between two time points."""
|
2018-12-21 00:54:56 +01:00
|
|
|
|
2019-05-21 03:35:52 +02:00
|
|
|
def __init__(self, measure_name, power_type="", measure_scale=None, has_port=True):
|
|
|
|
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
2018-12-21 00:54:56 +01:00
|
|
|
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."""
|
2019-05-21 03:35:52 +02:00
|
|
|
self.port_error_check(port)
|
2018-12-21 00:54:56 +01:00
|
|
|
if port != None:
|
|
|
|
|
meas_name = "{}{}".format(self.name, port)
|
|
|
|
|
else:
|
|
|
|
|
meas_name = self.name
|
2019-01-03 14:51:28 +01:00
|
|
|
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."""
|
|
|
|
|
|
2019-05-21 03:35:52 +02:00
|
|
|
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)
|
2019-01-03 14:51:28 +01:00
|
|
|
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."""
|
2019-05-21 03:35:52 +02:00
|
|
|
self.port_error_check(port)
|
2019-01-03 14:51:28 +01:00
|
|
|
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
|
2019-05-21 03:35:52 +02:00
|
|
|
trig_voltage = self.trig_val_of_vdd*vdd_voltage
|
2019-05-14 04:38:46 +02:00
|
|
|
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."""
|
|
|
|
|
|
2019-05-21 03:35:52 +02:00
|
|
|
def __init__(self, measure_name, targ_name, measure_scale=None, has_port=True):
|
|
|
|
|
spice_measurement.__init__(self, measure_name, measure_scale, has_port)
|
2019-05-14 04:38:46 +02:00
|
|
|
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."""
|
2019-05-21 03:35:52 +02:00
|
|
|
self.port_error_check(port)
|
2019-05-14 04:38:46 +02:00
|
|
|
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
|
2019-05-21 03:35:52 +02:00
|
|
|
targ_name = self.targ_name_no_port
|
2019-05-14 04:38:46 +02:00
|
|
|
return (meas_name,targ_name,time_at)
|
|
|
|
|
|