Refactored analytical model to be it's own module with shared code moved to simulation

This commit is contained in:
Hunter Nichols 2020-12-02 14:06:39 -08:00
parent ce9036af76
commit d111041385
5 changed files with 168 additions and 53 deletions

View File

@ -11,6 +11,7 @@ import globals
from globals import OPTS,find_exe,get_tool from globals import OPTS,find_exe,get_tool
from .lib import * from .lib import *
from .delay import * from .delay import *
from .elmore import *
from .setup_hold import * from .setup_hold import *
from .functional import * from .functional import *
from .simulation import * from .simulation import *

View File

@ -52,21 +52,6 @@ class delay(simulation):
self.create_signal_names() self.create_signal_names()
self.add_graph_exclusions() self.add_graph_exclusions()
def create_measurement_names(self):
""" Create measurement names. The names themselves currently define the type of measurement """
self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
self.power_meas_names = ["read0_power",
"read1_power",
"write0_power",
"write1_power",
"disabled_read0_power",
"disabled_read1_power",
"disabled_write0_power",
"disabled_write1_power"]
# self.voltage_when_names = ["volt_bl", "volt_br"]
# self.bitline_delay_names = ["delay_bl", "delay_br"]
def create_measurement_objects(self): def create_measurement_objects(self):
""" Create the measurements used for read and write ports """ """ Create the measurements used for read and write ports """
@ -927,29 +912,11 @@ class delay(simulation):
""" """
Probe address and data can be set separately to utilize other Probe address and data can be set separately to utilize other
functions in this characterizer besides analyze. functions in this characterizer besides analyze.
Netlist reduced for simulation.
""" """
super().set_probe(probe_address, probe_data)
self.probe_address = probe_address
self.probe_data = probe_data
self.bitline_column = self.get_data_bit_column_number(probe_address, probe_data)
self.wordline_row = self.get_address_row_number(probe_address)
self.prepare_netlist() 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 get_address_row_number(self, probe_address):
"""Calculates wordline row number of data bit under test using address and column mux size"""
return int(probe_address[self.sram.col_addr_size:], 2)
def prepare_netlist(self): def prepare_netlist(self):
""" Prepare a trimmed netlist and regular netlist. """ """ Prepare a trimmed netlist and regular netlist. """
@ -1193,14 +1160,6 @@ class delay(simulation):
# Add test cycle of read/write port pair. One port could have been used already, but the other has not. # Add test cycle of read/write port pair. One port could have been used already, but the other has not.
self.gen_test_cycles_one_port(cur_read_port, cur_write_port) self.gen_test_cycles_one_port(cur_read_port, cur_write_port)
def sum_delays(self, delays):
"""Adds the delays (delay_data objects) so the correct slew is maintained"""
delay = delays[0]
for i in range(1, len(delays)):
delay+=delays[i]
return delay
def analytical_delay(self, slews, loads): def analytical_delay(self, slews, loads):
""" """
Return the analytical model results for the SRAM. Return the analytical model results for the SRAM.
@ -1301,11 +1260,3 @@ class delay(simulation):
self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
if port in self.readwrite_ports: if port in self.readwrite_ports:
self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
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
# 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

