2018-02-21 22:38:43 +01:00
|
|
|
import sys,re,shutil
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
|
|
|
|
import tech
|
|
|
|
|
import math
|
2018-05-12 01:32:00 +02:00
|
|
|
from .stimuli import *
|
|
|
|
|
from .trim_spice import *
|
|
|
|
|
from .charutils import *
|
2016-11-11 18:41:43 +01:00
|
|
|
import utils
|
2017-11-16 22:52:58 +01:00
|
|
|
from globals import OPTS
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
class delay():
|
2018-02-21 22:38:43 +01:00
|
|
|
"""Functions to measure the delay and power of an SRAM at a given address and
|
2016-11-08 18:57:35 +01:00
|
|
|
data bit.
|
2018-02-21 22:38:43 +01:00
|
|
|
|
|
|
|
|
In general, this will perform the following actions:
|
|
|
|
|
1) Trim the netlist to remove unnecessary logic.
|
|
|
|
|
2) Find a feasible clock period using max load/slew on the trimmed netlist.
|
|
|
|
|
3) Characterize all loads/slews and consider fail when delay is greater than 5% of feasible delay using trimmed netlist.
|
|
|
|
|
4) Measure the leakage during the last cycle of the trimmed netlist when there is no operation.
|
|
|
|
|
5) Measure the leakage of the whole netlist (untrimmed) in each corner.
|
|
|
|
|
6) Subtract the trimmed leakage and add the untrimmed leakage to the power.
|
|
|
|
|
|
|
|
|
|
Netlist trimming can be removed by setting OPTS.trim_netlist to
|
|
|
|
|
False, but this is VERY slow.
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
def __init__(self, sram, spfile, corner):
|
|
|
|
|
self.sram = sram
|
2016-11-08 18:57:35 +01:00
|
|
|
self.name = sram.name
|
2018-02-21 22:38:43 +01:00
|
|
|
self.word_size = self.sram.word_size
|
|
|
|
|
self.addr_size = self.sram.addr_size
|
|
|
|
|
self.num_cols = self.sram.num_cols
|
|
|
|
|
self.num_rows = self.sram.num_rows
|
|
|
|
|
self.num_banks = self.sram.num_banks
|
|
|
|
|
self.sp_file = spfile
|
2017-11-14 22:24:14 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
# These are the member variables for a simulation
|
|
|
|
|
self.period = 0
|
2018-02-26 17:54:35 +01:00
|
|
|
self.set_load_slew(0,0)
|
2018-02-10 00:33:03 +01:00
|
|
|
self.set_corner(corner)
|
2018-02-26 17:54:35 +01:00
|
|
|
|
2018-02-10 00:33:03 +01:00
|
|
|
def set_corner(self,corner):
|
|
|
|
|
""" Set the corner values """
|
|
|
|
|
self.corner = corner
|
|
|
|
|
(self.process, self.vdd_voltage, self.temperature) = corner
|
|
|
|
|
|
2018-02-26 17:54:35 +01:00
|
|
|
def set_load_slew(self,load,slew):
|
|
|
|
|
""" Set the load and slew """
|
|
|
|
|
self.load = load
|
|
|
|
|
self.slew = slew
|
2017-11-23 00:57:29 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def check_arguments(self):
|
|
|
|
|
"""Checks if arguments given for write_stimulus() meets requirements"""
|
|
|
|
|
try:
|
|
|
|
|
int(self.probe_address, 2)
|
|
|
|
|
except ValueError:
|
|
|
|
|
debug.error("Probe Address is not of binary form: {0}".format(self.probe_address),1)
|
|
|
|
|
|
|
|
|
|
if len(self.probe_address) != self.addr_size:
|
|
|
|
|
debug.error("Probe Address's number of bits does not correspond to given SRAM",1)
|
|
|
|
|
|
|
|
|
|
if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0:
|
|
|
|
|
debug.error("Given probe_data is not an integer to specify a data bit",1)
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def write_generic_stimulus(self):
|
2018-02-21 22:38:43 +01:00
|
|
|
""" Create the instance, supplies, loads, and access transistors. """
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# add vdd/gnd statements
|
2018-02-07 23:04:18 +01:00
|
|
|
self.sf.write("\n* Global Power Supplies\n")
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.write_supply()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# instantiate the sram
|
2018-02-07 23:04:18 +01:00
|
|
|
self.sf.write("\n* Instantiation of the SRAM\n")
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.inst_sram(abits=self.addr_size,
|
|
|
|
|
dbits=self.word_size,
|
|
|
|
|
sram_name=self.name)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 23:04:18 +01:00
|
|
|
self.sf.write("\n* SRAM output loads\n")
|
2017-07-06 17:42:25 +02:00
|
|
|
for i in range(self.word_size):
|
2018-02-22 20:14:58 +01:00
|
|
|
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,self.load))
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
# add access transistors for data-bus
|
2018-02-07 23:04:18 +01:00
|
|
|
self.sf.write("\n* Transmission Gates for data-bus and control signals\n")
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.inst_accesstx(dbits=self.word_size)
|
2018-02-21 22:38:43 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def write_delay_stimulus(self):
|
2018-02-21 22:38:43 +01:00
|
|
|
""" Creates a stimulus file for simulations to probe a bitcell at a given clock period.
|
|
|
|
|
Address and bit were previously set with set_probe().
|
|
|
|
|
Input slew (in ns) and output capacitive load (in fF) are required for charaterization.
|
|
|
|
|
"""
|
|
|
|
|
self.check_arguments()
|
|
|
|
|
|
|
|
|
|
# obtains list of time-points for each rising clk edge
|
2018-02-22 20:14:58 +01:00
|
|
|
self.obtain_cycle_times()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
# creates and opens stimulus file for writing
|
|
|
|
|
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
|
|
|
|
self.sf = open(temp_stim, "w")
|
2018-02-22 20:14:58 +01:00
|
|
|
self.sf.write("* Delay stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(self.period,
|
|
|
|
|
self.load,
|
|
|
|
|
self.slew))
|
2018-05-12 01:32:00 +02:00
|
|
|
self.stim = stimuli(self.sf, self.corner)
|
2018-02-21 22:38:43 +01:00
|
|
|
# include files in stimulus file
|
|
|
|
|
self.stim.write_include(self.trim_sp_file)
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
self.write_generic_stimulus()
|
2018-02-21 22:38:43 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
# generate data and addr signals
|
2018-02-07 23:04:18 +01:00
|
|
|
self.sf.write("\n* Generation of data and address signals\n")
|
2016-11-08 18:57:35 +01:00
|
|
|
for i in range(self.word_size):
|
|
|
|
|
if i == self.probe_data:
|
2018-02-07 21:58:47 +01:00
|
|
|
self.gen_data(clk_times=self.cycle_times,
|
2018-02-22 20:14:58 +01:00
|
|
|
sig_name="data[{0}]".format(i))
|
|
|
|
|
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
else:
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.gen_constant(sig_name="d[{0}]".format(i),
|
2018-02-21 22:38:43 +01:00
|
|
|
v_val=0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
self.gen_addr(clk_times=self.cycle_times,
|
2018-02-22 20:14:58 +01:00
|
|
|
addr=self.probe_address)
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# generate control signals
|
2018-02-07 23:04:18 +01:00
|
|
|
self.sf.write("\n* Generation of control signals\n")
|
2018-02-22 20:14:58 +01:00
|
|
|
self.gen_csb(self.cycle_times)
|
|
|
|
|
self.gen_web(self.cycle_times)
|
|
|
|
|
self.gen_oeb(self.cycle_times)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 23:04:18 +01:00
|
|
|
self.sf.write("\n* Generation of global clock signal\n")
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.gen_pulse(sig_name="CLK",
|
2018-02-21 22:38:43 +01:00
|
|
|
v1=0,
|
2018-02-10 00:33:03 +01:00
|
|
|
v2=self.vdd_voltage,
|
2018-02-22 20:14:58 +01:00
|
|
|
offset=self.period,
|
|
|
|
|
period=self.period,
|
|
|
|
|
t_rise=self.slew,
|
|
|
|
|
t_fall=self.slew)
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
self.write_delay_measures()
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2018-02-05 23:07:12 +01:00
|
|
|
# run until the end of the cycle time
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.write_control(self.cycle_times[-1] + self.period)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
self.sf.close()
|
|
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def write_power_stimulus(self, trim):
|
2018-02-21 22:38:43 +01:00
|
|
|
""" Creates a stimulus file to measure leakage power only.
|
|
|
|
|
This works on the *untrimmed netlist*.
|
|
|
|
|
"""
|
|
|
|
|
self.check_arguments()
|
|
|
|
|
|
|
|
|
|
# obtains list of time-points for each rising clk edge
|
2018-02-22 20:14:58 +01:00
|
|
|
self.obtain_cycle_times()
|
2018-02-21 22:38:43 +01:00
|
|
|
|
|
|
|
|
# creates and opens stimulus file for writing
|
|
|
|
|
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
|
|
|
|
self.sf = open(temp_stim, "w")
|
2018-02-22 20:14:58 +01:00
|
|
|
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
|
2018-02-21 22:38:43 +01:00
|
|
|
self.stim = stimuli.stimuli(self.sf, self.corner)
|
|
|
|
|
|
|
|
|
|
# include UNTRIMMED files in stimulus file
|
|
|
|
|
if trim:
|
|
|
|
|
self.stim.write_include(self.trim_sp_file)
|
|
|
|
|
else:
|
|
|
|
|
self.stim.write_include(self.sim_sp_file)
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
self.write_generic_stimulus()
|
2018-02-21 22:38:43 +01:00
|
|
|
|
|
|
|
|
# generate data and addr signals
|
|
|
|
|
self.sf.write("\n* Generation of data and address signals\n")
|
|
|
|
|
for i in range(self.word_size):
|
|
|
|
|
self.stim.gen_constant(sig_name="d[{0}]".format(i),
|
|
|
|
|
v_val=0)
|
|
|
|
|
for i in range(self.addr_size):
|
|
|
|
|
self.stim.gen_constant(sig_name="A[{0}]".format(i),
|
|
|
|
|
v_val=0)
|
|
|
|
|
|
|
|
|
|
# generate control signals
|
|
|
|
|
self.sf.write("\n* Generation of control signals\n")
|
|
|
|
|
self.stim.gen_constant(sig_name="CSb", v_val=self.vdd_voltage)
|
|
|
|
|
self.stim.gen_constant(sig_name="WEb", v_val=self.vdd_voltage)
|
|
|
|
|
self.stim.gen_constant(sig_name="OEb", v_val=self.vdd_voltage)
|
|
|
|
|
|
|
|
|
|
self.sf.write("\n* Generation of global clock signal\n")
|
|
|
|
|
self.stim.gen_constant(sig_name="CLK", v_val=0)
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
self.write_power_measures()
|
2018-02-21 22:38:43 +01:00
|
|
|
|
|
|
|
|
# run until the end of the cycle time
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.write_control(2*self.period)
|
2018-02-21 22:38:43 +01:00
|
|
|
|
|
|
|
|
self.sf.close()
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def write_delay_measures(self):
|
2018-02-07 23:04:18 +01:00
|
|
|
"""
|
|
|
|
|
Write the measure statements to quantify the delay and power results.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
self.sf.write("\n* Measure statements for delay and power\n")
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 23:04:18 +01:00
|
|
|
# Output some comments to aid where cycles start and
|
|
|
|
|
# what is happening
|
2018-02-03 04:33:07 +01:00
|
|
|
for comment in self.cycle_comments:
|
|
|
|
|
self.sf.write("* {}\n".format(comment))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 23:04:18 +01:00
|
|
|
# Trigger on the clk of the appropriate cycle
|
2017-07-06 17:42:25 +02:00
|
|
|
trig_name = "clk"
|
2017-09-30 01:22:13 +02:00
|
|
|
targ_name = "{0}".format("d[{0}]".format(self.probe_data))
|
2018-02-10 00:33:03 +01:00
|
|
|
trig_val = targ_val = 0.5 * self.vdd_voltage
|
2018-02-07 23:04:18 +01:00
|
|
|
|
|
|
|
|
# Delay the target to measure after the negative edge
|
2018-02-22 01:03:49 +01:00
|
|
|
self.stim.gen_meas_delay(meas_name="DELAY_HL",
|
2018-02-10 00:33:03 +01:00
|
|
|
trig_name=trig_name,
|
|
|
|
|
targ_name=targ_name,
|
|
|
|
|
trig_val=trig_val,
|
|
|
|
|
targ_val=targ_val,
|
|
|
|
|
trig_dir="FALL",
|
|
|
|
|
targ_dir="FALL",
|
|
|
|
|
trig_td=self.cycle_times[self.read0_cycle],
|
2018-02-22 20:14:58 +01:00
|
|
|
targ_td=self.cycle_times[self.read0_cycle]+0.5*self.period)
|
2018-02-10 00:33:03 +01:00
|
|
|
|
2018-02-22 01:03:49 +01:00
|
|
|
self.stim.gen_meas_delay(meas_name="DELAY_LH",
|
2018-02-10 00:33:03 +01:00
|
|
|
trig_name=trig_name,
|
|
|
|
|
targ_name=targ_name,
|
|
|
|
|
trig_val=trig_val,
|
|
|
|
|
targ_val=targ_val,
|
|
|
|
|
trig_dir="FALL",
|
|
|
|
|
targ_dir="RISE",
|
|
|
|
|
trig_td=self.cycle_times[self.read1_cycle],
|
2018-02-22 20:14:58 +01:00
|
|
|
targ_td=self.cycle_times[self.read1_cycle]+0.5*self.period)
|
2018-02-10 00:33:03 +01:00
|
|
|
|
2018-02-22 01:03:49 +01:00
|
|
|
self.stim.gen_meas_delay(meas_name="SLEW_HL",
|
2018-02-10 00:33:03 +01:00
|
|
|
trig_name=targ_name,
|
|
|
|
|
targ_name=targ_name,
|
|
|
|
|
trig_val=0.9*self.vdd_voltage,
|
|
|
|
|
targ_val=0.1*self.vdd_voltage,
|
|
|
|
|
trig_dir="FALL",
|
|
|
|
|
targ_dir="FALL",
|
|
|
|
|
trig_td=self.cycle_times[self.read0_cycle],
|
2018-02-22 20:14:58 +01:00
|
|
|
targ_td=self.cycle_times[self.read0_cycle]+0.5*self.period)
|
2018-02-10 00:33:03 +01:00
|
|
|
|
2018-02-22 01:03:49 +01:00
|
|
|
self.stim.gen_meas_delay(meas_name="SLEW_LH",
|
2018-02-10 00:33:03 +01:00
|
|
|
trig_name=targ_name,
|
|
|
|
|
targ_name=targ_name,
|
|
|
|
|
trig_val=0.1*self.vdd_voltage,
|
|
|
|
|
targ_val=0.9*self.vdd_voltage,
|
|
|
|
|
trig_dir="RISE",
|
|
|
|
|
targ_dir="RISE",
|
|
|
|
|
trig_td=self.cycle_times[self.read1_cycle],
|
2018-02-22 20:14:58 +01:00
|
|
|
targ_td=self.cycle_times[self.read1_cycle]+0.5*self.period)
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
# add measure statements for power
|
2017-07-06 17:42:25 +02:00
|
|
|
t_initial = self.cycle_times[self.write0_cycle]
|
|
|
|
|
t_final = self.cycle_times[self.write0_cycle+1]
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.gen_meas_power(meas_name="WRITE0_POWER",
|
|
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
t_initial = self.cycle_times[self.write1_cycle]
|
|
|
|
|
t_final = self.cycle_times[self.write1_cycle+1]
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.gen_meas_power(meas_name="WRITE1_POWER",
|
|
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
2017-07-06 17:42:25 +02:00
|
|
|
|
|
|
|
|
t_initial = self.cycle_times[self.read0_cycle]
|
|
|
|
|
t_final = self.cycle_times[self.read0_cycle+1]
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.gen_meas_power(meas_name="READ0_POWER",
|
|
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
t_initial = self.cycle_times[self.read1_cycle]
|
|
|
|
|
t_final = self.cycle_times[self.read1_cycle+1]
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.gen_meas_power(meas_name="READ1_POWER",
|
|
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
2018-02-21 22:38:43 +01:00
|
|
|
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def write_power_measures(self):
|
2018-02-21 22:38:43 +01:00
|
|
|
"""
|
|
|
|
|
Write the measure statements to quantify the leakage power only.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
self.sf.write("\n* Measure statements for idle leakage power\n")
|
|
|
|
|
|
|
|
|
|
# add measure statements for power
|
2018-02-22 20:14:58 +01:00
|
|
|
t_initial = self.period
|
|
|
|
|
t_final = 2*self.period
|
2018-02-22 00:45:49 +01:00
|
|
|
self.stim.gen_meas_power(meas_name="leakage_power",
|
2018-02-21 22:38:43 +01:00
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def find_feasible_period(self):
|
2018-02-07 23:04:18 +01:00
|
|
|
"""
|
|
|
|
|
Uses an initial period and finds a feasible period before we
|
2016-11-08 18:57:35 +01:00
|
|
|
run the binary search algorithm to find min period. We check if
|
|
|
|
|
the given clock period is valid and if it's not, we continue to
|
|
|
|
|
double the period until we find a valid period to use as a
|
2018-02-07 23:04:18 +01:00
|
|
|
starting point.
|
|
|
|
|
"""
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-25 20:13:43 +01:00
|
|
|
feasible_period = float(tech.spice["feasible_period"])
|
2016-11-08 18:57:35 +01:00
|
|
|
time_out = 8
|
|
|
|
|
while True:
|
2017-08-07 19:24:45 +02:00
|
|
|
debug.info(1, "Trying feasible period: {0}ns".format(feasible_period))
|
2016-11-08 18:57:35 +01:00
|
|
|
time_out -= 1
|
|
|
|
|
|
|
|
|
|
if (time_out <= 0):
|
|
|
|
|
debug.error("Timed out, could not find a feasible period.",2)
|
2018-02-22 20:14:58 +01:00
|
|
|
self.period = feasible_period
|
|
|
|
|
(success, results)=self.run_delay_simulation()
|
2016-11-08 18:57:35 +01:00
|
|
|
if not success:
|
|
|
|
|
feasible_period = 2 * feasible_period
|
|
|
|
|
continue
|
2018-02-22 01:03:49 +01:00
|
|
|
feasible_delay_lh = results["delay_lh"]
|
|
|
|
|
feasible_slew_lh = results["slew_lh"]
|
|
|
|
|
feasible_delay_hl = results["delay_hl"]
|
|
|
|
|
feasible_slew_hl = results["slew_hl"]
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-25 20:13:43 +01:00
|
|
|
debug.info(1, "Found feasible_period: {0}ns feasible_delay {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period,
|
|
|
|
|
feasible_delay_lh,
|
|
|
|
|
feasible_delay_hl,
|
|
|
|
|
feasible_slew_lh,
|
|
|
|
|
feasible_slew_hl))
|
2018-02-22 20:14:58 +01:00
|
|
|
self.period = feasible_period
|
|
|
|
|
return (feasible_delay_lh, feasible_delay_hl)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def run_delay_simulation(self):
|
2018-02-21 22:38:43 +01:00
|
|
|
"""
|
|
|
|
|
This tries to simulate a period and checks if the result works. If
|
|
|
|
|
so, it returns True and the delays, slews, and powers. It
|
|
|
|
|
works on the trimmed netlist by default, so powers do not
|
|
|
|
|
include leakage of all cells.
|
2018-02-07 23:04:18 +01:00
|
|
|
"""
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# Checking from not data_value to data_value
|
2018-02-22 20:14:58 +01:00
|
|
|
self.write_delay_stimulus()
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.run_sim()
|
2018-05-12 01:32:00 +02:00
|
|
|
delay_hl = parse_output("timing", "delay_hl")
|
|
|
|
|
delay_lh = parse_output("timing", "delay_lh")
|
|
|
|
|
slew_hl = parse_output("timing", "slew_hl")
|
|
|
|
|
slew_lh = parse_output("timing", "slew_lh")
|
2018-02-22 01:03:49 +01:00
|
|
|
delays = (delay_hl, delay_lh, slew_hl, slew_lh)
|
2018-02-21 22:38:43 +01:00
|
|
|
|
2018-05-12 01:32:00 +02:00
|
|
|
read0_power=parse_output("timing", "read0_power")
|
|
|
|
|
write0_power=parse_output("timing", "write0_power")
|
|
|
|
|
read1_power=parse_output("timing", "read1_power")
|
|
|
|
|
write1_power=parse_output("timing", "write1_power")
|
2018-02-21 22:38:43 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
if not self.check_valid_delays(delays):
|
2018-02-21 22:38:43 +01:00
|
|
|
return (False,{})
|
|
|
|
|
|
2018-02-07 23:04:18 +01:00
|
|
|
# For debug, you sometimes want to inspect each simulation.
|
2016-11-08 18:57:35 +01:00
|
|
|
#key=raw_input("press return to continue")
|
|
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
# Scale results to ns and mw, respectively
|
2018-02-22 01:03:49 +01:00
|
|
|
result = { "delay_hl" : delay_hl*1e9,
|
|
|
|
|
"delay_lh" : delay_lh*1e9,
|
|
|
|
|
"slew_hl" : slew_hl*1e9,
|
|
|
|
|
"slew_lh" : slew_lh*1e9,
|
2018-02-21 22:38:43 +01:00
|
|
|
"read0_power" : read0_power*1e3,
|
|
|
|
|
"read1_power" : read1_power*1e3,
|
|
|
|
|
"write0_power" : write0_power*1e3,
|
|
|
|
|
"write1_power" : write1_power*1e3}
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# The delay is from the negative edge for our SRAM
|
2018-02-21 22:38:43 +01:00
|
|
|
return (True,result)
|
|
|
|
|
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def run_power_simulation(self):
|
2018-02-21 22:38:43 +01:00
|
|
|
"""
|
|
|
|
|
This simulates a disabled SRAM to get the leakage power when it is off.
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
"""
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
self.write_power_stimulus(trim=False)
|
2018-02-21 22:38:43 +01:00
|
|
|
self.stim.run_sim()
|
2018-05-12 01:32:00 +02:00
|
|
|
leakage_power=parse_output("timing", "leakage_power")
|
2018-02-21 22:38:43 +01:00
|
|
|
debug.check(leakage_power!="Failed","Could not measure leakage power.")
|
|
|
|
|
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
self.write_power_stimulus(trim=True)
|
2018-02-21 22:38:43 +01:00
|
|
|
self.stim.run_sim()
|
2018-05-12 01:32:00 +02:00
|
|
|
trim_leakage_power=parse_output("timing", "leakage_power")
|
2018-02-21 22:38:43 +01:00
|
|
|
debug.check(trim_leakage_power!="Failed","Could not measure leakage power.")
|
|
|
|
|
|
|
|
|
|
# For debug, you sometimes want to inspect each simulation.
|
|
|
|
|
#key=raw_input("press return to continue")
|
|
|
|
|
return (leakage_power*1e3, trim_leakage_power*1e3)
|
|
|
|
|
|
2018-05-12 01:32:00 +02:00
|
|
|
def check_valid_delays(self, delay_tuple):
|
2018-02-21 22:38:43 +01:00
|
|
|
""" Check if the measurements are defined and if they are valid. """
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-05-12 01:32:00 +02:00
|
|
|
(delay_hl, delay_lh, slew_hl, slew_lh) = delay_tuple
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
# if it failed or the read was longer than a period
|
2018-02-22 01:03:49 +01:00
|
|
|
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
|
2018-02-22 20:14:58 +01:00
|
|
|
debug.info(2,"Failed simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
|
|
|
|
|
self.load,
|
|
|
|
|
self.slew,
|
|
|
|
|
delay_hl,
|
|
|
|
|
delay_lh,
|
|
|
|
|
slew_hl,
|
|
|
|
|
slew_lh))
|
2018-02-21 22:38:43 +01:00
|
|
|
return False
|
2018-02-07 23:04:18 +01:00
|
|
|
# Scale delays to ns (they previously could have not been floats)
|
2018-02-22 01:03:49 +01:00
|
|
|
delay_hl *= 1e9
|
|
|
|
|
delay_lh *= 1e9
|
|
|
|
|
slew_hl *= 1e9
|
|
|
|
|
slew_lh *= 1e9
|
2018-02-22 20:14:58 +01:00
|
|
|
if delay_hl>self.period or delay_lh>self.period or slew_hl>self.period or slew_lh>self.period:
|
|
|
|
|
debug.info(2,"UNsuccessful simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
|
|
|
|
|
self.load,
|
|
|
|
|
self.slew,
|
|
|
|
|
delay_hl,
|
|
|
|
|
delay_lh,
|
|
|
|
|
slew_hl,
|
|
|
|
|
slew_lh))
|
2018-02-21 22:38:43 +01:00
|
|
|
return False
|
2016-11-08 18:57:35 +01:00
|
|
|
else:
|
2018-02-22 20:14:58 +01:00
|
|
|
debug.info(2,"Successful simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
|
|
|
|
|
self.load,
|
|
|
|
|
self.slew,
|
|
|
|
|
delay_hl,
|
|
|
|
|
delay_lh,
|
|
|
|
|
slew_hl,
|
|
|
|
|
slew_lh))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
return True
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def find_min_period(self, feasible_delay_lh, feasible_delay_hl):
|
2018-02-07 23:04:18 +01:00
|
|
|
"""
|
|
|
|
|
Searches for the smallest period with output delays being within 5% of
|
|
|
|
|
long period.
|
|
|
|
|
"""
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
previous_period = ub_period = self.period
|
2016-11-08 18:57:35 +01:00
|
|
|
lb_period = 0.0
|
|
|
|
|
|
|
|
|
|
# Binary search algorithm to find the min period (max frequency) of design
|
|
|
|
|
time_out = 25
|
|
|
|
|
while True:
|
|
|
|
|
time_out -= 1
|
|
|
|
|
if (time_out <= 0):
|
|
|
|
|
debug.error("Timed out, could not converge on minimum period.",2)
|
|
|
|
|
|
|
|
|
|
target_period = 0.5 * (ub_period + lb_period)
|
2018-02-22 20:14:58 +01:00
|
|
|
self.period = target_period
|
2016-11-08 18:57:35 +01:00
|
|
|
debug.info(1, "MinPeriod Search: {0}ns (ub: {1} lb: {2})".format(target_period,
|
2016-11-11 23:25:46 +01:00
|
|
|
ub_period,
|
|
|
|
|
lb_period))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
if self.try_period(feasible_delay_lh, feasible_delay_hl):
|
2016-11-08 18:57:35 +01:00
|
|
|
ub_period = target_period
|
|
|
|
|
else:
|
|
|
|
|
lb_period = target_period
|
|
|
|
|
|
2018-05-12 01:32:00 +02:00
|
|
|
if relative_compare(ub_period, lb_period, error_tolerance=0.05):
|
2017-07-06 17:42:25 +02:00
|
|
|
# ub_period is always feasible
|
|
|
|
|
return ub_period
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def try_period(self, feasible_delay_lh, feasible_delay_hl):
|
2018-02-07 23:04:18 +01:00
|
|
|
"""
|
|
|
|
|
This tries to simulate a period and checks if the result
|
|
|
|
|
works. If it does and the delay is within 5% still, it returns True.
|
|
|
|
|
"""
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# Checking from not data_value to data_value
|
2018-02-22 20:14:58 +01:00
|
|
|
self.write_delay_stimulus()
|
2018-02-10 00:33:03 +01:00
|
|
|
self.stim.run_sim()
|
2018-05-12 01:32:00 +02:00
|
|
|
delay_hl = parse_output("timing", "delay_hl")
|
|
|
|
|
delay_lh = parse_output("timing", "delay_lh")
|
|
|
|
|
slew_hl = parse_output("timing", "slew_hl")
|
|
|
|
|
slew_lh = parse_output("timing", "slew_lh")
|
2017-07-06 17:42:25 +02:00
|
|
|
# if it failed or the read was longer than a period
|
2018-02-22 01:03:49 +01:00
|
|
|
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
|
2018-02-22 20:14:58 +01:00
|
|
|
debug.info(2,"Invalid measures: Period {0}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns".format(self.period,
|
|
|
|
|
delay_hl,
|
|
|
|
|
delay_lh,
|
|
|
|
|
slew_hl,
|
|
|
|
|
slew_lh))
|
2017-08-07 19:24:45 +02:00
|
|
|
return False
|
2018-02-22 01:03:49 +01:00
|
|
|
delay_hl *= 1e9
|
|
|
|
|
delay_lh *= 1e9
|
|
|
|
|
slew_hl *= 1e9
|
|
|
|
|
slew_lh *= 1e9
|
2018-02-22 20:14:58 +01:00
|
|
|
if delay_hl>self.period or delay_lh>self.period or slew_hl>self.period or slew_lh>self.period:
|
|
|
|
|
debug.info(2,"Too long delay/slew: Period {0}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns".format(self.period,
|
|
|
|
|
delay_hl,
|
|
|
|
|
delay_lh,
|
|
|
|
|
slew_hl,
|
|
|
|
|
slew_lh))
|
2017-07-06 17:42:25 +02:00
|
|
|
return False
|
|
|
|
|
else:
|
2018-05-12 01:32:00 +02:00
|
|
|
if not relative_compare(delay_lh,feasible_delay_lh,error_tolerance=0.05):
|
2018-02-22 01:03:49 +01:00
|
|
|
debug.info(2,"Delay too big {0} vs {1}".format(delay_lh,feasible_delay_lh))
|
2017-07-06 17:42:25 +02:00
|
|
|
return False
|
2018-05-12 01:32:00 +02:00
|
|
|
elif not relative_compare(delay_hl,feasible_delay_hl,error_tolerance=0.05):
|
2018-02-22 01:03:49 +01:00
|
|
|
debug.info(2,"Delay too big {0} vs {1}".format(delay_hl,feasible_delay_hl))
|
2017-07-06 17:42:25 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#key=raw_input("press return to continue")
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
debug.info(2,"Successful period {0}, delay_hl={1}ns, delay_lh={2}ns slew_hl={3}ns slew_lh={4}ns".format(self.period,
|
|
|
|
|
delay_hl,
|
|
|
|
|
delay_lh,
|
|
|
|
|
slew_hl,
|
|
|
|
|
slew_lh))
|
2017-07-06 17:42:25 +02:00
|
|
|
return True
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
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
|
|
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
self.prepare_netlist()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def prepare_netlist(self):
|
|
|
|
|
""" Prepare a trimmed netlist and regular netlist. """
|
|
|
|
|
|
|
|
|
|
# Set up to trim the netlist here if that is enabled
|
|
|
|
|
if OPTS.trim_netlist:
|
|
|
|
|
self.trim_sp_file = "{}reduced.sp".format(OPTS.openram_temp)
|
|
|
|
|
self.trimsp=trim_spice(self.sp_file, self.trim_sp_file)
|
|
|
|
|
self.trimsp.set_configuration(self.num_banks,
|
|
|
|
|
self.num_rows,
|
|
|
|
|
self.num_cols,
|
|
|
|
|
self.word_size)
|
|
|
|
|
self.trimsp.trim(self.probe_address,self.probe_data)
|
|
|
|
|
else:
|
|
|
|
|
# The non-reduced netlist file when it is disabled
|
|
|
|
|
self.trim_sp_file = "{}sram.sp".format(OPTS.openram_temp)
|
|
|
|
|
|
|
|
|
|
# The non-reduced netlist file for power simulation
|
|
|
|
|
self.sim_sp_file = "{}sram.sp".format(OPTS.openram_temp)
|
|
|
|
|
# Make a copy in temp for debugging
|
|
|
|
|
shutil.copy(self.sp_file, self.sim_sp_file)
|
|
|
|
|
|
|
|
|
|
|
2017-11-14 22:24:14 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
def analyze(self,probe_address, probe_data, slews, loads):
|
2018-02-21 22:38:43 +01:00
|
|
|
"""
|
|
|
|
|
Main function to characterize an SRAM for a table. Computes both delay and power characterization.
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.set_probe(probe_address, probe_data)
|
|
|
|
|
|
2017-11-23 00:57:29 +01:00
|
|
|
# This is for debugging a full simulation
|
|
|
|
|
# debug.info(0,"Debug simulation running...")
|
|
|
|
|
# target_period=50.0
|
2018-02-22 01:03:49 +01:00
|
|
|
# feasible_delay_lh=0.059083183
|
|
|
|
|
# feasible_delay_hl=0.17953789
|
2017-11-23 00:57:29 +01:00
|
|
|
# load=1.6728
|
|
|
|
|
# slew=0.04
|
2018-02-22 20:14:58 +01:00
|
|
|
# self.try_period(target_period, feasible_delay_lh, feasible_delay_hl)
|
2017-11-23 00:57:29 +01:00
|
|
|
# sys.exit(1)
|
|
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
|
|
|
|
|
# 1) Find a feasible period and it's corresponding delays using the trimmed array.
|
2018-02-22 20:14:58 +01:00
|
|
|
self.load=max(loads)
|
|
|
|
|
self.slew=max(slews)
|
|
|
|
|
(feasible_delay_lh, feasible_delay_hl) = self.find_feasible_period()
|
2018-02-22 01:03:49 +01:00
|
|
|
debug.check(feasible_delay_lh>0,"Negative delay may not be possible")
|
|
|
|
|
debug.check(feasible_delay_hl>0,"Negative delay may not be possible")
|
2017-11-23 00:57:29 +01:00
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
# 2) Measure the delay, slew and power for all slew/load pairs.
|
|
|
|
|
# Make a list for each type of measurement to append results to
|
|
|
|
|
char_data = {}
|
2018-02-22 01:03:49 +01:00
|
|
|
for m in ["delay_lh", "delay_hl", "slew_lh", "slew_hl", "read0_power",
|
2018-02-21 22:38:43 +01:00
|
|
|
"read1_power", "write0_power", "write1_power", "leakage_power"]:
|
|
|
|
|
char_data[m]=[]
|
2018-02-24 00:20:52 +01:00
|
|
|
|
|
|
|
|
# 2a) Find the leakage power of the trimmmed and UNtrimmed arrays.
|
|
|
|
|
(full_array_leakage, trim_array_leakage)=self.run_power_simulation()
|
|
|
|
|
char_data["leakage_power"]=full_array_leakage
|
2018-02-22 00:45:49 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
for slew in slews:
|
|
|
|
|
for load in loads:
|
2018-02-26 17:54:35 +01:00
|
|
|
self.set_load_slew(load,slew)
|
2018-02-21 22:38:43 +01:00
|
|
|
# 2c) Find the delay, dynamic power, and leakage power of the trimmed array.
|
2018-02-22 20:14:58 +01:00
|
|
|
(success, delay_results) = self.run_delay_simulation()
|
|
|
|
|
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load))
|
2018-02-21 22:38:43 +01:00
|
|
|
for k,v in delay_results.items():
|
|
|
|
|
if "power" in k:
|
|
|
|
|
# Subtract partial array leakage and add full array leakage for the power measures
|
2018-02-24 00:20:52 +01:00
|
|
|
char_data[k].append(v - trim_array_leakage + full_array_leakage)
|
2018-02-21 22:38:43 +01:00
|
|
|
else:
|
|
|
|
|
char_data[k].append(v)
|
|
|
|
|
|
|
|
|
|
|
2018-02-22 00:45:49 +01:00
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
# 3) Finds the minimum period without degrading the delays by X%
|
2018-02-26 17:54:35 +01:00
|
|
|
self.set_load_slew(max(loads),max(slews))
|
2018-02-22 20:14:58 +01:00
|
|
|
min_period = self.find_min_period(feasible_delay_lh, feasible_delay_hl)
|
2017-07-06 17:42:25 +02:00
|
|
|
debug.check(type(min_period)==float,"Couldn't find minimum period.")
|
2018-02-22 01:03:49 +01:00
|
|
|
debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay_lh, feasible_delay_hl))
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
# 4) Pack up the final measurements
|
2018-05-12 01:32:00 +02:00
|
|
|
char_data["min_period"] = round_time(min_period)
|
2018-02-21 22:38:43 +01:00
|
|
|
|
|
|
|
|
return char_data
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def obtain_cycle_times(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Returns a list of key time-points [ns] of the waveform (each rising edge)
|
|
|
|
|
of the cycles to do a timing evaluation. The last time is the end of the simulation
|
|
|
|
|
and does not need a rising edge."""
|
|
|
|
|
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments = []
|
2017-07-06 17:42:25 +02:00
|
|
|
self.cycle_times = []
|
2018-02-07 21:58:47 +01:00
|
|
|
t_current = 0
|
|
|
|
|
|
|
|
|
|
# idle cycle, no operation
|
|
|
|
|
msg = "Idle cycle (no clock)"
|
|
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
2018-02-07 21:58:47 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current += self.period
|
2018-02-07 21:58:47 +01:00
|
|
|
|
|
|
|
|
# One period
|
|
|
|
|
msg = "W data 1 address 11..11 to initialize cell"
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-07 21:58:47 +01:00
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
|
|
|
|
t_current += self.period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
# One period
|
|
|
|
|
msg = "W data 0 address 11..11 (to ensure a write of value works)"
|
2017-07-06 17:42:25 +02:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-07 21:58:47 +01:00
|
|
|
self.write0_cycle=len(self.cycle_times)-1
|
|
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
|
|
|
|
t_current += self.period
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
# One period
|
|
|
|
|
msg = "W data 1 address 00..00 (to clear bus caps)"
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-07 21:58:47 +01:00
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
|
|
|
|
t_current += self.period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
# One period
|
|
|
|
|
msg = "R data 0 address 11..11 to check W0 worked"
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-07 21:58:47 +01:00
|
|
|
self.read0_cycle=len(self.cycle_times)-1
|
|
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
|
|
|
|
t_current += self.period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
# One period
|
2018-02-26 23:39:06 +01:00
|
|
|
msg = "Idle cycle (Read addr 00..00)"
|
2018-02-07 21:58:47 +01:00
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-21 22:38:43 +01:00
|
|
|
self.idle_cycle=len(self.cycle_times)-1
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current += self.period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
# One period
|
|
|
|
|
msg = "W data 1 address 11..11 (to ensure a write of value worked)"
|
2017-07-06 17:42:25 +02:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-07 21:58:47 +01:00
|
|
|
self.write1_cycle=len(self.cycle_times)-1
|
|
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
|
|
|
|
t_current += self.period
|
2018-02-07 21:58:47 +01:00
|
|
|
|
|
|
|
|
# One period
|
|
|
|
|
msg = "W data 0 address 00..00 (to clear bus caps)"
|
|
|
|
|
self.cycle_times.append(t_current)
|
|
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
|
|
|
|
t_current += self.period
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
# One period
|
|
|
|
|
msg = "R data 1 address 11..11 to check W1 worked"
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-07 21:58:47 +01:00
|
|
|
self.read1_cycle=len(self.cycle_times)-1
|
|
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
|
|
|
|
t_current += self.period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
# One period
|
2018-02-26 23:39:06 +01:00
|
|
|
msg = "Idle cycle (Read addr 11..11)"
|
2018-02-07 21:58:47 +01:00
|
|
|
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current,
|
|
|
|
|
msg))
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-22 20:14:58 +01:00
|
|
|
t_current += self.period
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-02-07 21:58:47 +01:00
|
|
|
|
2018-02-21 22:38:43 +01:00
|
|
|
def analytical_delay(self,sram, slews, loads):
|
2017-11-09 20:13:44 +01:00
|
|
|
""" Just return the analytical model results for the SRAM.
|
|
|
|
|
"""
|
2018-02-22 01:03:49 +01:00
|
|
|
delay_lh = []
|
|
|
|
|
delay_hl = []
|
|
|
|
|
slew_lh = []
|
|
|
|
|
slew_hl = []
|
2017-11-09 20:13:44 +01:00
|
|
|
for slew in slews:
|
|
|
|
|
for load in loads:
|
2018-02-26 17:54:35 +01:00
|
|
|
self.set_load_slew(load,slew)
|
2018-02-22 20:14:58 +01:00
|
|
|
bank_delay = sram.analytical_delay(self.slew,self.load)
|
2018-02-28 21:18:41 +01:00
|
|
|
# Convert from ps to ns
|
2018-02-22 01:03:49 +01:00
|
|
|
delay_lh.append(bank_delay.delay/1e3)
|
|
|
|
|
delay_hl.append(bank_delay.delay/1e3)
|
|
|
|
|
slew_lh.append(bank_delay.slew/1e3)
|
|
|
|
|
slew_hl.append(bank_delay.slew/1e3)
|
2017-11-09 20:13:44 +01:00
|
|
|
|
2018-02-27 01:32:28 +01:00
|
|
|
power = sram.analytical_power(self.process, self.vdd_voltage, self.temperature, 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))
|
2018-02-27 22:02:22 +01:00
|
|
|
|
2017-11-09 20:13:44 +01:00
|
|
|
data = {"min_period": 0,
|
2018-02-22 01:03:49 +01:00
|
|
|
"delay_lh": delay_lh,
|
|
|
|
|
"delay_hl": delay_hl,
|
|
|
|
|
"slew_lh": slew_lh,
|
|
|
|
|
"slew_hl": slew_hl,
|
2018-02-27 01:32:28 +01:00
|
|
|
"read0_power": power.dynamic,
|
2018-02-27 22:02:22 +01:00
|
|
|
"read1_power": power.dynamic,
|
2018-02-27 01:32:28 +01:00
|
|
|
"write0_power": power.dynamic,
|
2018-02-27 22:02:22 +01:00
|
|
|
"write1_power": power.dynamic,
|
|
|
|
|
"leakage_power": power.leakage
|
2017-11-09 20:13:44 +01:00
|
|
|
}
|
|
|
|
|
return data
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def gen_data(self, clk_times, sig_name):
|
2018-02-07 23:04:18 +01:00
|
|
|
""" Generates the PWL data inputs for a simulation timing test. """
|
2018-02-07 21:58:47 +01:00
|
|
|
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
|
|
|
|
|
# we are asserting the opposite value on the other side of the tx gate during
|
|
|
|
|
# the read to be "worst case". Otherwise, it can actually assist the read.
|
|
|
|
|
values = [0, 1, 0, 1, 1, 1, 1, 0, 0, 0 ]
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.gen_pwl(sig_name, clk_times, values, self.period, self.slew, 0.05)
|
2018-02-07 21:58:47 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def gen_addr(self, clk_times, addr):
|
2018-02-07 23:04:18 +01:00
|
|
|
"""
|
|
|
|
|
Generates the address inputs for a simulation timing test.
|
|
|
|
|
This alternates between all 1's and all 0's for the address.
|
2018-02-07 21:58:47 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
zero_values = [0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ]
|
|
|
|
|
ones_values = [1, 1, 1, 0, 1, 0, 1, 0, 1, 1 ]
|
|
|
|
|
|
|
|
|
|
for i in range(len(addr)):
|
|
|
|
|
sig_name = "A[{0}]".format(i)
|
|
|
|
|
if addr[i]=="1":
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.gen_pwl(sig_name, clk_times, ones_values, self.period, self.slew, 0.05)
|
2018-02-07 21:58:47 +01:00
|
|
|
else:
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.gen_pwl(sig_name, clk_times, zero_values, self.period, self.slew, 0.05)
|
2018-02-07 21:58:47 +01:00
|
|
|
|
|
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def gen_csb(self, clk_times):
|
2018-02-07 23:04:18 +01:00
|
|
|
""" Generates the PWL CSb signal """
|
2018-02-07 21:58:47 +01:00
|
|
|
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
|
|
|
|
|
# Keep CSb asserted in NOP for measuring >1 period
|
|
|
|
|
values = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.gen_pwl("csb", clk_times, values, self.period, self.slew, 0.05)
|
2018-02-07 21:58:47 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def gen_web(self, clk_times):
|
2018-02-07 23:04:18 +01:00
|
|
|
""" Generates the PWL WEb signal """
|
2018-02-07 21:58:47 +01:00
|
|
|
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
|
|
|
|
|
# Keep WEb deasserted in NOP for measuring >1 period
|
|
|
|
|
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.gen_pwl("web", clk_times, values, self.period, self.slew, 0.05)
|
2018-02-07 21:58:47 +01:00
|
|
|
|
|
|
|
|
# Keep acc_en deasserted in NOP for measuring >1 period
|
|
|
|
|
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.gen_pwl("acc_en", clk_times, values, self.period, self.slew, 0)
|
2018-02-07 21:58:47 +01:00
|
|
|
values = [0, 1, 1, 1, 0, 0, 1, 1, 0, 0]
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.gen_pwl("acc_en_inv", clk_times, values, self.period, self.slew, 0)
|
2018-02-07 21:58:47 +01:00
|
|
|
|
2018-02-22 20:14:58 +01:00
|
|
|
def gen_oeb(self, clk_times):
|
2018-02-07 23:04:18 +01:00
|
|
|
""" Generates the PWL WEb signal """
|
2018-02-07 21:58:47 +01:00
|
|
|
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
|
|
|
|
|
# Keep OEb asserted in NOP for measuring >1 period
|
|
|
|
|
values = [1, 1, 1, 1, 0, 0, 1, 1, 0, 0]
|
2018-02-22 20:14:58 +01:00
|
|
|
self.stim.gen_pwl("oeb", clk_times, values, self.period, self.slew, 0.05)
|