Clean up Python comments and improve comments in stimulus file.

This commit is contained in:
Matt Guthaus 2018-02-07 14:04:18 -08:00
parent 3820861ce8
commit 3e4ef36efe
2 changed files with 115 additions and 62 deletions

View File

@ -10,7 +10,7 @@ from globals import OPTS
class delay():
"""
Functions to measure the delay of the SRAM at a given address and
Functions to measure the delay of an SRAM at a given address and
data bit.
"""
@ -52,7 +52,7 @@ class delay():
# 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.sf.write("\n* Stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(period,load,slew))
# include files in stimulus file
model_list = tech.spice["fet_models"] + [self.sram_sp_file]
@ -60,26 +60,26 @@ class delay():
# add vdd/gnd statements
self.sf.write("* Global Power Supplies\n")
self.sf.write("\n* Global Power Supplies\n")
stimuli.write_supply(self.sf)
# instantiate the sram
self.sf.write("* Instantiation of the SRAM\n")
self.sf.write("\n* Instantiation of the SRAM\n")
stimuli.inst_sram(stim_file=self.sf,
abits=self.addr_size,
dbits=self.word_size,
sram_name=self.name)
self.sf.write("* SRAM output loads\n")
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))
# add access transistors for data-bus
self.sf.write("* Transmission Gates for data-bus and control signals\n")
self.sf.write("\n* Transmission Gates for data-bus and control signals\n")
stimuli.inst_accesstx(stim_file=self.sf, dbits=self.word_size)
# generate data and addr signals
self.sf.write("* Generation of data and address signals\n")
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,
@ -97,20 +97,20 @@ class delay():
slew=slew)
# generate control signals
self.sf.write("* Generation of control signals\n")
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.sf.write("* Generation of global clock signal\n")
self.sf.write("\n* Generation of global clock signal\n")
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)
t_rise=slew,
t_fall=slew)
self.write_measures(period)
@ -120,17 +120,23 @@ class delay():
self.sf.close()
def write_measures(self,period):
# meas statement for delay and power measurements
self.sf.write("* Measure statements for delay and power\n")
"""
Write the measure statements to quantify the delay and power results.
"""
self.sf.write("\n* Measure statements for delay and power\n")
# Output some comments to aid where cycles start and
# what is happening
for comment in self.cycle_comments:
self.sf.write("* {}\n".format(comment))
# Trigger on the clk of the appropriate cycle
trig_name = "clk"
targ_name = "{0}".format("d[{0}]".format(self.probe_data))
trig_val = targ_val = 0.5 * self.vdd
# add measure statments for delay0
# delay the target to measure after the negative edge
# Delay the target to measure after the negative edge
stimuli.gen_meas_delay(stim_file=self.sf,
meas_name="DELAY0",
trig_name=trig_name,
@ -205,11 +211,13 @@ class delay():
t_final=t_final)
def find_feasible_period(self, load, slew):
"""Uses an initial period and finds a feasible period before we
"""
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. """
starting point.
"""
feasible_period = tech.spice["feasible_period"]
time_out = 8
@ -225,13 +233,20 @@ class delay():
feasible_period = 2 * feasible_period
continue
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))
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)
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."""
"""
This tries to simulate a period and checks if the result
works. If so, it returns True and the delays and slews.
"""
# Checking from not data_value to data_value
self.write_stimulus(period, load, slew)
@ -243,17 +258,40 @@ class delay():
# 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))
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))
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))
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))
# For debug, you sometimes want to inspect each simulation.
#key=raw_input("press return to continue")
# The delay is from the negative edge for our SRAM
@ -262,8 +300,10 @@ class delay():
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. """
"""
Searches for the smallest period with output delays being within 5% of
long period.
"""
previous_period = ub_period = feasible_period
lb_period = 0.0
@ -291,8 +331,10 @@ class delay():
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."""
"""
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)
@ -303,14 +345,16 @@ class delay():
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,"Invalid measures: Period {0}, delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1))
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))
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))
return False
else:
if not ch.relative_compare(delay1,feasible_delay1,error_tolerance=0.05):
@ -323,7 +367,8 @@ class delay():
#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}, " +
"delay0={1}ns, delay1={2}ns slew0={3}ns slew1={4}ns".format(period, delay0, delay1, slew0, slew1))
return True
def set_probe(self,probe_address, probe_data):
@ -520,7 +565,7 @@ class delay():
return data
def gen_data(self, clk_times, sig_name, period, slew):
"""Generates the PWL data inputs for a simulation timing test."""
""" 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.
@ -528,8 +573,9 @@ class delay():
stimuli.gen_pwl(self.sf, sig_name, clk_times, values, period, slew, 0.05)
def gen_addr(self, clk_times, addr, period, slew):
"""Generates the address inputs for a simulation timing test.
One cycle is different to clear the bus
"""
Generates the address inputs for a simulation timing test.
This alternates between all 1's and all 0's for the address.
"""
zero_values = [0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ]
@ -544,14 +590,14 @@ class delay():
def gen_csb(self, clk_times, period, slew):
""" Generates the PWL CSb signal"""
""" 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]
stimuli.gen_pwl(self.sf, "csb", clk_times, values, period, slew, 0.05)
def gen_web(self, clk_times, period, slew):
""" Generates the PWL WEb signal"""
""" 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]
@ -564,7 +610,7 @@ class delay():
stimuli.gen_pwl(self.sf, "acc_en_inv", clk_times, values, period, slew, 0)
def gen_oeb(self, clk_times, period, slew):
""" Generates the PWL WEb signal"""
""" 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]

View File

@ -1,7 +1,7 @@
"""
This file generates the test structure and stimulus for an sram
simulation. There are various functions that can be be used to
generate stimulus for other simulations as well.
This file generates simple spice cards for simulation. There are
various functions that can be be used to generate stimulus for other
simulations as well.
"""
import tech
@ -22,7 +22,7 @@ tx_width = tech.spice["minwidth_tx"]
tx_length = tech.spice["channel"]
def inst_sram(stim_file, abits, dbits, sram_name):
"""function to instatiate the sram subckt"""
""" Function to instatiate an SRAM subckt. """
stim_file.write("Xsram ")
for i in range(dbits):
stim_file.write("D[{0}] ".format(i))
@ -32,11 +32,11 @@ def inst_sram(stim_file, abits, dbits, sram_name):
stim_file.write("{0} ".format(i))
stim_file.write("{0} ".format(tech.spice["clk"]))
stim_file.write("{0} {1} ".format(vdd_name, gnd_name))
stim_file.write("{0}\n\n".format(sram_name))
stim_file.write("{0}\n".format(sram_name))
def inst_model(stim_file, pins, model_name):
"""function to instantiate a model"""
""" Function to instantiate a generic model with a set of pins """
stim_file.write("X{0} ".format(model_name))
for pin in pins:
stim_file.write("{0} ".format(pin))
@ -44,7 +44,7 @@ def inst_model(stim_file, pins, model_name):
def create_inverter(stim_file, size=1, beta=2.5):
"""Generates inverter for the top level signals (only for sim purposes)"""
""" Generates inverter for the top level signals (only for sim purposes) """
stim_file.write(".SUBCKT test_inv in out {0} {1}\n".format(vdd_name, gnd_name))
stim_file.write("mpinv out in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name,
pmos_name,
@ -58,9 +58,10 @@ def create_inverter(stim_file, size=1, beta=2.5):
def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5):
"""Generates buffer for top level signals (only for sim
purposes). Size is pair for PMOS, NMOS width multiple. It includes
a beta of 3."""
"""
Generates buffer for top level signals (only for sim
purposes). Size is pair for PMOS, NMOS width multiple.
"""
stim_file.write(".SUBCKT test_{2} in out {0} {1}\n".format(vdd_name,
gnd_name,
@ -85,7 +86,7 @@ def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5):
def inst_buffer(stim_file, buffer_name, signal_list):
"""Adds buffers to each top level signal that is in signal_list (only for sim purposes)"""
""" Adds buffers to each top level signal that is in signal_list (only for sim purposes) """
for signal in signal_list:
stim_file.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal,
"test"+vdd_name,
@ -94,7 +95,7 @@ def inst_buffer(stim_file, buffer_name, signal_list):
def inst_inverter(stim_file, signal_list):
"""Adds inv for each signal that needs its inverted version (only for sim purposes)"""
""" Adds inv for each signal that needs its inverted version (only for sim purposes) """
for signal in signal_list:
stim_file.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal,
"test"+vdd_name,
@ -102,7 +103,7 @@ def inst_inverter(stim_file, signal_list):
def inst_accesstx(stim_file, dbits):
"""Adds transmission gate for inputs to data-bus (only for sim purposes)"""
""" Adds transmission gate for inputs to data-bus (only for sim purposes) """
stim_file.write("* Tx Pin-list: Drain Gate Source Body\n")
for i in range(dbits):
pmos_access_string="mp{0} DATA[{0}] acc_en D[{0}] {1} {2} w={3}u l={4}u\n"
@ -119,8 +120,11 @@ def inst_accesstx(stim_file, dbits):
tx_length))
def gen_pulse(stim_file, sig_name, v1=gnd_voltage, v2=vdd_voltage, offset=0, period=1, t_rise=0, t_fall=0):
"""Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
from 50% to 50%."""
"""
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
from 50% to 50%.
"""
stim_file.write("* PULSE: period={0}\n".format(period))
pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n"
stim_file.write(pulse_string.format(sig_name,
v1,
@ -133,6 +137,11 @@ def gen_pulse(stim_file, sig_name, v1=gnd_voltage, v2=vdd_voltage, offset=0, per
def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup):
"""
Generate a PWL stimulus given a signal name and data values at each period.
Automatically creates slews and ensures each data occurs a setup before the clock
edge.
"""
# the initial value is not a clock time
debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match.")
@ -140,6 +149,7 @@ def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup):
times = np.array(clk_times) - setup*period
values = np.array(data_values) * vdd_voltage
half_slew = 0.5 * slew
stim_file.write("* (time, data): {}\n".format(zip(clk_times, data_values)))
stim_file.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
for i in range(1,len(times)-1):
stim_file.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,
@ -148,13 +158,10 @@ def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup):
values[i]))
stim_file.write(")\n")
def gen_constant(stim_file, sig_name, v_val):
"""Generates a constant signal with reference voltage and the voltage value"""
""" Generates a constant signal with reference voltage and the voltage value """
stim_file.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val))
def get_inverse_voltage(value):
if value > 0.5*vdd_voltage:
return gnd_voltage
@ -173,7 +180,7 @@ def get_inverse_value(value):
def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td):
"""Creates the .meas statement for the measurement of delay"""
""" Creates the .meas statement for the measurement of delay """
measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n"
stim_file.write(measure_string.format(meas_name,
trig_name,
@ -186,7 +193,7 @@ def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_va
targ_td))
def gen_meas_power(stim_file, meas_name, t_initial, t_final):
"""Creates the .meas statement for the measurement of avg power"""
""" Creates the .meas statement for the measurement of avg power """
# power mea cmd is different in different spice:
if OPTS.spice_name == "hspice":
power_exp = "power"
@ -196,9 +203,9 @@ def gen_meas_power(stim_file, meas_name, t_initial, t_final):
power_exp,
t_initial,
t_final))
stim_file.write("\n")
def write_control(stim_file, end_time):
""" Write the control cards to run and end the simulation """
# UIC is needed for ngspice to converge
stim_file.write(".TRAN 5p {0}n UIC\n".format(end_time))
if OPTS.spice_name == "ngspice":
@ -227,22 +234,22 @@ def write_include(stim_file, models):
"""Writes include statements, inputs are lists of model files"""
for item in list(models):
if os.path.isfile(item):
stim_file.write(".include \"{0}\"\n\n".format(item))
stim_file.write(".include \"{0}\"\n".format(item))
else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
def write_supply(stim_file):
"""Writes supply voltage statements"""
""" Writes supply voltage statements """
stim_file.write("V{0} {0} 0.0 {1}\n".format(vdd_name, vdd_voltage))
stim_file.write("V{0} {0} 0.0 {1}\n".format(gnd_name, gnd_voltage))
# This is for the test power supply
stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+vdd_name, vdd_voltage))
stim_file.write("V{0} {0} 0.0 {1}\n\n".format("test"+gnd_name, gnd_voltage))
stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+gnd_name, gnd_voltage))
def run_sim():
"""Run hspice in batch mode and output rawfile to parse."""
""" Run hspice in batch mode and output rawfile to parse. """
temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
import datetime
start_time = datetime.datetime.now()