@ -0,0 +1,105 @@
# See LICENSE for licensing information.
#
# 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.
#
from .simulation import simulation
from globals import OPTS
import debug
class elmore(simulation):
"""
Delay model for the SRAM which calculates Elmore delays along the SRAM critical path.
"""
def __init__(self, sram, spfile, corner):
super().__init__(sram, spfile, corner)
# self.targ_read_ports = []
# self.targ_write_ports = []
# self.period = 0
# if self.write_size:
# self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
# else:
# self.num_wmasks = 0
#self.set_load_slew(0, 0)
self.set_corner(corner)
self.create_signal_names()
self.add_graph_exclusions()
def analytical_delay(self, slews, loads):
"""
Return the analytical model results for the SRAM.
"""
if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0:
debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0' * self.addr_size, 0)
self.create_graph()
self.set_internal_spice_names()
self.create_measurement_names()
port = self.read_ports[0]
self.graph.get_all_paths('{}{}'.format("clk", port),
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
# Select the path with the bitline (bl)
bl_name, br_name = self.get_bl_name(self.graph.all_paths, port)
bl_path = [path for path in self.graph.all_paths if bl_name in path][0]
# Set delay/power for slews and loads
port_data = self.get_empty_measure_data_dict()
power = self.analytical_power(slews, loads)
debug.info(1, 'Slew, Load, Delay(ns), Slew(ns)')
max_delay = 0.0
for slew in slews:
for load in loads:
# Calculate delay based on slew and load
path_delays = self.graph.get_timing(bl_path, self.corner, slew, load)
total_delay = self.sum_delays(path_delays)
max_delay = max(max_delay, total_delay.delay)
debug.info(1,
'{}, {}, {}, {}'.format(slew,
load,
total_delay.delay / 1e3,
total_delay.slew / 1e3))
# Delay is only calculated on a single port and replicated for now.
for port in self.all_ports:
for mname in self.delay_meas_names + self.power_meas_names:
if "power" in mname:
port_data[port][mname].append(power.dynamic)
elif "delay" in mname and port in self.read_ports:
port_data[port][mname].append(total_delay.delay / 1e3)
elif "slew" in mname and port in self.read_ports:
port_data[port][mname].append(total_delay.slew / 1e3)
else:
debug.error("Measurement name not recognized: {}".format(mname), 1)
# Estimate the period as double the delay with margin
period_margin = 0.1
sram_data = {"min_period": (max_delay / 1e3) * 2 * period_margin,
"leakage_power": power.leakage}
debug.info(2, "SRAM Data:\n{}".format(sram_data))
debug.info(2, "Port Data:\n{}".format(port_data))
return (sram_data, port_data)
def analytical_power(self, slews, loads):
"""Get the dynamic and leakage power from the SRAM"""
# slews unused, only last load is used
load = loads[-1]
power = self.sram.analytical_power(self.corner, load)
# convert from nW to mW
power.dynamic /= 1e6
power.leakage /= 1e6
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
return power

View File

@ -11,6 +11,7 @@ import math
import datetime import datetime
from .setup_hold import * from .setup_hold import *
from .delay import * from .delay import *
from .elmore import *
from .charutils import * from .charutils import *
import tech import tech
import numpy as np import numpy as np
@ -582,11 +583,12 @@ class lib:
def compute_delay(self): def compute_delay(self):
"""Compute SRAM delays for current corner""" """Compute SRAM delays for current corner"""
self.d = delay(self.sram, self.sp_file, self.corner)
if self.use_model: if self.use_model:
self.d = elmore(self.sram, self.sp_file, self.corner)
char_results = self.d.analytical_delay(self.slews,self.loads) char_results = self.d.analytical_delay(self.slews,self.loads)
self.char_sram_results, self.char_port_results = char_results self.char_sram_results, self.char_port_results = char_results
else: else:
self.d = delay(self.sram, self.sp_file, self.corner)
if (self.sram.num_spare_rows == 0): if (self.sram.num_spare_rows == 0):
probe_address = "1" * self.sram.addr_size probe_address = "1" * self.sram.addr_size
else: else:

View File

@ -39,6 +39,21 @@ class simulation():
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
def create_measurement_names(self):
""" Create measurement names. The names themselves currently define the type of measurement """
self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
self.power_meas_names = ["read0_power",
"read1_power",
"write0_power",
"write1_power",
"disabled_read0_power",
"disabled_read1_power",
"disabled_write0_power",
"disabled_write1_power"]
# self.voltage_when_names = ["volt_bl", "volt_br"]
# self.bitline_delay_names = ["delay_bl", "delay_br"]
def set_corner(self, corner): def set_corner(self, corner):
""" Set the corner values """ """ Set the corner values """
self.corner = corner self.corner = corner
@ -92,6 +107,32 @@ class simulation():
self.cycle_comments = [] self.cycle_comments = []
self.fn_cycle_comments = [] self.fn_cycle_comments = []
def set_probe(self, probe_address, probe_data):
"""
Probe address and data can be set separately to utilize other
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.wordline_row = self.get_address_row_number(probe_address)
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 get_address_row_number(self, probe_address):
"""Calculates wordline row number of data bit under test using address and column mux size"""
return int(probe_address[self.sram.col_addr_size:], 2)
def add_control_one_port(self, port, op): def add_control_one_port(self, port, op):
"""Appends control signals for operation to a given port""" """Appends control signals for operation to a given port"""
# Determine values to write to port # Determine values to write to port
@ -539,6 +580,21 @@ class simulation():
for i in range(len(bl_names)): for i in range(len(bl_names)):
bl_names[i] = bl_names[i].split('.')[-1] bl_names[i] = bl_names[i].split('.')[-1]
return bl_names[0], bl_names[1] return bl_names[0], bl_names[1]
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
# 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
def sum_delays(self, delays):
"""Adds the delays (delay_data objects) so the correct slew is maintained"""
delay = delays[0]
for i in range(1, len(delays)):
delay+=delays[i]
return delay