Fixed conflict in delay.py

This commit is contained in:
Hunter Nichols
2018-02-27 13:02:22 -08:00
185 changed files with 4308533 additions and 915 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ def parse_output(filename, key):
if val != None:
debug.info(4, "Key = " + key + " Val = " + val.group(1))
return val.group(1)
return convert_to_float(val.group(1))
else:
return "Failed"
+423 -253
View File
@@ -1,34 +1,54 @@
import sys
import re
import sys,re,shutil
import debug
import tech
import math
import stimuli
from trim_spice import trim_spice
import charutils as ch
import utils
from globals import OPTS
class delay():
"""
Functions to measure the delay of an SRAM at a given address and
"""Functions to measure the delay and power of an SRAM at a given address and
data bit.
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.
"""
def __init__(self,sram,spfile, corner):
def __init__(self, sram, spfile, corner):
self.sram = sram
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
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
# These are the member variables for a simulation
self.period = 0
self.set_load_slew(0,0)
self.set_corner(corner)
def set_corner(self,corner):
""" Set the corner values """
self.corner = corner
(self.process, self.vdd_voltage, self.temperature) = corner
self.gnd_voltage = 0
def set_load_slew(self,load,slew):
""" Set the load and slew """
self.load = load
self.slew = slew
def check_arguments(self):
"""Checks if arguments given for write_stimulus() meets requirements"""
@@ -43,27 +63,10 @@ class delay():
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)
def write_stimulus(self, period, load, slew):
""" 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
self.obtain_cycle_times(period)
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.sf = open(temp_stim, "w")
self.sf.write("* Stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(period,load,slew))
self.stim = stimuli.stimuli(self.sf, self.corner)
# include files in stimulus file
self.stim.write_include(self.sram_sp_file)
def write_generic_stimulus(self):
""" Create the instance, supplies, loads, and access transistors. """
# add vdd/gnd statements
self.sf.write("\n* Global Power Supplies\n")
self.stim.write_supply()
@@ -75,52 +78,123 @@ class delay():
self.sf.write("\n* SRAM output loads\n")
for i in range(self.word_size):
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,load))
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,self.load))
# add access transistors for data-bus
self.sf.write("\n* Transmission Gates for data-bus and control signals\n")
self.stim.inst_accesstx(dbits=self.word_size)
def write_delay_stimulus(self):
""" 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
self.obtain_cycle_times()
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.sf = open(temp_stim, "w")
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))
self.stim = stimuli.stimuli(self.sf, self.corner)
# include files in stimulus file
self.stim.write_include(self.trim_sp_file)
self.write_generic_stimulus()
# generate data and addr signals
self.sf.write("\n* Generation of data and address signals\n")
for i in range(self.word_size):
if i == self.probe_data:
self.gen_data(clk_times=self.cycle_times,
sig_name="data[{0}]".format(i),
period=period,
slew=slew)
sig_name="data[{0}]".format(i))
else:
self.stim.gen_constant(sig_name="d[{0}]".format(i),
v_val=self.gnd_voltage)
v_val=0)
self.gen_addr(clk_times=self.cycle_times,
addr=self.probe_address,
period=period,
slew=slew)
addr=self.probe_address)
# generate control signals
self.sf.write("\n* Generation of control signals\n")
self.gen_csb(self.cycle_times, period, slew)
self.gen_web(self.cycle_times, period, slew)
self.gen_oeb(self.cycle_times, period, slew)
self.gen_csb(self.cycle_times)
self.gen_web(self.cycle_times)
self.gen_oeb(self.cycle_times)
self.sf.write("\n* Generation of global clock signal\n")
self.stim.gen_pulse(sig_name="CLK",
v1=self.gnd_voltage,
v1=0,
v2=self.vdd_voltage,
offset=period,
period=period,
t_rise=slew,
t_fall=slew)
offset=self.period,
period=self.period,
t_rise=self.slew,
t_fall=self.slew)
self.write_measures(period)
self.write_delay_measures()
# run until the end of the cycle time
self.stim.write_control(self.cycle_times[-1] + period)
self.stim.write_control(self.cycle_times[-1] + self.period)
self.sf.close()
def write_measures(self,period):
def write_power_stimulus(self, trim):
""" 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
self.obtain_cycle_times()
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.sf = open(temp_stim, "w")
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
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)
self.write_generic_stimulus()
# 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)
self.write_power_measures()
# run until the end of the cycle time
self.stim.write_control(2*self.period)
self.sf.close()
def write_delay_measures(self):
"""
Write the measure statements to quantify the delay and power results.
"""
@@ -138,7 +212,7 @@ class delay():
trig_val = targ_val = 0.5 * self.vdd_voltage
# Delay the target to measure after the negative edge
self.stim.gen_meas_delay(meas_name="DELAY0",
self.stim.gen_meas_delay(meas_name="DELAY_HL",
trig_name=trig_name,
targ_name=targ_name,
trig_val=trig_val,
@@ -146,9 +220,9 @@ class delay():
trig_dir="FALL",
targ_dir="FALL",
trig_td=self.cycle_times[self.read0_cycle],
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
targ_td=self.cycle_times[self.read0_cycle]+0.5*self.period)
self.stim.gen_meas_delay(meas_name="DELAY1",
self.stim.gen_meas_delay(meas_name="DELAY_LH",
trig_name=trig_name,
targ_name=targ_name,
trig_val=trig_val,
@@ -156,9 +230,9 @@ class delay():
trig_dir="FALL",
targ_dir="RISE",
trig_td=self.cycle_times[self.read1_cycle],
targ_td=self.cycle_times[self.read1_cycle]+0.5*period)
targ_td=self.cycle_times[self.read1_cycle]+0.5*self.period)
self.stim.gen_meas_delay(meas_name="SLEW0",
self.stim.gen_meas_delay(meas_name="SLEW_HL",
trig_name=targ_name,
targ_name=targ_name,
trig_val=0.9*self.vdd_voltage,
@@ -166,9 +240,9 @@ class delay():
trig_dir="FALL",
targ_dir="FALL",
trig_td=self.cycle_times[self.read0_cycle],
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
targ_td=self.cycle_times[self.read0_cycle]+0.5*self.period)
self.stim.gen_meas_delay(meas_name="SLEW1",
self.stim.gen_meas_delay(meas_name="SLEW_LH",
trig_name=targ_name,
targ_name=targ_name,
trig_val=0.1*self.vdd_voltage,
@@ -176,7 +250,7 @@ class delay():
trig_dir="RISE",
targ_dir="RISE",
trig_td=self.cycle_times[self.read1_cycle],
targ_td=self.cycle_times[self.read1_cycle]+0.5*period)
targ_td=self.cycle_times[self.read1_cycle]+0.5*self.period)
# add measure statements for power
t_initial = self.cycle_times[self.write0_cycle]
@@ -202,8 +276,23 @@ class delay():
self.stim.gen_meas_power(meas_name="READ1_POWER",
t_initial=t_initial,
t_final=t_final)
def find_feasible_period(self, load, slew):
def write_power_measures(self):
"""
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
t_initial = self.period
t_final = 2*self.period
self.stim.gen_meas_power(meas_name="leakage_power",
t_initial=t_initial,
t_final=t_final)
def find_feasible_period(self):
"""
Uses an initial period and finds a feasible period before we
run the binary search algorithm to find min period. We check if
@@ -212,7 +301,7 @@ class delay():
starting point.
"""
feasible_period = tech.spice["feasible_period"]
feasible_period = float(tech.spice["feasible_period"])
time_out = 8
while True:
debug.info(1, "Trying feasible period: {0}ns".format(feasible_period))
@@ -220,81 +309,134 @@ class delay():
if (time_out <= 0):
debug.error("Timed out, could not find a feasible period.",2)
(success, feasible_delay1, feasible_slew1, feasible_delay0, feasible_slew0)=self.run_simulation(feasible_period,load,slew)
self.period = feasible_period
(success, results)=self.run_delay_simulation()
if not success:
feasible_period = 2 * feasible_period
continue
feasible_delay_lh = results["delay_lh"]
feasible_slew_lh = results["slew_lh"]
feasible_delay_hl = results["delay_hl"]
feasible_slew_hl = results["slew_hl"]
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))
return (feasible_period, feasible_delay1, feasible_delay0)
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))
self.period = feasible_period
return (feasible_delay_lh, feasible_delay_hl)
def run_simulation(self, period, load, slew):
"""
This tries to simulate a period and checks if the result
works. If so, it returns True and the delays and slews.
def run_delay_simulation(self):
"""
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.
"""
# Checking from not data_value to data_value
self.write_stimulus(period, load, slew)
self.write_delay_stimulus()
self.stim.run_sim()
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"))
# if it failed or the read was longer than a period
if type(delay0)!=float or type(delay1)!=float or type(slew1)!=float or type(slew0)!=float:
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))
return (False,0,0,0,0)
# Scale delays to ns (they previously could have not been floats)
delay0 *= 1e9
delay1 *= 1e9
slew0 *= 1e9
slew1 *= 1e9
if delay0>period or delay1>period or slew0>period or slew1>period:
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))
return (False,0,0,0,0)
else:
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))
delay_hl = ch.parse_output("timing", "delay_hl")
delay_lh = ch.parse_output("timing", "delay_lh")
slew_hl = ch.parse_output("timing", "slew_hl")
slew_lh = ch.parse_output("timing", "slew_lh")
delays = (delay_hl, delay_lh, slew_hl, slew_lh)
read0_power=ch.parse_output("timing", "read0_power")
write0_power=ch.parse_output("timing", "write0_power")
read1_power=ch.parse_output("timing", "read1_power")
write1_power=ch.parse_output("timing", "write1_power")
if not self.check_valid_delays(delays):
return (False,{})
# For debug, you sometimes want to inspect each simulation.
#key=raw_input("press return to continue")
# Scale results to ns and mw, respectively
result = { "delay_hl" : delay_hl*1e9,
"delay_lh" : delay_lh*1e9,
"slew_hl" : slew_hl*1e9,
"slew_lh" : slew_lh*1e9,
"read0_power" : read0_power*1e3,
"read1_power" : read1_power*1e3,
"write0_power" : write0_power*1e3,
"write1_power" : write1_power*1e3}
# The delay is from the negative edge for our SRAM
return (True,delay1,slew1,delay0,slew0)
return (True,result)
def run_power_simulation(self):
"""
This simulates a disabled SRAM to get the leakage power when it is off.
"""
def find_min_period(self,feasible_period, load, slew, feasible_delay1, feasible_delay0):
self.write_power_stimulus(trim=False)
self.stim.run_sim()
leakage_power=ch.parse_output("timing", "leakage_power")
debug.check(leakage_power!="Failed","Could not measure leakage power.")
self.write_power_stimulus(trim=True)
self.stim.run_sim()
trim_leakage_power=ch.parse_output("timing", "leakage_power")
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)
def check_valid_delays(self, (delay_hl, delay_lh, slew_hl, slew_lh)):
""" Check if the measurements are defined and if they are valid. """
# if it failed or the read was longer than a period
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
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))
return False
# Scale delays to ns (they previously could have not been floats)
delay_hl *= 1e9
delay_lh *= 1e9
slew_hl *= 1e9
slew_lh *= 1e9
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))
return False
else:
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))
return True
def find_min_period(self, feasible_delay_lh, feasible_delay_hl):
"""
Searches for the smallest period with output delays being within 5% of
long period.
"""
previous_period = ub_period = feasible_period
previous_period = ub_period = self.period
lb_period = 0.0
# Binary search algorithm to find the min period (max frequency) of design
@@ -305,11 +447,12 @@ class delay():
debug.error("Timed out, could not converge on minimum period.",2)
target_period = 0.5 * (ub_period + lb_period)
self.period = target_period
debug.info(1, "MinPeriod Search: {0}ns (ub: {1} lb: {2})".format(target_period,
ub_period,
lb_period))
if self.try_period(target_period, load, slew, feasible_delay1, feasible_delay0):
if self.try_period(feasible_delay_lh, feasible_delay_hl):
ub_period = target_period
else:
lb_period = target_period
@@ -319,54 +462,54 @@ class delay():
return ub_period
def try_period(self, period, load, slew, feasible_delay1, feasible_delay0):
def try_period(self, feasible_delay_lh, feasible_delay_hl):
"""
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.
"""
# Checking from not data_value to data_value
self.write_stimulus(period,load,slew)
self.write_delay_stimulus()
self.stim.run_sim()
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"))
delay_hl = ch.parse_output("timing", "delay_hl")
delay_lh = ch.parse_output("timing", "delay_lh")
slew_hl = ch.parse_output("timing", "slew_hl")
slew_lh = ch.parse_output("timing", "slew_lh")
# if it failed or the read was longer than a period
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))
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
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))
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))
delay_hl *= 1e9
delay_lh *= 1e9
slew_hl *= 1e9
slew_lh *= 1e9
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))
return False
else:
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))
if not ch.relative_compare(delay_lh,feasible_delay_lh,error_tolerance=0.05):
debug.info(2,"Delay too big {0} vs {1}".format(delay_lh,feasible_delay_lh))
return False
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))
elif not ch.relative_compare(delay_hl,feasible_delay_hl,error_tolerance=0.05):
debug.info(2,"Delay too big {0} vs {1}".format(delay_hl,feasible_delay_hl))
return False
#key=raw_input("press return to continue")
debug.info(2,"Successful period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period,
delay0,
delay1,
slew0,
slew1))
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))
return True
def set_probe(self,probe_address, probe_data):
@@ -375,12 +518,35 @@ class delay():
self.probe_address = probe_address
self.probe_data = probe_data
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)
def analyze(self,probe_address, probe_data, slews, loads):
"""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
"""
Main function to characterize an SRAM for a table. Computes both delay and power characterization.
"""
self.set_probe(probe_address, probe_data)
@@ -388,58 +554,62 @@ class delay():
# 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
# feasible_delay_lh=0.059083183
# feasible_delay_hl=0.17953789
# load=1.6728
# slew=0.04
# self.try_period(target_period, load, slew, feasible_delay1, feasible_delay0)
# self.try_period(target_period, feasible_delay_lh, feasible_delay_hl)
# sys.exit(1)
(feasible_period, feasible_delay1, feasible_delay0) = self.find_feasible_period(max(loads), max(slews))
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"))
# 1) Find a feasible period and it's corresponding delays using the trimmed array.
self.load=max(loads)
self.slew=max(slews)
(feasible_delay_lh, feasible_delay_hl) = self.find_feasible_period()
debug.check(feasible_delay_lh>0,"Negative delay may not be possible")
debug.check(feasible_delay_hl>0,"Negative delay may not be possible")
LH_delay = []
HL_delay = []
LH_slew = []
HL_slew = []
# 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 = {}
for m in ["delay_lh", "delay_hl", "slew_lh", "slew_hl", "read0_power",
"read1_power", "write0_power", "write1_power", "leakage_power"]:
char_data[m]=[]
# 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
for slew in slews:
for load in loads:
(success, delay1, slew1, delay0, slew0) = self.run_simulation(feasible_period, load, slew)
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(slew,load))
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%
min_period = self.find_min_period(feasible_period, max(loads), max(slews), feasible_delay1, feasible_delay0)
self.set_load_slew(load,slew)
# 2c) Find the delay, dynamic power, and leakage power of the trimmed array.
(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))
for k,v in delay_results.items():
if "power" in k:
# Subtract partial array leakage and add full array leakage for the power measures
char_data[k].append(v - trim_array_leakage + full_array_leakage)
else:
char_data[k].append(v)
# 3) Finds the minimum period without degrading the delays by X%
self.set_load_slew(max(loads),max(slews))
min_period = self.find_min_period(feasible_delay_lh, feasible_delay_hl)
debug.check(type(min_period)==float,"Couldn't find minimum period.")
debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay1, feasible_delay0))
debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay_lh, feasible_delay_hl))
# 4) Pack up the final measurements
char_data["min_period"] = ch.round_time(min_period)
return char_data
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
}
return data
def obtain_cycle_times(self, period):
def obtain_cycle_times(self):
"""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."""
@@ -451,135 +621,135 @@ class delay():
# idle cycle, no operation
msg = "Idle cycle (no clock)"
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0,
t_current,
msg))
t_current,
msg))
self.cycle_times.append(t_current)
t_current += period
t_current += self.period
# One period
msg = "W data 1 address 11..11 to initialize cell"
self.cycle_times.append(t_current)
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current += period
t_current,
msg))
t_current += self.period
# One period
msg = "W data 0 address 11..11 (to ensure a write of value works)"
self.cycle_times.append(t_current)
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,
t_current,
msg))
t_current += period
t_current,
msg))
t_current += self.period
# One period
msg = "W data 1 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,
t_current,
msg))
t_current += period
t_current,
msg))
t_current += self.period
# One period
msg = "R data 0 address 11..11 to check W0 worked"
self.cycle_times.append(t_current)
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,
t_current,
msg))
t_current += period
t_current,
msg))
t_current += self.period
# One period
msg = "Idle cycle"
msg = "Idle cycle (Read addr 00..00)"
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current,
msg))
self.cycle_times.append(t_current)
t_current += period
self.idle_cycle=len(self.cycle_times)-1
t_current += self.period
# One period
msg = "W data 1 address 11..11 (to ensure a write of value worked)"
self.cycle_times.append(t_current)
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,
t_current,
msg))
t_current += period
t_current,
msg))
t_current += self.period
# 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,
t_current,
msg))
t_current += period
t_current,
msg))
t_current += self.period
# One period
msg = "R data 1 address 11..11 to check W1 worked"
self.cycle_times.append(t_current)
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,
t_current,
msg))
t_current += period
t_current,
msg))
t_current += self.period
# One period
msg = "Idle cycle"
msg = "Idle cycle (Read addr 11..11)"
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current,
msg))
self.cycle_times.append(t_current)
t_current += period
t_current += self.period
def analytical_model(self,sram, slews, loads):
def analytical_delay(self,sram, slews, loads):
""" Just return the analytical model results for the SRAM.
"""
LH_delay = []
HL_delay = []
LH_slew = []
HL_slew = []
delay_lh = []
delay_hl = []
slew_lh = []
slew_hl = []
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)
self.set_load_slew(load,slew)
bank_delay = sram.analytical_delay(self.slew,self.load)
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)
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))
#print "Dynamic: ",power.dynamic," nW"
#print "Leakage: ",power.leakage," nW"
data = {"min_period": 0,
"delay1": LH_delay,
"delay0": HL_delay,
"slew1": LH_slew,
"slew0": HL_slew,
"delay_lh": delay_lh,
"delay_hl": delay_hl,
"slew_lh": slew_lh,
"slew_hl": slew_hl,
"read0_power": power.dynamic,
"read1_power": power.leakage,
"read1_power": power.dynamic,
"write0_power": power.dynamic,
"write1_power": power.leakage
"write1_power": power.dynamic,
"leakage_power": power.leakage
}
return data
def gen_data(self, clk_times, sig_name, period, slew):
def gen_data(self, clk_times, sig_name):
""" Generates the PWL data inputs for a simulation timing test. """
# 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 ]
self.stim.gen_pwl(sig_name, clk_times, values, period, slew, 0.05)
self.stim.gen_pwl(sig_name, clk_times, values, self.period, self.slew, 0.05)
def gen_addr(self, clk_times, addr, period, slew):
def gen_addr(self, clk_times, addr):
"""
Generates the address inputs for a simulation timing test.
This alternates between all 1's and all 0's for the address.
@@ -591,34 +761,34 @@ class delay():
for i in range(len(addr)):
sig_name = "A[{0}]".format(i)
if addr[i]=="1":
self.stim.gen_pwl(sig_name, clk_times, ones_values, period, slew, 0.05)
self.stim.gen_pwl(sig_name, clk_times, ones_values, self.period, self.slew, 0.05)
else:
self.stim.gen_pwl(sig_name, clk_times, zero_values, period, slew, 0.05)
self.stim.gen_pwl(sig_name, clk_times, zero_values, self.period, self.slew, 0.05)
def gen_csb(self, clk_times, period, slew):
def gen_csb(self, clk_times):
""" Generates the PWL CSb signal """
# 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]
self.stim.gen_pwl("csb", clk_times, values, period, slew, 0.05)
self.stim.gen_pwl("csb", clk_times, values, self.period, self.slew, 0.05)
def gen_web(self, clk_times, period, slew):
def gen_web(self, clk_times):
""" Generates the PWL WEb signal """
# 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]
self.stim.gen_pwl("web", clk_times, values, period, slew, 0.05)
self.stim.gen_pwl("web", clk_times, values, self.period, self.slew, 0.05)
# Keep acc_en deasserted in NOP for measuring >1 period
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
self.stim.gen_pwl("acc_en", clk_times, values, period, slew, 0)
self.stim.gen_pwl("acc_en", clk_times, values, self.period, self.slew, 0)
values = [0, 1, 1, 1, 0, 0, 1, 1, 0, 0]
self.stim.gen_pwl("acc_en_inv", clk_times, values, period, slew, 0)
self.stim.gen_pwl("acc_en_inv", clk_times, values, self.period, self.slew, 0)
def gen_oeb(self, clk_times, period, slew):
def gen_oeb(self, clk_times):
""" Generates the PWL WEb signal """
# 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]
self.stim.gen_pwl("oeb", clk_times, values, period, slew, 0.05)
self.stim.gen_pwl("oeb", clk_times, values, self.period, self.slew, 0.05)
+96 -87
View File
@@ -1,4 +1,4 @@
import os,sys,re,shutil
import os,sys,re
import debug
import math
import setup_hold
@@ -6,7 +6,6 @@ import delay
import charutils as ch
import tech
import numpy as np
from trim_spice import trim_spice
from globals import OPTS
class lib:
@@ -18,30 +17,12 @@ class lib:
self.sp_file = sp_file
self.use_model = use_model
self.prepare_netlist()
self.prepare_tables()
self.create_corners()
self.characterize_corners()
def prepare_netlist(self):
""" Determine whether to use regular or trimmed netlist. """
# Set up to trim the netlist here if that is enabled
if OPTS.trim_netlist:
self.sim_sp_file = "{}reduced.sp".format(OPTS.openram_temp)
self.trimsp=trim_spice(self.sp_file, self.sim_sp_file)
self.trimsp.set_configuration(self.sram.num_banks,
self.sram.num_rows,
self.sram.num_cols,
self.sram.word_size)
else:
# Else, use the non-reduced netlist file for 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)
def prepare_tables(self):
""" Determine the load/slews if they aren't specified in the config file. """
@@ -49,7 +30,7 @@ class lib:
#self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8])
self.load_scales = np.array([0.25, 1, 8])
#self.load_scales = np.array([0.25, 1])
self.load = tech.spice["msflop_in_cap"]
self.load = tech.spice["dff_in_cap"]
self.loads = self.load_scales*self.load
debug.info(1,"Loads: {0}".format(self.loads))
@@ -78,7 +59,7 @@ class lib:
proc,
volt,
temp)
self.corner_name = self.corner_name.replace(".","") # Remove decimals
self.corner_name = self.corner_name.replace(".","p") # Remove decimals
lib_name = self.out_dir+"{}.lib".format(self.corner_name)
# A corner is a tuple of PVT
@@ -93,10 +74,15 @@ class lib:
self.lib = open(lib_name, "w")
debug.info(1,"Writing to {0}".format(lib_name))
self.characterize()
self.lib.close()
def characterize(self):
""" Characterize the current corner. """
self.compute_delay()
self.compute_setup_hold()
self.write_header()
self.write_data_bus()
@@ -106,8 +92,13 @@ class lib:
self.write_control_pins()
self.write_clk()
self.write_footer()
self.lib.close()
def write_footer(self):
""" Write the footer """
self.lib.write("}\n")
def write_header(self):
""" Write the header information """
@@ -119,7 +110,7 @@ class lib:
self.write_defaults()
self.write_LUT_templates()
self.lib.write(" default_operating_conditions : TT; \n")
self.lib.write(" default_operating_conditions : OC; \n")
self.write_bus()
@@ -127,14 +118,21 @@ class lib:
self.lib.write("{\n")
self.lib.write(" memory(){ \n")
self.lib.write(" type : ram;\n")
self.lib.write(" address_width : {0};\n".format(self.sram.addr_size))
self.lib.write(" word_width : {0};\n".format(self.sram.word_size))
self.lib.write(" address_width : {};\n".format(self.sram.addr_size))
self.lib.write(" word_width : {};\n".format(self.sram.word_size))
self.lib.write(" }\n")
self.lib.write(" interface_timing : true;\n")
self.lib.write(" dont_use : true;\n")
self.lib.write(" map_only : true;\n")
self.lib.write(" dont_touch : true;\n")
self.lib.write(" area : {0};\n\n".format(self.sram.width * self.sram.height))
self.lib.write(" area : {};\n\n".format(self.sram.width * self.sram.height))
# Leakage is included in dynamic when macro is enabled
self.lib.write(" leakage_power () {\n")
self.lib.write(" when : \"CSb\";\n")
self.lib.write(" value : {};\n".format(self.char_results["leakage_power"]))
self.lib.write(" }\n")
self.lib.write(" cell_leakage_power : {};\n".format(0))
def write_units(self):
@@ -147,7 +145,8 @@ class lib:
self.lib.write(" capacitive_load_unit(1 ,fF) ;\n")
self.lib.write(" leakage_power_unit : \"1mW\" ;\n")
self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n")
self.lib.write(" operating_conditions({}){{\n".format(self.process))
self.lib.write(" operating_conditions(OC){\n")
self.lib.write(" process : {} ;\n".format(1.0)) # How to use TT, FF, SS?
self.lib.write(" voltage : {} ;\n".format(self.voltage))
self.lib.write(" temperature : {};\n".format(self.temperature))
self.lib.write(" }\n\n")
@@ -164,6 +163,10 @@ class lib:
self.lib.write(" slew_lower_threshold_pct_rise : 10.0 ;\n")
self.lib.write(" slew_upper_threshold_pct_rise : 90.0 ;\n\n")
self.lib.write(" nom_voltage : {};\n".format(tech.spice["nom_supply_voltage"]))
self.lib.write(" nom_temperature : {};\n".format(tech.spice["nom_temperature"]))
self.lib.write(" nom_process : {};\n".format(1.0))
self.lib.write(" default_cell_leakage_power : 0.0 ;\n")
self.lib.write(" default_leakage_power_density : 0.0 ;\n")
self.lib.write(" default_input_pin_cap : 1.0 ;\n")
@@ -267,8 +270,6 @@ class lib:
def write_FF_setuphold(self):
""" Adds Setup and Hold timing results"""
self.compute_setup_hold()
self.lib.write(" timing(){ \n")
self.lib.write(" timing_type : setup_rising; \n")
self.lib.write(" related_pin : \"clk\"; \n")
@@ -299,12 +300,12 @@ class lib:
def write_data_bus(self):
""" Adds data bus timing results."""
self.compute_delay()
self.lib.write(" bus(DATA){\n")
self.lib.write(" bus_type : DATA; \n")
self.lib.write(" direction : inout; \n")
self.lib.write(" max_capacitance : {0}; \n".format(8*tech.spice["msflop_in_cap"]))
# This is conservative, but limit to range that we characterized.
self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads)))
self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads)))
self.lib.write(" three_state : \"!OEb & !clk\"; \n")
self.lib.write(" memory_write(){ \n")
self.lib.write(" address : ADDR; \n")
@@ -313,53 +314,29 @@ class lib:
self.lib.write(" memory_read(){ \n")
self.lib.write(" address : ADDR; \n")
self.lib.write(" }\n")
self.lib.write(" pin(DATA[{0}:0])".format(self.sram.word_size - 1))
self.lib.write("{\n")
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"OEb & !clk\"; \n")
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(self.delay["write1_power"]))
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(self.delay["write0_power"]))
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" pin(DATA[{0}:0]){{\n".format(self.sram.word_size - 1))
self.write_FF_setuphold()
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"!OEb & !clk\"; \n")
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(self.delay["read1_power"]))
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(self.delay["read0_power"]))
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" timing(){ \n")
self.lib.write(" timing_sense : non_unate; \n")
self.lib.write(" related_pin : \"clk\"; \n")
self.lib.write(" timing_type : falling_edge; \n")
self.lib.write(" cell_rise(CELL_TABLE) {\n")
rounded_values = map(ch.round_time,self.delay["delay1"])
self.write_values(rounded_values,len(self.loads)," ")
self.lib.write(" }\n")
self.write_values(self.char_results["delay_lh"],len(self.loads)," ")
self.lib.write(" }\n") # rise delay
self.lib.write(" cell_fall(CELL_TABLE) {\n")
rounded_values = map(ch.round_time,self.delay["delay0"])
self.write_values(rounded_values,len(self.loads)," ")
self.lib.write(" }\n")
self.lib.write(" rise_transition(CELL_TABLE) {\n")
rounded_values = map(ch.round_time,self.delay["slew1"])
self.write_values(rounded_values,len(self.loads)," ")
self.lib.write(" }\n")
self.lib.write(" fall_transition(CELL_TABLE) {\n")
rounded_values = map(ch.round_time,self.delay["slew0"])
self.write_values(rounded_values,len(self.loads)," ")
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" }\n\n")
self.write_values(self.char_results["delay_hl"],len(self.loads)," ")
self.lib.write(" }\n") # fall delay
self.lib.write(" rise_transition(CELL_TABLE) {\n")
self.write_values(self.char_results["slew_lh"],len(self.loads)," ")
self.lib.write(" }\n") # rise trans
self.lib.write(" fall_transition(CELL_TABLE) {\n")
self.write_values(self.char_results["slew_hl"],len(self.loads)," ")
self.lib.write(" }\n") # fall trans
self.lib.write(" }\n") # timing
self.lib.write(" }\n") # pin
self.lib.write(" }\n\n") # bus
def write_addr_bus(self):
@@ -368,9 +345,8 @@ class lib:
self.lib.write(" bus(ADDR){\n")
self.lib.write(" bus_type : ADDR; \n")
self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["msflop_in_cap"]))
self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]))
self.lib.write(" max_transition : {0};\n".format(self.slews[-1]))
self.lib.write(" fanout_load : 1.000000;\n")
self.lib.write(" pin(ADDR[{0}:0])".format(self.sram.addr_size - 1))
self.lib.write("{\n")
@@ -387,7 +363,7 @@ class lib:
self.lib.write(" pin({0})".format(i))
self.lib.write("{\n")
self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["msflop_in_cap"]))
self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]))
self.write_FF_setuphold()
self.lib.write(" }\n\n")
@@ -395,14 +371,50 @@ class lib:
def write_clk(self):
""" Adds clk pin timing results."""
self.compute_delay()
self.lib.write(" pin(clk){\n")
self.lib.write(" clock : true;\n")
self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["msflop_in_cap"]))
min_pulse_width = ch.round_time(self.delay["min_period"])/2.0
min_period = ch.round_time(self.delay["min_period"])
# This should actually be a min inverter cap, but ok...
self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]))
# Find the average power of 1 and 0 bits for writes and reads over all loads/slews
# Could make it a table, but this is fine for now.
avg_write_power = np.mean(self.char_results["write1_power"] + self.char_results["write0_power"])
avg_read_power = np.mean(self.char_results["read1_power"] + self.char_results["read0_power"])
# Equally divide read/write power between first and second half of clock period
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"!CSb & clk & !WEb\"; \n")
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0))
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(avg_write_power/2.0))
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"!CSb & !clk & WEb\"; \n")
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0))
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"{0}\");\n".format(avg_read_power/2.0))
self.lib.write(" }\n")
self.lib.write(" }\n")
# Have 0 internal power when disabled, this will be represented as leakage power.
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"CSb\"; \n")
self.lib.write(" rise_power(scalar){\n")
self.lib.write(" values(\"0\");\n")
self.lib.write(" }\n")
self.lib.write(" fall_power(scalar){\n")
self.lib.write(" values(\"0\");\n")
self.lib.write(" }\n")
self.lib.write(" }\n")
min_pulse_width = ch.round_time(self.char_results["min_period"])/2.0
min_period = ch.round_time(self.char_results["min_period"])
self.lib.write(" timing(){ \n")
self.lib.write(" timing_type :\"min_pulse_width\"; \n")
self.lib.write(" related_pin : clk; \n")
@@ -425,23 +437,20 @@ class lib:
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write("}\n")
def compute_delay(self):
""" Do the analysis if we haven't characterized the SRAM yet """
try:
self.d
except AttributeError:
self.d = delay.delay(self.sram, self.sim_sp_file, self.corner)
self.d = delay.delay(self.sram, self.sp_file, self.corner)
if self.use_model:
self.delay = self.d.analytical_model(self.sram,self.slews,self.loads)
self.char_results = self.d.analytical_delay(self.sram,self.slews,self.loads)
else:
probe_address = "1" * self.sram.addr_size
probe_data = self.sram.word_size - 1
# We must trim based on a specific address and data bit
if OPTS.trim_netlist:
self.trimsp.trim(probe_address,probe_data)
self.delay = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
self.char_results = self.d.analyze(probe_address, probe_data, self.slews, self.loads)
def compute_setup_hold(self):
""" Do the analysis if we haven't characterized a FF yet """
@@ -451,7 +460,7 @@ class lib:
except AttributeError:
self.sh = setup_hold.setup_hold(self.corner)
if self.use_model:
self.times = self.sh.analytical_model(self.slews,self.loads)
self.times = self.sh.analytical_setuphold(self.slews,self.loads)
else:
self.times = self.sh.analyze(self.slews,self.slews)
+1 -1
View File
@@ -299,7 +299,7 @@ class setup_hold():
}
return times
def analytical_model(self,related_slews, constrained_slews):
def analytical_setuphold(self,related_slews, constrained_slews):
""" Just return the fixed setup/hold times from the technology.
"""
LH_setup = []
+1
View File
@@ -286,6 +286,7 @@ class stimuli():
OPTS.openram_temp)
valid_retcode=0
else:
# ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit
cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe,
temp_stim,
OPTS.openram_temp)