2016-11-08 18:57:35 +01:00
|
|
|
import sys
|
|
|
|
|
import re
|
|
|
|
|
import debug
|
|
|
|
|
import tech
|
|
|
|
|
import math
|
|
|
|
|
import stimuli
|
|
|
|
|
import charutils as ch
|
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():
|
|
|
|
|
"""
|
|
|
|
|
Functions to measure the delay of the SRAM at a given address and
|
|
|
|
|
data bit.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self,sram,spfile):
|
|
|
|
|
self.name = sram.name
|
|
|
|
|
self.num_words = sram.num_words
|
|
|
|
|
self.word_size = sram.word_size
|
|
|
|
|
self.addr_size = sram.addr_size
|
|
|
|
|
self.sram_sp_file = spfile
|
2017-11-14 22:24:14 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
self.vdd = tech.spice["supply_voltage"]
|
|
|
|
|
self.gnd = tech.spice["gnd_voltage"]
|
2016-11-08 18:57:35 +01:00
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
def write_stimulus(self, period, load, slew):
|
2018-02-05 20:36:46 +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.
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
|
|
|
|
self.check_arguments()
|
|
|
|
|
|
|
|
|
|
# obtains list of time-points for each rising clk edge
|
2017-07-06 17:42:25 +02:00
|
|
|
self.obtain_cycle_times(period)
|
2016-11-08 18:57:35 +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-05 20:36:46 +01:00
|
|
|
self.sf.write("* Stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(period,load,slew))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# include files in stimulus file
|
|
|
|
|
model_list = tech.spice["fet_models"] + [self.sram_sp_file]
|
|
|
|
|
stimuli.write_include(stim_file=self.sf, models=model_list)
|
|
|
|
|
|
|
|
|
|
# add vdd/gnd statements
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.sf.write("* Global Power Supplies\n")
|
2017-07-06 17:42:25 +02:00
|
|
|
stimuli.write_supply(self.sf)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# instantiate the sram
|
|
|
|
|
self.sf.write("* Instantiation of the SRAM\n")
|
|
|
|
|
stimuli.inst_sram(stim_file=self.sf,
|
|
|
|
|
abits=self.addr_size,
|
|
|
|
|
dbits=self.word_size,
|
|
|
|
|
sram_name=self.name)
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
self.sf.write("* SRAM output loads\n")
|
|
|
|
|
for i in range(self.word_size):
|
2017-09-30 01:22:13 +02:00
|
|
|
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,load))
|
2017-07-06 17:42:25 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
# add access transistors for data-bus
|
2017-07-06 17:42:25 +02:00
|
|
|
self.sf.write("* Transmission Gates for data-bus and control signals\n")
|
|
|
|
|
stimuli.inst_accesstx(stim_file=self.sf, dbits=self.word_size)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# generate data and addr signals
|
2017-07-06 17:42:25 +02:00
|
|
|
self.sf.write("* 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:
|
2017-07-06 17:42:25 +02:00
|
|
|
stimuli.gen_data(stim_file=self.sf,
|
|
|
|
|
clk_times=self.cycle_times,
|
2017-09-30 01:22:13 +02:00
|
|
|
sig_name="data[{0}]".format(i),
|
2017-07-06 17:42:25 +02:00
|
|
|
period=period,
|
|
|
|
|
slew=slew)
|
2016-11-08 18:57:35 +01:00
|
|
|
else:
|
|
|
|
|
stimuli.gen_constant(stim_file=self.sf,
|
2017-09-30 01:22:13 +02:00
|
|
|
sig_name="d[{0}]".format(i),
|
2017-07-06 17:42:25 +02:00
|
|
|
v_val=self.gnd)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
stimuli.gen_addr(self.sf,
|
|
|
|
|
clk_times=self.cycle_times,
|
|
|
|
|
addr=self.probe_address,
|
|
|
|
|
period=period,
|
|
|
|
|
slew=slew)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# generate control signals
|
|
|
|
|
self.sf.write("* Generation of control signals\n")
|
|
|
|
|
stimuli.gen_csb(self.sf, self.cycle_times, period, slew)
|
|
|
|
|
stimuli.gen_web(self.sf, self.cycle_times, period, slew)
|
|
|
|
|
stimuli.gen_oeb(self.sf, self.cycle_times, period, slew)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
self.sf.write("* Generation of global clock signal\n")
|
2017-07-06 17:42:25 +02:00
|
|
|
stimuli.gen_pulse(stim_file=self.sf,
|
|
|
|
|
sig_name="CLK",
|
|
|
|
|
v1=self.gnd,
|
|
|
|
|
v2=self.vdd,
|
|
|
|
|
offset=period,
|
|
|
|
|
period=period,
|
|
|
|
|
t_rise = slew,
|
|
|
|
|
t_fall = slew)
|
|
|
|
|
|
|
|
|
|
self.write_measures(period)
|
|
|
|
|
|
|
|
|
|
# run until the last cycle time
|
|
|
|
|
stimuli.write_control(self.sf,self.cycle_times[-1])
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
self.sf.close()
|
|
|
|
|
|
|
|
|
|
def write_measures(self,period):
|
2016-11-08 18:57:35 +01:00
|
|
|
# meas statement for delay and power measurements
|
|
|
|
|
self.sf.write("* Measure statements for delay and power\n")
|
|
|
|
|
|
2018-02-03 04:33:07 +01:00
|
|
|
for comment in self.cycle_comments:
|
|
|
|
|
self.sf.write("* {}\n".format(comment))
|
|
|
|
|
|
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))
|
2017-07-06 17:42:25 +02:00
|
|
|
trig_val = targ_val = 0.5 * self.vdd
|
|
|
|
|
# add measure statments for delay0
|
2018-02-03 04:33:07 +01:00
|
|
|
# delay the target to measure after the negative edge
|
2016-11-08 18:57:35 +01:00
|
|
|
stimuli.gen_meas_delay(stim_file=self.sf,
|
2017-07-06 17:42:25 +02:00
|
|
|
meas_name="DELAY0",
|
2016-11-08 18:57:35 +01:00
|
|
|
trig_name=trig_name,
|
|
|
|
|
targ_name=targ_name,
|
|
|
|
|
trig_val=trig_val,
|
|
|
|
|
targ_val=targ_val,
|
2017-07-06 17:42:25 +02:00
|
|
|
trig_dir="FALL",
|
|
|
|
|
targ_dir="FALL",
|
2018-02-05 20:36:46 +01:00
|
|
|
trig_td=self.cycle_times[self.read0_cycle],
|
|
|
|
|
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
stimuli.gen_meas_delay(stim_file=self.sf,
|
|
|
|
|
meas_name="DELAY1",
|
|
|
|
|
trig_name=trig_name,
|
|
|
|
|
targ_name=targ_name,
|
|
|
|
|
trig_val=trig_val,
|
|
|
|
|
targ_val=targ_val,
|
|
|
|
|
trig_dir="FALL",
|
|
|
|
|
targ_dir="RISE",
|
2018-02-05 20:36:46 +01:00
|
|
|
trig_td=self.cycle_times[self.read1_cycle],
|
|
|
|
|
targ_td=self.cycle_times[self.read1_cycle]+0.5*period)
|
2017-07-06 17:42:25 +02:00
|
|
|
|
|
|
|
|
stimuli.gen_meas_delay(stim_file=self.sf,
|
|
|
|
|
meas_name="SLEW0",
|
|
|
|
|
trig_name=targ_name,
|
|
|
|
|
targ_name=targ_name,
|
2018-02-05 20:36:46 +01:00
|
|
|
trig_val=0.8*self.vdd,
|
|
|
|
|
targ_val=0.2*self.vdd,
|
2017-07-06 17:42:25 +02:00
|
|
|
trig_dir="FALL",
|
|
|
|
|
targ_dir="FALL",
|
2018-02-05 20:36:46 +01:00
|
|
|
trig_td=self.cycle_times[self.read0_cycle],
|
|
|
|
|
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
|
2017-07-06 17:42:25 +02:00
|
|
|
|
|
|
|
|
stimuli.gen_meas_delay(stim_file=self.sf,
|
|
|
|
|
meas_name="SLEW1",
|
|
|
|
|
trig_name=targ_name,
|
|
|
|
|
targ_name=targ_name,
|
2018-02-05 20:36:46 +01:00
|
|
|
trig_val=0.2*self.vdd,
|
|
|
|
|
targ_val=0.8*self.vdd,
|
2017-07-06 17:42:25 +02:00
|
|
|
trig_dir="RISE",
|
|
|
|
|
targ_dir="RISE",
|
2018-02-05 20:36:46 +01:00
|
|
|
trig_td=self.cycle_times[self.read1_cycle],
|
|
|
|
|
targ_td=self.cycle_times[self.read1_cycle]+0.5*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]
|
2016-11-08 18:57:35 +01:00
|
|
|
stimuli.gen_meas_power(stim_file=self.sf,
|
2017-07-06 17:42:25 +02:00
|
|
|
meas_name="WRITE0_POWER",
|
2016-11-08 18:57:35 +01:00
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
|
|
|
|
|
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]
|
2016-11-08 18:57:35 +01:00
|
|
|
stimuli.gen_meas_power(stim_file=self.sf,
|
2017-07-06 17:42:25 +02:00
|
|
|
meas_name="WRITE1_POWER",
|
|
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
|
|
|
|
|
|
|
|
|
t_initial = self.cycle_times[self.read0_cycle]
|
|
|
|
|
t_final = self.cycle_times[self.read0_cycle+1]
|
|
|
|
|
stimuli.gen_meas_power(stim_file=self.sf,
|
|
|
|
|
meas_name="READ0_POWER",
|
2016-11-08 18:57:35 +01:00
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
|
|
|
|
|
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]
|
|
|
|
|
stimuli.gen_meas_power(stim_file=self.sf,
|
|
|
|
|
meas_name="READ1_POWER",
|
|
|
|
|
t_initial=t_initial,
|
|
|
|
|
t_final=t_final)
|
|
|
|
|
|
|
|
|
|
def find_feasible_period(self, load, slew):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""Uses an initial period and finds a feasible period before we
|
|
|
|
|
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
|
|
|
|
|
starting point. """
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
feasible_period = 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)
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
(success, feasible_delay1, feasible_slew1, feasible_delay0, feasible_slew0)=self.run_simulation(feasible_period,load,slew)
|
2016-11-08 18:57:35 +01:00
|
|
|
if not success:
|
|
|
|
|
feasible_period = 2 * feasible_period
|
|
|
|
|
continue
|
|
|
|
|
|
2017-08-07 19:24:45 +02:00
|
|
|
debug.info(1, "Found feasible_period: {0}ns feasible_delay1/0 {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period,feasible_delay1,feasible_delay0,feasible_slew1,feasible_slew0))
|
2017-07-06 17:42:25 +02:00
|
|
|
return (feasible_period, feasible_delay1, feasible_delay0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
def run_simulation(self, period, load, slew):
|
2016-11-08 18:57:35 +01:00
|
|
|
""" This tries to simulate a period and checks if the result
|
2017-07-06 17:42:25 +02:00
|
|
|
works. If so, it returns True and the delays and slews."""
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# Checking from not data_value to data_value
|
2017-07-06 17:42:25 +02:00
|
|
|
self.write_stimulus(period, load, slew)
|
2016-11-08 18:57:35 +01:00
|
|
|
stimuli.run_sim()
|
2017-07-06 17:42:25 +02:00
|
|
|
delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0"))
|
|
|
|
|
delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1"))
|
|
|
|
|
slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0"))
|
|
|
|
|
slew1 = ch.convert_to_float(ch.parse_output("timing", "slew1"))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
# if it failed or the read was longer than a period
|
2017-07-06 17:42:25 +02:00
|
|
|
if type(delay0)!=float or type(delay1)!=float or type(slew1)!=float or type(slew0)!=float:
|
2018-02-03 04:33:07 +01:00
|
|
|
debug.info(2,"Failed simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,load,slew,delay0,delay1,slew0,slew1))
|
2017-07-06 17:42:25 +02:00
|
|
|
return (False,0,0,0,0)
|
2017-08-07 19:24:45 +02:00
|
|
|
delay0 *= 1e9
|
|
|
|
|
delay1 *= 1e9
|
|
|
|
|
slew0 *= 1e9
|
|
|
|
|
slew1 *= 1e9
|
|
|
|
|
if delay0>period or delay1>period or slew0>period or slew1>period:
|
2018-02-03 04:33:07 +01:00
|
|
|
debug.info(2,"UNsuccessful simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,load,slew,delay0,delay1,slew0,slew1))
|
2017-08-07 19:24:45 +02:00
|
|
|
return (False,0,0,0,0)
|
2016-11-08 18:57:35 +01:00
|
|
|
else:
|
2017-08-07 19:24:45 +02:00
|
|
|
debug.info(2,"Successful simulation: period {0} load {1} slew {2}, delay0={3}n delay1={4}ns slew0={5}n slew1={6}n".format(period,load,slew,delay0,delay1,slew0,slew1))
|
2016-11-08 18:57:35 +01:00
|
|
|
#key=raw_input("press return to continue")
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# The delay is from the negative edge for our SRAM
|
|
|
|
|
return (True,delay1,slew1,delay0,slew0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
def find_min_period(self,feasible_period, load, slew, feasible_delay1, feasible_delay0):
|
|
|
|
|
"""Searches for the smallest period with output delays being within 5% of
|
|
|
|
|
long period. """
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
previous_period = ub_period = feasible_period
|
|
|
|
|
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)
|
|
|
|
|
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
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
if self.try_period(target_period, load, slew, feasible_delay1, feasible_delay0):
|
2016-11-08 18:57:35 +01:00
|
|
|
ub_period = target_period
|
|
|
|
|
else:
|
|
|
|
|
lb_period = target_period
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
if ch.relative_compare(ub_period, lb_period, error_tolerance=0.05):
|
|
|
|
|
# ub_period is always feasible
|
|
|
|
|
return ub_period
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
def try_period(self, period, load, slew, feasible_delay1, feasible_delay0):
|
|
|
|
|
""" 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
|
|
|
|
|
self.write_stimulus(period,load,slew)
|
|
|
|
|
stimuli.run_sim()
|
|
|
|
|
delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0"))
|
|
|
|
|
delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1"))
|
2017-08-07 19:24:45 +02:00
|
|
|
slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0"))
|
|
|
|
|
slew1 = ch.convert_to_float(ch.parse_output("timing", "slew1"))
|
2017-07-06 17:42:25 +02:00
|
|
|
# if it failed or the read was longer than a period
|
2017-08-07 19:24:45 +02:00
|
|
|
if type(delay0)!=float or type(delay1)!=float or type(slew1)!=float or type(slew0)!=float:
|
|
|
|
|
debug.info(2,"Invalid measures: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1))
|
|
|
|
|
return False
|
|
|
|
|
delay0 *= 1e9
|
|
|
|
|
delay1 *= 1e9
|
|
|
|
|
slew0 *= 1e9
|
|
|
|
|
slew1 *= 1e9
|
|
|
|
|
if delay0>period or delay1>period or slew0>period or slew1>period:
|
|
|
|
|
debug.info(2,"Too long delay/slew: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1))
|
2017-07-06 17:42:25 +02:00
|
|
|
return False
|
|
|
|
|
else:
|
2017-08-07 19:24:45 +02:00
|
|
|
if not ch.relative_compare(delay1,feasible_delay1,error_tolerance=0.05):
|
|
|
|
|
debug.info(2,"Delay too big {0} vs {1}".format(delay1,feasible_delay1))
|
2017-07-06 17:42:25 +02:00
|
|
|
return False
|
2017-08-07 19:24:45 +02:00
|
|
|
elif not ch.relative_compare(delay0,feasible_delay0,error_tolerance=0.05):
|
|
|
|
|
debug.info(2,"Delay too big {0} vs {1}".format(delay0,feasible_delay0))
|
2017-07-06 17:42:25 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#key=raw_input("press return to continue")
|
|
|
|
|
|
2017-08-07 19:24:45 +02:00
|
|
|
debug.info(2,"Successful period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1))
|
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
|
|
|
|
|
|
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):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""main function to calculate the min period for a low_to_high
|
|
|
|
|
transistion and a high_to_low transistion returns a dictionary
|
|
|
|
|
that contains all both the min period and associated delays
|
|
|
|
|
Dictionary Keys: min_period1, delay1, min_period0, delay0
|
|
|
|
|
"""
|
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
|
|
|
|
|
# feasible_delay1=0.059083183
|
|
|
|
|
# feasible_delay0=0.17953789
|
|
|
|
|
# load=1.6728
|
|
|
|
|
# slew=0.04
|
|
|
|
|
# self.try_period(target_period, load, slew, feasible_delay1, feasible_delay0)
|
|
|
|
|
# sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
2017-08-07 19:24:45 +02:00
|
|
|
(feasible_period, feasible_delay1, feasible_delay0) = self.find_feasible_period(max(loads), max(slews))
|
2017-07-06 17:42:25 +02:00
|
|
|
debug.check(feasible_delay1>0,"Negative delay may not be possible")
|
|
|
|
|
debug.check(feasible_delay0>0,"Negative delay may not be possible")
|
|
|
|
|
|
|
|
|
|
# The power variables are just scalars. These use the final feasible period simulation
|
|
|
|
|
# which should have worked.
|
|
|
|
|
read0_power=ch.convert_to_float(ch.parse_output("timing", "read0_power"))
|
|
|
|
|
write0_power=ch.convert_to_float(ch.parse_output("timing", "write0_power"))
|
|
|
|
|
read1_power=ch.convert_to_float(ch.parse_output("timing", "read1_power"))
|
|
|
|
|
write1_power=ch.convert_to_float(ch.parse_output("timing", "write1_power"))
|
|
|
|
|
|
|
|
|
|
LH_delay = []
|
|
|
|
|
HL_delay = []
|
|
|
|
|
LH_slew = []
|
|
|
|
|
HL_slew = []
|
|
|
|
|
for slew in slews:
|
|
|
|
|
for load in loads:
|
|
|
|
|
(success, delay1, slew1, delay0, slew0) = self.run_simulation(feasible_period, load, slew)
|
2018-02-01 00:38:02 +01:00
|
|
|
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(slew,load))
|
2017-07-06 17:42:25 +02:00
|
|
|
LH_delay.append(delay1)
|
|
|
|
|
HL_delay.append(delay0)
|
|
|
|
|
LH_slew.append(slew1)
|
|
|
|
|
HL_slew.append(slew0)
|
|
|
|
|
|
|
|
|
|
# finds the minimum period without degrading the delays by X%
|
2017-08-07 19:24:45 +02:00
|
|
|
min_period = self.find_min_period(feasible_period, max(loads), max(slews), feasible_delay1, feasible_delay0)
|
2017-07-06 17:42:25 +02:00
|
|
|
debug.check(type(min_period)==float,"Couldn't find minimum period.")
|
2017-11-23 00:57:29 +01:00
|
|
|
debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay1, feasible_delay0))
|
2017-07-06 17:42:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
data = {"min_period": ch.round_time(min_period),
|
|
|
|
|
"delay1": LH_delay,
|
|
|
|
|
"delay0": HL_delay,
|
|
|
|
|
"slew1": LH_slew,
|
|
|
|
|
"slew0": HL_slew,
|
|
|
|
|
"read0_power": read0_power*1e3,
|
|
|
|
|
"read1_power": read1_power*1e3,
|
|
|
|
|
"write0_power": write0_power*1e3,
|
|
|
|
|
"write1_power": write1_power*1e3
|
2016-11-08 18:57:35 +01:00
|
|
|
}
|
2017-04-24 20:55:11 +02:00
|
|
|
return data
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
def obtain_cycle_times(self, period):
|
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
|
|
|
# idle cycle, no operation
|
|
|
|
|
t_current = period
|
|
|
|
|
self.cycle_times = []
|
|
|
|
|
# cycle0: W data 1 address 1111 to initialize cell to a value
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments.append("Cycle0 {}ns: W data 1 address 111 to initialize cell".format(t_current))
|
2017-07-06 17:42:25 +02:00
|
|
|
t_current += period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# cycle1: W data 0 address 1111 (to ensure a write of value works)
|
|
|
|
|
self.cycle_times.append(t_current)
|
|
|
|
|
self.write0_cycle=1
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments.append("Cycle1 {}ns: W data 0 address 111 (to ensure a write of value works)".format(t_current))
|
2017-07-06 17:42:25 +02:00
|
|
|
t_current += period
|
|
|
|
|
|
|
|
|
|
# cycle2: W data 1 address 0000 (to clear the data bus cap)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments.append("Cycle2 {}ns: W data 1 address 0000 (to clear bus caps)".format(t_current))
|
2017-07-06 17:42:25 +02:00
|
|
|
t_current += period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# cycle3: R data 0 address 1111 to check W0 works
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2017-07-06 17:42:25 +02:00
|
|
|
self.read0_cycle=3
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments.append("Cycle3 {}ns: R data 0 address 1111 to check W0 worked".format(t_current))
|
2017-07-06 17:42:25 +02:00
|
|
|
t_current += period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# cycle4: W data 1 address 1111 (to ensure a write of value works)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2017-07-06 17:42:25 +02:00
|
|
|
self.write1_cycle=4
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments.append("Cycle4 {}ns: W data 1 address 1111 (to ensure a write of value worked)".format(t_current))
|
2017-07-06 17:42:25 +02:00
|
|
|
t_current += period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# cycle5: W data 0 address 0000 (to clear the data bus cap)
|
|
|
|
|
self.cycle_times.append(t_current)
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments.append("Cycle5 {}ns: W data 0 address 0000 (to clear bus caps)".format(t_current))
|
2017-07-06 17:42:25 +02:00
|
|
|
t_current += period
|
|
|
|
|
|
|
|
|
|
# cycle6: R data 1 address 1111 to check W1 works
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2017-07-06 17:42:25 +02:00
|
|
|
self.read1_cycle=6
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments.append("Cycle6 {}ns: R data 1 address 1111 to check W1 worked".format(t_current))
|
2017-07-06 17:42:25 +02:00
|
|
|
t_current += period
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-07-06 17:42:25 +02:00
|
|
|
# cycle7: wait a clock period to end the simulation
|
2016-11-08 18:57:35 +01:00
|
|
|
self.cycle_times.append(t_current)
|
2018-02-03 04:33:07 +01:00
|
|
|
self.cycle_comments.append("Cycle7 {}ns: Idle period to end simulation".format(t_current))
|
2017-07-06 17:42:25 +02:00
|
|
|
t_current += period
|
|
|
|
|
|
2018-02-03 04:33:07 +01:00
|
|
|
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-11-09 20:13:44 +01:00
|
|
|
def analytical_model(self,sram, slews, loads):
|
|
|
|
|
""" Just return the analytical model results for the SRAM.
|
|
|
|
|
"""
|
|
|
|
|
LH_delay = []
|
|
|
|
|
HL_delay = []
|
|
|
|
|
LH_slew = []
|
|
|
|
|
HL_slew = []
|
|
|
|
|
for slew in slews:
|
|
|
|
|
for load in loads:
|
|
|
|
|
bank_delay = sram.analytical_delay(slew,load)
|
|
|
|
|
# Convert from ps to ns
|
|
|
|
|
LH_delay.append(bank_delay.delay/1e3)
|
|
|
|
|
HL_delay.append(bank_delay.delay/1e3)
|
|
|
|
|
LH_slew.append(bank_delay.slew/1e3)
|
|
|
|
|
HL_slew.append(bank_delay.slew/1e3)
|
|
|
|
|
|
|
|
|
|
data = {"min_period": 0,
|
|
|
|
|
"delay1": LH_delay,
|
|
|
|
|
"delay0": HL_delay,
|
|
|
|
|
"slew1": LH_slew,
|
|
|
|
|
"slew0": HL_slew,
|
|
|
|
|
"read0_power": 0,
|
|
|
|
|
"read1_power": 0,
|
|
|
|
|
"write0_power": 0,
|
|
|
|
|
"write1_power": 0
|
|
|
|
|
}
|
|
|
|
|
return data
|
|
|
|
|
|