Begin modifications for corner-based characterization. Made stimuli.py a class. Golden output files are not updated.

This commit is contained in:
Matt Guthaus 2018-02-09 15:33:03 -08:00
parent b7be042c7f
commit f86985821a
15 changed files with 557 additions and 509 deletions

View File

@ -14,15 +14,20 @@ class delay():
data bit. data bit.
""" """
def __init__(self,sram,spfile): def __init__(self,sram,spfile, corner):
self.name = sram.name self.name = sram.name
self.num_words = sram.num_words self.num_words = sram.num_words
self.word_size = sram.word_size self.word_size = sram.word_size
self.addr_size = sram.addr_size self.addr_size = sram.addr_size
self.sram_sp_file = spfile self.sram_sp_file = spfile
self.vdd = tech.spice["supply_voltage"] self.set_corner(corner)
self.gnd = tech.spice["gnd_voltage"]
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 check_arguments(self): def check_arguments(self):
@ -53,20 +58,18 @@ class delay():
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.sf = open(temp_stim, "w") 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("* 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 # include files in stimulus file
model_list = tech.spice["fet_models"] + [self.sram_sp_file] self.stim.write_include(self.sram_sp_file)
stimuli.write_include(stim_file=self.sf, models=model_list)
# add vdd/gnd statements # add vdd/gnd statements
self.sf.write("\n* Global Power Supplies\n") self.sf.write("\n* Global Power Supplies\n")
stimuli.write_supply(self.sf) self.stim.write_supply()
# instantiate the sram # instantiate the sram
self.sf.write("\n* Instantiation of the SRAM\n") self.sf.write("\n* Instantiation of the SRAM\n")
stimuli.inst_sram(stim_file=self.sf, self.stim.inst_sram(abits=self.addr_size,
abits=self.addr_size,
dbits=self.word_size, dbits=self.word_size,
sram_name=self.name) sram_name=self.name)
@ -76,7 +79,7 @@ class delay():
# add access transistors for data-bus # add access transistors for data-bus
self.sf.write("\n* 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) self.stim.inst_accesstx(dbits=self.word_size)
# generate data and addr signals # generate data and addr signals
self.sf.write("\n* Generation of data and address signals\n") self.sf.write("\n* Generation of data and address signals\n")
@ -87,9 +90,8 @@ class delay():
period=period, period=period,
slew=slew) slew=slew)
else: else:
stimuli.gen_constant(stim_file=self.sf, self.stim.gen_constant(sig_name="d[{0}]".format(i),
sig_name="d[{0}]".format(i), v_val=self.gnd_voltage)
v_val=self.gnd)
self.gen_addr(clk_times=self.cycle_times, self.gen_addr(clk_times=self.cycle_times,
addr=self.probe_address, addr=self.probe_address,
@ -103,10 +105,9 @@ class delay():
self.gen_oeb(self.cycle_times, period, slew) self.gen_oeb(self.cycle_times, period, slew)
self.sf.write("\n* Generation of global clock signal\n") self.sf.write("\n* Generation of global clock signal\n")
stimuli.gen_pulse(stim_file=self.sf, self.stim.gen_pulse(sig_name="CLK",
sig_name="CLK", v1=self.gnd_voltage,
v1=self.gnd, v2=self.vdd_voltage,
v2=self.vdd,
offset=period, offset=period,
period=period, period=period,
t_rise=slew, t_rise=slew,
@ -115,7 +116,7 @@ class delay():
self.write_measures(period) self.write_measures(period)
# run until the end of the cycle time # run until the end of the cycle time
stimuli.write_control(self.sf,self.cycle_times[-1] + period) self.stim.write_control(self.cycle_times[-1] + period)
self.sf.close() self.sf.close()
@ -134,11 +135,10 @@ class delay():
# Trigger on the clk of the appropriate cycle # Trigger on the clk of the appropriate cycle
trig_name = "clk" trig_name = "clk"
targ_name = "{0}".format("d[{0}]".format(self.probe_data)) targ_name = "{0}".format("d[{0}]".format(self.probe_data))
trig_val = targ_val = 0.5 * self.vdd trig_val = targ_val = 0.5 * self.vdd_voltage
# 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, self.stim.gen_meas_delay(meas_name="DELAY0",
meas_name="DELAY0",
trig_name=trig_name, trig_name=trig_name,
targ_name=targ_name, targ_name=targ_name,
trig_val=trig_val, trig_val=trig_val,
@ -148,8 +148,7 @@ class delay():
trig_td=self.cycle_times[self.read0_cycle], 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*period)
stimuli.gen_meas_delay(stim_file=self.sf, self.stim.gen_meas_delay(meas_name="DELAY1",
meas_name="DELAY1",
trig_name=trig_name, trig_name=trig_name,
targ_name=targ_name, targ_name=targ_name,
trig_val=trig_val, trig_val=trig_val,
@ -159,23 +158,21 @@ class delay():
trig_td=self.cycle_times[self.read1_cycle], 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*period)
stimuli.gen_meas_delay(stim_file=self.sf, self.stim.gen_meas_delay(meas_name="SLEW0",
meas_name="SLEW0",
trig_name=targ_name, trig_name=targ_name,
targ_name=targ_name, targ_name=targ_name,
trig_val=0.9*self.vdd, trig_val=0.9*self.vdd_voltage,
targ_val=0.1*self.vdd, targ_val=0.1*self.vdd_voltage,
trig_dir="FALL", trig_dir="FALL",
targ_dir="FALL", targ_dir="FALL",
trig_td=self.cycle_times[self.read0_cycle], 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*period)
stimuli.gen_meas_delay(stim_file=self.sf, self.stim.gen_meas_delay(meas_name="SLEW1",
meas_name="SLEW1",
trig_name=targ_name, trig_name=targ_name,
targ_name=targ_name, targ_name=targ_name,
trig_val=0.1*self.vdd, trig_val=0.1*self.vdd_voltage,
targ_val=0.9*self.vdd, targ_val=0.9*self.vdd_voltage,
trig_dir="RISE", trig_dir="RISE",
targ_dir="RISE", targ_dir="RISE",
trig_td=self.cycle_times[self.read1_cycle], trig_td=self.cycle_times[self.read1_cycle],
@ -184,29 +181,25 @@ class delay():
# add measure statements for power # add measure statements for power
t_initial = self.cycle_times[self.write0_cycle] t_initial = self.cycle_times[self.write0_cycle]
t_final = self.cycle_times[self.write0_cycle+1] t_final = self.cycle_times[self.write0_cycle+1]
stimuli.gen_meas_power(stim_file=self.sf, self.stim.gen_meas_power(meas_name="WRITE0_POWER",
meas_name="WRITE0_POWER",
t_initial=t_initial, t_initial=t_initial,
t_final=t_final) t_final=t_final)
t_initial = self.cycle_times[self.write1_cycle] t_initial = self.cycle_times[self.write1_cycle]
t_final = self.cycle_times[self.write1_cycle+1] t_final = self.cycle_times[self.write1_cycle+1]
stimuli.gen_meas_power(stim_file=self.sf, self.stim.gen_meas_power(meas_name="WRITE1_POWER",
meas_name="WRITE1_POWER",
t_initial=t_initial, t_initial=t_initial,
t_final=t_final) t_final=t_final)
t_initial = self.cycle_times[self.read0_cycle] t_initial = self.cycle_times[self.read0_cycle]
t_final = self.cycle_times[self.read0_cycle+1] t_final = self.cycle_times[self.read0_cycle+1]
stimuli.gen_meas_power(stim_file=self.sf, self.stim.gen_meas_power(meas_name="READ0_POWER",
meas_name="READ0_POWER",
t_initial=t_initial, t_initial=t_initial,
t_final=t_final) t_final=t_final)
t_initial = self.cycle_times[self.read1_cycle] t_initial = self.cycle_times[self.read1_cycle]
t_final = self.cycle_times[self.read1_cycle+1] t_final = self.cycle_times[self.read1_cycle+1]
stimuli.gen_meas_power(stim_file=self.sf, self.stim.gen_meas_power(meas_name="READ1_POWER",
meas_name="READ1_POWER",
t_initial=t_initial, t_initial=t_initial,
t_final=t_final) t_final=t_final)
@ -249,7 +242,7 @@ class delay():
# Checking from not data_value to data_value # Checking from not data_value to data_value
self.write_stimulus(period, load, slew) self.write_stimulus(period, load, slew)
stimuli.run_sim() self.stim.run_sim()
delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0")) delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0"))
delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1")) delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1"))
slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0")) slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0"))
@ -334,7 +327,7 @@ class delay():
# Checking from not data_value to data_value # Checking from not data_value to data_value
self.write_stimulus(period,load,slew) self.write_stimulus(period,load,slew)
stimuli.run_sim() self.stim.run_sim()
delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0")) delay0 = ch.convert_to_float(ch.parse_output("timing", "delay0"))
delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1")) delay1 = ch.convert_to_float(ch.parse_output("timing", "delay1"))
slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0")) slew0 = ch.convert_to_float(ch.parse_output("timing", "slew0"))
@ -575,7 +568,7 @@ class delay():
# we are asserting the opposite value on the other side of the tx gate during # 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. # the read to be "worst case". Otherwise, it can actually assist the read.
values = [0, 1, 0, 1, 1, 1, 1, 0, 0, 0 ] values = [0, 1, 0, 1, 1, 1, 1, 0, 0, 0 ]
stimuli.gen_pwl(self.sf, sig_name, clk_times, values, period, slew, 0.05) self.stim.gen_pwl(sig_name, clk_times, values, period, slew, 0.05)
def gen_addr(self, clk_times, addr, period, slew): def gen_addr(self, clk_times, addr, period, slew):
""" """
@ -589,9 +582,9 @@ class delay():
for i in range(len(addr)): for i in range(len(addr)):
sig_name = "A[{0}]".format(i) sig_name = "A[{0}]".format(i)
if addr[i]=="1": if addr[i]=="1":
stimuli.gen_pwl(self.sf, sig_name, clk_times, ones_values, period, slew, 0.05) self.stim.gen_pwl(sig_name, clk_times, ones_values, period, slew, 0.05)
else: else:
stimuli.gen_pwl(self.sf, sig_name, clk_times, zero_values, period, slew, 0.05) self.stim.gen_pwl(sig_name, clk_times, zero_values, period, slew, 0.05)
def gen_csb(self, clk_times, period, slew): def gen_csb(self, clk_times, period, slew):
@ -599,24 +592,24 @@ class delay():
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP # values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
# Keep CSb asserted in NOP for measuring >1 period # Keep CSb asserted in NOP for measuring >1 period
values = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] values = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
stimuli.gen_pwl(self.sf, "csb", clk_times, values, period, slew, 0.05) self.stim.gen_pwl("csb", clk_times, values, period, slew, 0.05)
def gen_web(self, clk_times, period, slew): 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 # values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
# Keep WEb deasserted in NOP for measuring >1 period # Keep WEb deasserted in NOP for measuring >1 period
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1] values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
stimuli.gen_pwl(self.sf, "web", clk_times, values, period, slew, 0.05) self.stim.gen_pwl("web", clk_times, values, period, slew, 0.05)
# Keep acc_en deasserted in NOP for measuring >1 period # Keep acc_en deasserted in NOP for measuring >1 period
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1] values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
stimuli.gen_pwl(self.sf, "acc_en", clk_times, values, period, slew, 0) self.stim.gen_pwl("acc_en", clk_times, values, period, slew, 0)
values = [0, 1, 1, 1, 0, 0, 1, 1, 0, 0] values = [0, 1, 1, 1, 0, 0, 1, 1, 0, 0]
stimuli.gen_pwl(self.sf, "acc_en_inv", clk_times, values, period, slew, 0) self.stim.gen_pwl("acc_en_inv", clk_times, values, period, slew, 0)
def gen_oeb(self, clk_times, period, slew): 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 # values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
# Keep OEb asserted in NOP for measuring >1 period # Keep OEb asserted in NOP for measuring >1 period
values = [1, 1, 1, 1, 0, 0, 1, 1, 0, 0] values = [1, 1, 1, 1, 0, 0, 1, 1, 0, 0]
stimuli.gen_pwl(self.sf, "oeb", clk_times, values, period, slew, 0.05) self.stim.gen_pwl("oeb", clk_times, values, period, slew, 0.05)

View File

@ -12,14 +12,22 @@ from globals import OPTS
class lib: class lib:
""" lib file generation.""" """ lib file generation."""
def __init__(self, libname, sram, sp_file, use_model=OPTS.analytical_delay): def __init__(self, out_dir, sram, sp_file, use_model=OPTS.analytical_delay):
self.out_dir = out_dir
self.sram = sram self.sram = sram
self.sp_file = sp_file self.sp_file = sp_file
self.use_model = use_model self.use_model = use_model
self.name = sram.name
self.num_words = sram.num_words self.prepare_netlist()
self.word_size = sram.word_size
self.addr_size = sram.addr_size 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 # Set up to trim the netlist here if that is enabled
if OPTS.trim_netlist: if OPTS.trim_netlist:
@ -35,11 +43,13 @@ class lib:
# Make a copy in temp for debugging # Make a copy in temp for debugging
shutil.copy(self.sp_file, self.sim_sp_file) 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. """
# These are the parameters to determine the table sizes # These are the parameters to determine the table sizes
#self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8]) #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, 8])
#self.load_scales = np.array([0.25, 1]) #self.load_scales = np.array([0.25, 1])
self.load = tech.spice["FF_in_cap"] self.load = tech.spice["msflop_in_cap"]
self.loads = self.load_scales*self.load self.loads = self.load_scales*self.load
debug.info(1,"Loads: {0}".format(self.loads)) debug.info(1,"Loads: {0}".format(self.loads))
@ -50,8 +60,42 @@ class lib:
self.slews = self.slew_scales*self.slew self.slews = self.slew_scales*self.slew
debug.info(1,"Slews: {0}".format(self.slews)) debug.info(1,"Slews: {0}".format(self.slews))
debug.info(1,"Writing to {0}".format(libname))
self.lib = open(libname, "w") def create_corners(self):
""" Create corners for characterization. """
# Get the corners from the options file
self.temperatures = OPTS.temperatures
self.supply_voltages = OPTS.supply_voltages
self.process_corners = OPTS.process_corners
# Enumerate all possible corners
self.corners = []
self.lib_files = []
for proc in self.process_corners:
for temp in self.temperatures:
for volt in self.supply_voltages:
self.corner_name = "{0}_{1}_{2}_{3}".format(self.sram.name,
proc,
volt,
temp)
self.corner_name = self.corner_name.replace(".","") # Remove decimals
lib_name = self.out_dir+"{}.lib".format(self.corner_name)
# A corner is a tuple of PVT
self.corners.append((proc, volt, temp))
self.lib_files.append(lib_name)
def characterize_corners(self):
""" Characterize the list of corners. """
for (self.corner,lib_name) in zip(self.corners,self.lib_files):
debug.info(1,"Corner: " + str(self.corner))
(self.process, self.voltage, self.temperature) = self.corner
self.lib = open(lib_name, "w")
debug.info(1,"Writing to {0}".format(lib_name))
self.characterize()
def characterize(self):
""" Characterize the current corner. """
self.write_header() self.write_header()
@ -67,7 +111,7 @@ class lib:
def write_header(self): def write_header(self):
""" Write the header information """ """ Write the header information """
self.lib.write("library ({0}_lib)".format(self.name)) self.lib.write("library ({0}_lib)".format(self.corner_name))
self.lib.write("{\n") self.lib.write("{\n")
self.lib.write(" delay_model : \"table_lookup\";\n") self.lib.write(" delay_model : \"table_lookup\";\n")
@ -79,12 +123,12 @@ class lib:
self.write_bus() self.write_bus()
self.lib.write("cell ({0})".format(self.name)) self.lib.write("cell ({0})".format(self.sram.name))
self.lib.write("{\n") self.lib.write("{\n")
self.lib.write(" memory(){ \n") self.lib.write(" memory(){ \n")
self.lib.write(" type : ram;\n") self.lib.write(" type : ram;\n")
self.lib.write(" address_width : {0};\n".format(self.addr_size)) self.lib.write(" address_width : {0};\n".format(self.sram.addr_size))
self.lib.write(" word_width : {0};\n".format(self.word_size)) self.lib.write(" word_width : {0};\n".format(self.sram.word_size))
self.lib.write(" }\n") self.lib.write(" }\n")
self.lib.write(" interface_timing : true;\n") self.lib.write(" interface_timing : true;\n")
self.lib.write(" dont_use : true;\n") self.lib.write(" dont_use : true;\n")
@ -103,9 +147,9 @@ class lib:
self.lib.write(" capacitive_load_unit(1 ,fF) ;\n") self.lib.write(" capacitive_load_unit(1 ,fF) ;\n")
self.lib.write(" leakage_power_unit : \"1mW\" ;\n") self.lib.write(" leakage_power_unit : \"1mW\" ;\n")
self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n") self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n")
self.lib.write(" operating_conditions(TT){\n") self.lib.write(" operating_conditions({}){{\n".format(self.process))
self.lib.write(" voltage : {0} ;\n".format(tech.spice["supply_voltage"])) self.lib.write(" voltage : {} ;\n".format(self.voltage))
self.lib.write(" temperature : 25.000 ;\n") self.lib.write(" temperature : {};\n".format(self.temperature))
self.lib.write(" }\n\n") self.lib.write(" }\n\n")
def write_defaults(self): def write_defaults(self):
@ -206,17 +250,17 @@ class lib:
self.lib.write(" type (DATA){\n") self.lib.write(" type (DATA){\n")
self.lib.write(" base_type : array;\n") self.lib.write(" base_type : array;\n")
self.lib.write(" data_type : bit;\n") self.lib.write(" data_type : bit;\n")
self.lib.write(" bit_width : {0};\n".format(self.word_size)) self.lib.write(" bit_width : {0};\n".format(self.sram.word_size))
self.lib.write(" bit_from : 0;\n") self.lib.write(" bit_from : 0;\n")
self.lib.write(" bit_to : {0};\n".format(self.word_size - 1)) self.lib.write(" bit_to : {0};\n".format(self.sram.word_size - 1))
self.lib.write(" }\n\n") self.lib.write(" }\n\n")
self.lib.write(" type (ADDR){\n") self.lib.write(" type (ADDR){\n")
self.lib.write(" base_type : array;\n") self.lib.write(" base_type : array;\n")
self.lib.write(" data_type : bit;\n") self.lib.write(" data_type : bit;\n")
self.lib.write(" bit_width : {0};\n".format(self.addr_size)) self.lib.write(" bit_width : {0};\n".format(self.sram.addr_size))
self.lib.write(" bit_from : 0;\n") self.lib.write(" bit_from : 0;\n")
self.lib.write(" bit_to : {0};\n".format(self.addr_size - 1)) self.lib.write(" bit_to : {0};\n".format(self.sram.addr_size - 1))
self.lib.write(" }\n\n") self.lib.write(" }\n\n")
@ -260,7 +304,7 @@ class lib:
self.lib.write(" bus(DATA){\n") self.lib.write(" bus(DATA){\n")
self.lib.write(" bus_type : DATA; \n") self.lib.write(" bus_type : DATA; \n")
self.lib.write(" direction : inout; \n") self.lib.write(" direction : inout; \n")
self.lib.write(" max_capacitance : {0}; \n".format(8*tech.spice["FF_in_cap"])) self.lib.write(" max_capacitance : {0}; \n".format(8*tech.spice["msflop_in_cap"]))
self.lib.write(" three_state : \"!OEb & !clk\"; \n") self.lib.write(" three_state : \"!OEb & !clk\"; \n")
self.lib.write(" memory_write(){ \n") self.lib.write(" memory_write(){ \n")
self.lib.write(" address : ADDR; \n") self.lib.write(" address : ADDR; \n")
@ -269,7 +313,7 @@ class lib:
self.lib.write(" memory_read(){ \n") self.lib.write(" memory_read(){ \n")
self.lib.write(" address : ADDR; \n") self.lib.write(" address : ADDR; \n")
self.lib.write(" }\n") self.lib.write(" }\n")
self.lib.write(" pin(DATA[{0}:0])".format(self.word_size - 1)) self.lib.write(" pin(DATA[{0}:0])".format(self.sram.word_size - 1))
self.lib.write("{\n") self.lib.write("{\n")
self.lib.write(" internal_power(){\n") self.lib.write(" internal_power(){\n")
@ -324,10 +368,10 @@ class lib:
self.lib.write(" bus(ADDR){\n") self.lib.write(" bus(ADDR){\n")
self.lib.write(" bus_type : ADDR; \n") self.lib.write(" bus_type : ADDR; \n")
self.lib.write(" direction : input; \n") self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) self.lib.write(" capacitance : {0}; \n".format(tech.spice["msflop_in_cap"]))
self.lib.write(" max_transition : {0};\n".format(self.slews[-1])) self.lib.write(" max_transition : {0};\n".format(self.slews[-1]))
self.lib.write(" fanout_load : 1.000000;\n") self.lib.write(" fanout_load : 1.000000;\n")
self.lib.write(" pin(ADDR[{0}:0])".format(self.addr_size - 1)) self.lib.write(" pin(ADDR[{0}:0])".format(self.sram.addr_size - 1))
self.lib.write("{\n") self.lib.write("{\n")
self.write_FF_setuphold() self.write_FF_setuphold()
@ -343,7 +387,7 @@ class lib:
self.lib.write(" pin({0})".format(i)) self.lib.write(" pin({0})".format(i))
self.lib.write("{\n") self.lib.write("{\n")
self.lib.write(" direction : input; \n") self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) self.lib.write(" capacitance : {0}; \n".format(tech.spice["msflop_in_cap"]))
self.write_FF_setuphold() self.write_FF_setuphold()
self.lib.write(" }\n\n") self.lib.write(" }\n\n")
@ -356,7 +400,7 @@ class lib:
self.lib.write(" pin(clk){\n") self.lib.write(" pin(clk){\n")
self.lib.write(" clock : true;\n") self.lib.write(" clock : true;\n")
self.lib.write(" direction : input; \n") self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) 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_pulse_width = ch.round_time(self.delay["min_period"])/2.0
min_period = ch.round_time(self.delay["min_period"]) min_period = ch.round_time(self.delay["min_period"])
self.lib.write(" timing(){ \n") self.lib.write(" timing(){ \n")
@ -388,12 +432,12 @@ class lib:
try: try:
self.d self.d
except AttributeError: except AttributeError:
self.d = delay.delay(self.sram, self.sim_sp_file) self.d = delay.delay(self.sram, self.sim_sp_file, self.corner)
if self.use_model: if self.use_model:
self.delay = self.d.analytical_model(self.sram,self.slews,self.loads) self.delay = self.d.analytical_model(self.sram,self.slews,self.loads)
else: else:
probe_address = "1" * self.addr_size probe_address = "1" * self.sram.addr_size
probe_data = self.word_size - 1 probe_data = self.sram.word_size - 1
# We must trim based on a specific address and data bit # We must trim based on a specific address and data bit
if OPTS.trim_netlist: if OPTS.trim_netlist:
self.trimsp.trim(probe_address,probe_data) self.trimsp.trim(probe_address,probe_data)
@ -405,7 +449,7 @@ class lib:
try: try:
self.sh self.sh
except AttributeError: except AttributeError:
self.sh = setup_hold.setup_hold() self.sh = setup_hold.setup_hold(self.corner)
if self.use_model: if self.use_model:
self.times = self.sh.analytical_model(self.slews,self.loads) self.times = self.sh.analytical_model(self.slews,self.loads)
else: else:

View File

@ -13,19 +13,24 @@ class setup_hold():
(Bisection Methodology) (Bisection Methodology)
""" """
def __init__(self): def __init__(self, corner):
# This must match the spice model order # This must match the spice model order
self.pins = ["data", "dout", "dout_bar", "clk", "vdd", "gnd"] self.pins = ["data", "dout", "dout_bar", "clk", "vdd", "gnd"]
self.model_name = "ms_flop" self.model_name = "ms_flop"
self.model_location = OPTS.openram_tech + "sp_lib/ms_flop.sp" self.model_location = OPTS.openram_tech + "sp_lib/ms_flop.sp"
self.period = tech.spice["feasible_period"] self.period = tech.spice["feasible_period"]
self.vdd = tech.spice["supply_voltage"]
self.gnd = tech.spice["gnd_voltage"]
debug.info(2,"Feasible period from technology file: {0} ".format(self.period)) debug.info(2,"Feasible period from technology file: {0} ".format(self.period))
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 write_stimulus(self, mode, target_time, correct_value): def write_stimulus(self, mode, target_time, correct_value):
"""Creates a stimulus file for SRAM setup/hold time calculation""" """Creates a stimulus file for SRAM setup/hold time calculation"""
@ -33,13 +38,13 @@ class setup_hold():
# creates and opens the stimulus file for writing # creates and opens the stimulus file for writing
temp_stim = OPTS.openram_temp + "stim.sp" temp_stim = OPTS.openram_temp + "stim.sp"
self.sf = open(temp_stim, "w") self.sf = open(temp_stim, "w")
self.stim = stimuli.stimuli(self.sf, self.corner)
self.write_header(correct_value) self.write_header(correct_value)
# instantiate the master-slave d-flip-flop # instantiate the master-slave d-flip-flop
self.sf.write("\n* Instantiation of the Master-Slave D-flip-flop\n") self.sf.write("\n* Instantiation of the Master-Slave D-flip-flop\n")
stimuli.inst_model(stim_file=self.sf, self.stim.inst_model(pins=self.pins,
pins=self.pins,
model_name=self.model_name) model_name=self.model_name)
self.write_data(mode=mode, self.write_data(mode=mode,
@ -52,7 +57,7 @@ class setup_hold():
correct_value=correct_value) correct_value=correct_value)
stimuli.write_control(self.sf,4*self.period) self.stim.write_control(4*self.period)
self.sf.close() self.sf.close()
@ -61,13 +66,11 @@ class setup_hold():
self.sf.write("\n* Stimulus for setup/hold: data {0} period {1}n\n".format(correct_value, self.period)) self.sf.write("\n* Stimulus for setup/hold: data {0} period {1}n\n".format(correct_value, self.period))
# include files in stimulus file # include files in stimulus file
self.model_list = tech.spice["fet_models"] + [self.model_location] self.stim.write_include(self.model_location)
stimuli.write_include(stim_file=self.sf,
models=self.model_list)
# add vdd/gnd statements # add vdd/gnd statements
self.sf.write("\n* Global Power Supplies\n") self.sf.write("\n* Global Power Supplies\n")
stimuli.write_supply(self.sf) self.stim.write_supply()
def write_data(self, mode, target_time, correct_value): def write_data(self, mode, target_time, correct_value):
@ -77,7 +80,7 @@ class setup_hold():
""" """
self.sf.write("\n* Generation of the data and clk signals\n") self.sf.write("\n* Generation of the data and clk signals\n")
incorrect_value = stimuli.get_inverse_value(correct_value) incorrect_value = self.stim.get_inverse_value(correct_value)
if mode=="HOLD": if mode=="HOLD":
init_value = incorrect_value init_value = incorrect_value
start_value = correct_value start_value = correct_value
@ -87,8 +90,7 @@ class setup_hold():
start_value = incorrect_value start_value = incorrect_value
end_value = correct_value end_value = correct_value
stimuli.gen_pwl(stim_file=self.sf, self.stim.gen_pwl(sig_name="data",
sig_name="data",
clk_times=[0, self.period, target_time], clk_times=[0, self.period, target_time],
data_values=[init_value, start_value, end_value], data_values=[init_value, start_value, end_value],
period=target_time, period=target_time,
@ -99,8 +101,7 @@ class setup_hold():
""" Create the clock signal for setup/hold analysis. First period initializes the FF """ Create the clock signal for setup/hold analysis. First period initializes the FF
while the second is used for characterization.""" while the second is used for characterization."""
stimuli.gen_pwl(stim_file=self.sf, self.stim.gen_pwl(sig_name="clk",
sig_name="clk",
# initial clk edge is right after the 0 time to initialize a flop # initial clk edge is right after the 0 time to initialize a flop
# without using .IC on an internal node. # without using .IC on an internal node.
# Return input to value after one period. # Return input to value after one period.
@ -135,10 +136,9 @@ class setup_hold():
self.sf.write("\n* Measure statements for pass/fail verification\n") self.sf.write("\n* Measure statements for pass/fail verification\n")
trig_name = "clk" trig_name = "clk"
targ_name = "dout" targ_name = "dout"
trig_val = targ_val = 0.5 * self.vdd trig_val = targ_val = 0.5 * self.vdd_voltage
# Start triggers right before the clock edge at 2*period # Start triggers right before the clock edge at 2*period
stimuli.gen_meas_delay(stim_file=self.sf, self.stim.gen_meas_delay(meas_name="clk2q_delay",
meas_name="clk2q_delay",
trig_name=trig_name, trig_name=trig_name,
targ_name=targ_name, targ_name=targ_name,
trig_val=trig_val, trig_val=trig_val,
@ -151,8 +151,7 @@ class setup_hold():
targ_name = "data" targ_name = "data"
# Start triggers right after initialize value is returned to normal # Start triggers right after initialize value is returned to normal
# at one period # at one period
stimuli.gen_meas_delay(stim_file=self.sf, self.stim.gen_meas_delay(meas_name="setup_hold_time",
meas_name="setup_hold_time",
trig_name=trig_name, trig_name=trig_name,
targ_name=targ_name, targ_name=targ_name,
trig_val=trig_val, trig_val=trig_val,
@ -186,7 +185,7 @@ class setup_hold():
self.write_stimulus(mode=mode, self.write_stimulus(mode=mode,
target_time=feasible_bound, target_time=feasible_bound,
correct_value=correct_value) correct_value=correct_value)
stimuli.run_sim() self.stim.run_sim()
ideal_clk_to_q = ch.convert_to_float(ch.parse_output("timing", "clk2q_delay")) ideal_clk_to_q = ch.convert_to_float(ch.parse_output("timing", "clk2q_delay"))
setuphold_time = ch.convert_to_float(ch.parse_output("timing", "setup_hold_time")) setuphold_time = ch.convert_to_float(ch.parse_output("timing", "setup_hold_time"))
debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time)) debug.info(2,"*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, correct_value,ideal_clk_to_q,setuphold_time))
@ -219,7 +218,7 @@ class setup_hold():
feasible_bound)) feasible_bound))
stimuli.run_sim() self.stim.run_sim()
clk_to_q = ch.convert_to_float(ch.parse_output("timing", "clk2q_delay")) clk_to_q = ch.convert_to_float(ch.parse_output("timing", "clk2q_delay"))
setuphold_time = ch.convert_to_float(ch.parse_output("timing", "setup_hold_time")) setuphold_time = ch.convert_to_float(ch.parse_output("timing", "setup_hold_time"))
if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float: if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float:

View File

@ -12,121 +12,131 @@ import sys
import numpy as np import numpy as np
from globals import OPTS from globals import OPTS
vdd_voltage = tech.spice["supply_voltage"]
gnd_voltage = tech.spice["gnd_voltage"]
vdd_name = tech.spice["vdd_name"]
gnd_name = tech.spice["gnd_name"]
pmos_name = tech.spice["pmos_name"]
nmos_name = tech.spice["nmos_name"]
tx_width = tech.spice["minwidth_tx"]
tx_length = tech.spice["channel"]
def inst_sram(stim_file, abits, dbits, sram_name): class stimuli():
""" Class for providing stimuli functions """
def __init__(self, stim_file, corner):
self.vdd_name = tech.spice["vdd_name"]
self.gnd_name = tech.spice["gnd_name"]
self.pmos_name = tech.spice["pmos"]
self.nmos_name = tech.spice["nmos"]
self.tx_width = tech.spice["minwidth_tx"]
self.tx_length = tech.spice["channel"]
self.sf = stim_file
(self.process, self.vdd_voltage, self.temperature) = corner
self.gnd_voltage = 0
self.device_models = tech.spice["fet_models"][self.process]
def inst_sram(self, abits, dbits, sram_name):
""" Function to instatiate an SRAM subckt. """ """ Function to instatiate an SRAM subckt. """
stim_file.write("Xsram ") self.sf.write("Xsram ")
for i in range(dbits): for i in range(dbits):
stim_file.write("D[{0}] ".format(i)) self.sf.write("D[{0}] ".format(i))
for i in range(abits): for i in range(abits):
stim_file.write("A[{0}] ".format(i)) self.sf.write("A[{0}] ".format(i))
for i in tech.spice["control_signals"]: for i in tech.spice["control_signals"]:
stim_file.write("{0} ".format(i)) self.sf.write("{0} ".format(i))
stim_file.write("{0} ".format(tech.spice["clk"])) self.sf.write("{0} ".format(tech.spice["clk"]))
stim_file.write("{0} {1} ".format(vdd_name, gnd_name)) self.sf.write("{0} {1} ".format(self.vdd_name, self.gnd_name))
stim_file.write("{0}\n".format(sram_name)) self.sf.write("{0}\n".format(sram_name))
def inst_model(stim_file, pins, model_name): def inst_model(self, pins, model_name):
""" Function to instantiate a generic model with a set of pins """ """ Function to instantiate a generic model with a set of pins """
stim_file.write("X{0} ".format(model_name)) self.sf.write("X{0} ".format(model_name))
for pin in pins: for pin in pins:
stim_file.write("{0} ".format(pin)) self.sf.write("{0} ".format(pin))
stim_file.write("{0}\n".format(model_name)) self.sf.write("{0}\n".format(model_name))
def create_inverter(stim_file, size=1, beta=2.5): def create_inverter(self, 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)) self.sf.write(".SUBCKT test_inv in out {0} {1}\n".format(self.vdd_name, self.gnd_name))
stim_file.write("mpinv out in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name, self.sf.write("mpinv out in {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name,
pmos_name, self.pmos_name,
beta * size * tx_width, beta * size * self.tx_width,
tx_length)) self.tx_length))
stim_file.write("mninv out in {0} {0} {1} w={2}u l={3}u\n".format(gnd_name, self.sf.write("mninv out in {0} {0} {1} w={2}u l={3}u\n".format(self.gnd_name,
nmos_name, self.nmos_name,
size * tx_width, size * self.tx_width,
tx_length)) self.tx_length))
stim_file.write(".ENDS test_inv\n") self.sf.write(".ENDS test_inv\n")
def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5): def create_buffer(self, buffer_name, size=[1,3], beta=2.5):
""" """
Generates buffer for top level signals (only for sim Generates buffer for top level signals (only for sim
purposes). Size is pair for PMOS, NMOS width multiple. purposes). Size is pair for PMOS, NMOS width multiple.
""" """
stim_file.write(".SUBCKT test_{2} in out {0} {1}\n".format(vdd_name, self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
gnd_name, self.gnd_name,
buffer_name)) buffer_name))
stim_file.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name, self.sf.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name,
pmos_name, self.pmos_name,
beta * size[0] * tx_width, beta * size[0] * self.tx_width,
tx_length)) self.tx_length))
stim_file.write("mninv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(gnd_name, self.sf.write("mninv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(self.gnd_name,
nmos_name, self.nmos_name,
size[0] * tx_width, size[0] * self.tx_width,
tx_length)) self.tx_length))
stim_file.write("mpinv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(vdd_name, self.sf.write("mpinv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name,
pmos_name, self.pmos_name,
beta * size[1] * tx_width, beta * size[1] * self.tx_width,
tx_length)) self.tx_length))
stim_file.write("mninv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(gnd_name, self.sf.write("mninv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(self.gnd_name,
nmos_name, self.nmos_name,
size[1] * tx_width, size[1] * self.tx_width,
tx_length)) self.tx_length))
stim_file.write(".ENDS test_{0}\n\n".format(buffer_name)) self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
def inst_buffer(stim_file, buffer_name, signal_list): def inst_buffer(self, 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: for signal in signal_list:
stim_file.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal, self.sf.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal,
"test"+vdd_name, "test"+self.vdd_name,
"test"+gnd_name, "test"+self.gnd_name,
buffer_name)) buffer_name))
def inst_inverter(stim_file, signal_list): def inst_inverter(self, 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: for signal in signal_list:
stim_file.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal, self.sf.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal,
"test"+vdd_name, "test"+self.vdd_name,
"test"+gnd_name)) "test"+self.gnd_name))
def inst_accesstx(stim_file, dbits): def inst_accesstx(self, 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") self.sf.write("* Tx Pin-list: Drain Gate Source Body\n")
for i in range(dbits): 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" pmos_access_string="mp{0} DATA[{0}] acc_en D[{0}] {1} {2} w={3}u l={4}u\n"
stim_file.write(pmos_access_string.format(i, self.sf.write(pmos_access_string.format(i,
"test"+vdd_name, "test"+self.vdd_name,
pmos_name, self.pmos_name,
2 * tx_width, 2 * self.tx_width,
tx_length)) self.tx_length))
nmos_access_string="mn{0} DATA[{0}] acc_en_inv D[{0}] {1} {2} w={3}u l={4}u\n" nmos_access_string="mn{0} DATA[{0}] acc_en_inv D[{0}] {1} {2} w={3}u l={4}u\n"
stim_file.write(nmos_access_string.format(i, self.sf.write(nmos_access_string.format(i,
"test"+gnd_name, "test"+self.gnd_name,
nmos_name, self.nmos_name,
2 * tx_width, 2 * self.tx_width,
tx_length)) self.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): def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
""" """
Generates a periodic signal with 50% duty cycle and slew rates. Period is measured Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
from 50% to 50%. from 50% to 50%.
""" """
stim_file.write("* PULSE: period={0}\n".format(period)) self.sf.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" 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, self.sf.write(pulse_string.format(sig_name,
v1, v1,
v2, v2,
offset, offset,
@ -136,7 +146,7 @@ def gen_pulse(stim_file, sig_name, v1=gnd_voltage, v2=vdd_voltage, offset=0, per
period)) period))
def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup): def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup):
""" """
Generate a PWL stimulus given a signal name and data values at each period. 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 Automatically creates slews and ensures each data occurs a setup before the clock
@ -148,30 +158,30 @@ def gen_pwl(stim_file, sig_name, clk_times, data_values, period, slew, setup):
# shift signal times earlier for setup time # shift signal times earlier for setup time
times = np.array(clk_times) - setup*period times = np.array(clk_times) - setup*period
values = np.array(data_values) * vdd_voltage values = np.array(data_values) * self.vdd_voltage
half_slew = 0.5 * slew half_slew = 0.5 * slew
stim_file.write("* (time, data): {}\n".format(zip(clk_times, data_values))) self.sf.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])) self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
for i in range(1,len(times)): for i in range(1,len(times)):
stim_file.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew, self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,
values[i-1], values[i-1],
times[i]+half_slew, times[i]+half_slew,
values[i])) values[i]))
stim_file.write(")\n") self.sf.write(")\n")
def gen_constant(stim_file, sig_name, v_val): def gen_constant(self, 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)) self.sf.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val))
def get_inverse_voltage(value): def get_inverse_voltage(self, value):
if value > 0.5*vdd_voltage: if value > 0.5*self.vdd_voltage:
return gnd_voltage return self.gnd_voltage
elif value <= 0.5*vdd_voltage: elif value <= 0.5*self.vdd_voltage:
return vdd_voltage return self.vdd_voltage
else: else:
debug.error("Invalid value to get an inverse of: {0}".format(value)) debug.error("Invalid value to get an inverse of: {0}".format(value))
def get_inverse_value(value): def get_inverse_value(self, value):
if value > 0.5: if value > 0.5:
return 0 return 0
elif value <= 0.5: elif value <= 0.5:
@ -180,10 +190,10 @@ def get_inverse_value(value):
debug.error("Invalid value to get an inverse of: {0}".format(value)) debug.error("Invalid value to get an inverse of: {0}".format(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): def gen_meas_delay(self, 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" 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, self.sf.write(measure_string.format(meas_name,
trig_name, trig_name,
trig_val, trig_val,
trig_dir, trig_dir,
@ -193,65 +203,67 @@ def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_va
targ_dir, targ_dir,
targ_td)) targ_td))
def gen_meas_power(stim_file, meas_name, t_initial, t_final): def gen_meas_power(self, 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: # power mea cmd is different in different spice:
if OPTS.spice_name == "hspice": if OPTS.spice_name == "hspice":
power_exp = "power" power_exp = "power"
else: else:
power_exp = "par('(-1*v(" + str(vdd_name) + ")*I(v" + str(vdd_name) + "))')" power_exp = "par('(-1*v(" + str(self.vdd_name) + ")*I(v" + str(self.vdd_name) + "))')"
stim_file.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name, self.sf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name,
power_exp, power_exp,
t_initial, t_initial,
t_final)) t_final))
def write_control(stim_file, end_time): def write_control(self, end_time):
""" Write the control cards to run and end the simulation """ """ Write the control cards to run and end the simulation """
# UIC is needed for ngspice to converge # UIC is needed for ngspice to converge
stim_file.write(".TRAN 5p {0}n UIC\n".format(end_time)) self.sf.write(".TRAN 5p {0}n UIC\n".format(end_time))
if OPTS.spice_name == "ngspice": if OPTS.spice_name == "ngspice":
# ngspice sometimes has convergence problems if not using gear method # ngspice sometimes has convergence problems if not using gear method
# which is more accurate, but slower than the default trapezoid method # which is more accurate, but slower than the default trapezoid method
# Do not remove this or it may not converge due to some "pa_00" nodes # Do not remove this or it may not converge due to some "pa_00" nodes
# unless you figure out what these are. # unless you figure out what these are.
stim_file.write(".OPTIONS POST=1 RUNLVL=4 PROBE method=gear\n") self.sf.write(".OPTIONS POST=1 RUNLVL=4 PROBE method=gear TEMP={}\n".format(self.temperature))
else: else:
stim_file.write(".OPTIONS POST=1 RUNLVL=4 PROBE\n") self.sf.write(".OPTIONS POST=1 RUNLVL=4 PROBE TEMP={}\n".format(self.temperature))
# create plots for all signals # create plots for all signals
stim_file.write("* probe is used for hspice/xa, while plot is used in ngspice\n") self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
if OPTS.debug_level>0: if OPTS.debug_level>0:
if OPTS.spice_name in ["hspice","xa"]: if OPTS.spice_name in ["hspice","xa"]:
stim_file.write(".probe V(*)\n") self.sf.write(".probe V(*)\n")
else: else:
stim_file.write(".plot V(*)\n") self.sf.write(".plot V(*)\n")
else: else:
stim_file.write("*.probe V(*)\n") self.sf.write("*.probe V(*)\n")
stim_file.write("*.plot V(*)\n") self.sf.write("*.plot V(*)\n")
# end the stimulus file # end the stimulus file
stim_file.write(".end\n\n") self.sf.write(".end\n\n")
def write_include(stim_file, models): def write_include(self, circuit):
"""Writes include statements, inputs are lists of model files""" """Writes include statements, inputs are lists of model files"""
for item in list(models): includes = self.device_models + [circuit]
self.sf.write("* {} process corner\n".format(self.process))
for item in list(includes):
if os.path.isfile(item): if os.path.isfile(item):
stim_file.write(".include \"{0}\"\n".format(item)) self.sf.write(".include \"{0}\"\n".format(item))
else: else:
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item)) debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
def write_supply(stim_file): def write_supply(self):
""" Writes supply voltage statements """ """ Writes supply voltage statements """
stim_file.write("V{0} {0} 0.0 {1}\n".format(vdd_name, vdd_voltage)) self.sf.write("V{0} {0} 0.0 {1}\n".format(self.vdd_name, self.vdd_voltage))
stim_file.write("V{0} {0} 0.0 {1}\n".format(gnd_name, gnd_voltage)) self.sf.write("V{0} {0} 0.0 {1}\n".format(self.gnd_name, self.gnd_voltage))
# This is for the test power supply # This is for the test power supply
stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+vdd_name, vdd_voltage)) self.sf.write("V{0} {0} 0.0 {1}\n".format("test"+self.vdd_name, self.vdd_voltage))
stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+gnd_name, gnd_voltage)) self.sf.write("V{0} {0} 0.0 {1}\n".format("test"+self.gnd_name, self.gnd_voltage))
def run_sim(): def run_sim(self):
""" 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) temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
import datetime import datetime

View File

@ -3,6 +3,9 @@ num_words = 16
num_banks = 1 num_banks = 1
tech_name = "scn3me_subm" tech_name = "scn3me_subm"
supply_voltages = [ 5.0 ]
temperatures = [ 25.0 ]
output_path = "temp" output_path = "temp"
output_name = "sram_2_16_1_scn3me_subm" output_name = "sram_2_16_1_scn3me_subm"

View File

@ -45,6 +45,13 @@ class options(optparse.Values):
# Purge the temp directory after a successful run (doesn't purge on errors, anyhow) # Purge the temp directory after a successful run (doesn't purge on errors, anyhow)
purge_temp = True purge_temp = True
# These are the configuration parameters
rw_ports = 1
r_ports = 0
supply_voltages = [3]
temperatures = [25]
process_corners = ["TT"]
# These are the default modules that can be over-riden # These are the default modules that can be over-riden
decoder = "hierarchical_decoder" decoder = "hierarchical_decoder"

View File

@ -1040,8 +1040,7 @@ class sram(design.design):
# Characterize the design # Characterize the design
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
from characterizer import lib from characterizer import lib
libname = OPTS.output_path + self.name + ".lib" print("LIB: Characterizing... ")
print("LIB: Writing to {0}".format(libname))
if OPTS.analytical_delay: if OPTS.analytical_delay:
print("Using analytical delay models (no characterization)") print("Using analytical delay models (no characterization)")
else: else:
@ -1049,7 +1048,7 @@ class sram(design.design):
print("Performing simulation-based characterization with {}".format(OPTS.spice_name)) print("Performing simulation-based characterization with {}".format(OPTS.spice_name))
if OPTS.trim_netlist: if OPTS.trim_netlist:
print("Trimming netlist to speed up characterization.") print("Trimming netlist to speed up characterization.")
lib.lib(libname=libname,sram=self,sp_file=sp_file) lib.lib(out_dir=OPTS.output_path, sram=self, sp_file=sp_file)
print_time("Characterization", datetime.datetime.now(), start_time) print_time("Characterization", datetime.datetime.now(), start_time)
# Write the layout # Write the layout

View File

@ -27,7 +27,7 @@ class timing_sram_test(openram_test):
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
import sram import sram
import tech
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram.sram(word_size=OPTS.word_size, s = sram.sram(word_size=OPTS.word_size,
num_words=OPTS.num_words, num_words=OPTS.num_words,
@ -43,9 +43,9 @@ class timing_sram_test(openram_test):
probe_data = s.word_size - 1 probe_data = s.word_size - 1
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
d = delay.delay(s,tempspice) d = delay.delay(s,tempspice,tech.spice["nom_delay"])
import tech import tech
loads = [tech.spice["FF_in_cap"]*4] loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data,slews,loads) data = d.analyze(probe_address, probe_data,slews,loads)
#print data #print data

View File

@ -31,7 +31,7 @@ class timing_setup_test(openram_test):
import tech import tech
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
sh = setup_hold.setup_hold() sh = setup_hold.setup_hold(tech.spice["nom_corner"]))
data = sh.analyze(slews,slews) data = sh.analyze(slews,slews)
if OPTS.tech_name == "freepdk45": if OPTS.tech_name == "freepdk45":

View File

@ -27,7 +27,7 @@ class timing_sram_test(openram_test):
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
import sram import sram
import tech
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram.sram(word_size=OPTS.word_size, s = sram.sram(word_size=OPTS.word_size,
num_words=OPTS.num_words, num_words=OPTS.num_words,
@ -41,9 +41,9 @@ class timing_sram_test(openram_test):
probe_data = s.word_size - 1 probe_data = s.word_size - 1
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
d = delay.delay(s,tempspice) d = delay.delay(s,tempspice,tech.spice["nom_corner"])
import tech import tech
loads = [tech.spice["FF_in_cap"]*4] loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data,slews,loads) data = d.analyze(probe_address, probe_data,slews,loads)
#print data #print data

View File

@ -30,7 +30,7 @@ class timing_setup_test(openram_test):
import tech import tech
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
sh = setup_hold.setup_hold() sh = setup_hold.setup_hold(tech.spice["nom_corner"])
data = sh.analyze(slews,slews) data = sh.analyze(slews,slews)
if OPTS.tech_name == "freepdk45": if OPTS.tech_name == "freepdk45":

View File

@ -5,7 +5,7 @@ Check the .lib file for an SRAM
import unittest import unittest
from testutils import header,openram_test from testutils import header,openram_test
import sys,os import sys,os,re
sys.path.append(os.path.join(sys.path[0],"..")) sys.path.append(os.path.join(sys.path[0],".."))
import globals import globals
from globals import OPTS from globals import OPTS
@ -30,12 +30,18 @@ class lib_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)
filename = s.name + "_analytical.lib" lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=True)
libname = OPTS.openram_temp + filename
lib.lib(libname=libname,sram=s,sp_file=tempspice,use_model=True)
# let's diff the result with a golden model # get all of the .lib files generated
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename) files = os.listdir(OPTS.openram_temp)
nametest = re.compile("\.lib$", re.IGNORECASE)
lib_files = filter(nametest.search, files)
# and compare them with the golden model
for filename in lib_files:
newname = filename.replace(".lib","_analytical.lib")
libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname)
self.isapproxdiff(libname,golden,0.15) self.isapproxdiff(libname,golden,0.15)
globals.end_openram() globals.end_openram()

View File

@ -5,7 +5,7 @@ Check the .lib file for an SRAM
import unittest import unittest
from testutils import header,openram_test from testutils import header,openram_test
import sys,os import sys,os,re
sys.path.append(os.path.join(sys.path[0],"..")) sys.path.append(os.path.join(sys.path[0],".."))
import globals import globals
from globals import OPTS from globals import OPTS
@ -39,11 +39,16 @@ class lib_test(openram_test):
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)
filename = s.name + ".lib" lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
libname = OPTS.openram_temp + filename
lib.lib(libname=libname,sram=s,sp_file=tempspice,use_model=False)
# let's diff the result with a golden model # get all of the .lib files generated
files = os.listdir(OPTS.openram_temp)
nametest = re.compile("\.lib$", re.IGNORECASE)
lib_files = filter(nametest.search, files)
# and compare them with the golden model
for filename in lib_files:
libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename) golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
self.isapproxdiff(libname,golden,0.15) self.isapproxdiff(libname,golden,0.15)

View File

@ -237,26 +237,16 @@ drc["metal4_extend_via4"] = 0.07
spice = {} spice = {}
spice["nmos"] = "nmos_vtg" spice["nmos"] = "nmos_vtg"
spice["pmos"] = "pmos_vtg" spice["pmos"] = "pmos_vtg"
# This is a map of corners to model files
SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR") SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR")
spice["fet_models"] = [SPICE_MODEL_DIR+"/NMOS_VTG.inc", spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/NMOS_VTG.inc",SPICE_MODEL_DIR+"/PMOS_VTG.inc"]}
SPICE_MODEL_DIR+"/PMOS_VTG.inc"]
#spice stimulus related variables #spice stimulus related variables
spice["feasible_period"] = 5 # estimated feasible period in ns spice["feasible_period"] = 5 # estimated feasible period in ns
spice["supply_voltage"] = 1.0 #vdd in [Volts] spice["supply_voltage"] = 1.0 # ideal vdd in [Volts]
spice["gnd_voltage"] = 0.0 #gnd in [Volts] spice["rise_time"] = 0.005 # rise time in [Nano-seconds]
spice["rise_time"] = 0.005 #rise time in [Nano-seconds] spice["fall_time"] = 0.005 # fall time in [Nano-seconds]
spice["fall_time"] = 0.005 #fall time in [Nano-seconds] spice["nom_corner"] = ("TT", 1.0, 25) # Nominal process corner
spice["temp"] = 25 #temperature in [Celsius]
#parasitics of metal for bit/word lines
spice["bitline_res"] = 0.1 #bitline resistance in [Ohms/micro-meter]
spice["bitline_cap"] = 0.2 #bitline capacitance in [Femto-farad/micro-meter]
spice["wordline_res"] = 0.1 #wordline resistance in [Ohms/micro-meter]
spice["wordline_cap"] = 0.2 #wordline capacitance in [Femto-farad/micro-meter]
spice["FF_in_cap"] = 0.2091 #Input capacitance of ms_flop (Din) [Femto-farad]
spice["tri_gate_out_cap"] = 0.41256 #Output capacitance of tri_gate (tri_out) [Femto-farad]
#sram signal names #sram signal names
spice["vdd_name"] = "vdd" spice["vdd_name"] = "vdd"
@ -264,8 +254,6 @@ spice["gnd_name"] = "gnd"
spice["control_signals"] = ["CSb", "WEb", "OEb"] spice["control_signals"] = ["CSb", "WEb", "OEb"]
spice["data_name"] = "DATA" spice["data_name"] = "DATA"
spice["addr_name"] = "ADDR" spice["addr_name"] = "ADDR"
spice["pmos_name"] = spice["pmos"]
spice["nmos_name"] = spice["nmos"]
spice["minwidth_tx"] = drc["minwidth_tx"] spice["minwidth_tx"] = drc["minwidth_tx"]
spice["channel"] = drc["minlength_channel"] spice["channel"] = drc["minlength_channel"]
spice["clk"] = "clk" spice["clk"] = "clk"
@ -280,6 +268,7 @@ spice["msflop_setup"] = 9 # DFF setup time in ps
spice["msflop_hold"] = 1 # DFF hold time in ps spice["msflop_hold"] = 1 # DFF hold time in ps
spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps
spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load
spice["msflop_in_cap"] = 0.2091 # Input capacitance of ms_flop (Din) [Femto-farad]
################################################### ###################################################

View File

@ -197,24 +197,16 @@ drc["minarea_metal3"] = 0
spice={} spice={}
spice["nmos"]="n" spice["nmos"]="n"
spice["pmos"]="p" spice["pmos"]="p"
spice["fet_models"] = [os.environ.get("SPICE_MODEL_DIR")+"/on_c5n.sp"] # This is a map of corners to model files
SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR")
spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/on_c5n.sp"] }
#spice stimulus related variables #spice stimulus related variables
spice["feasible_period"] = 5 # estimated feasible period in ns spice["feasible_period"] = 5 # estimated feasible period in ns
spice["supply_voltage"] = 5.0 #vdd in [Volts] spice["supply_voltage"] = 5.0 # ideal vdd in [Volts]
spice["gnd_voltage"] = 0.0 #gnd in [Volts] spice["rise_time"] = 0.05 # rise time in [Nano-seconds]
spice["rise_time"] = 0.05 #rise time in [Nano-seconds] spice["fall_time"] = 0.05 # fall time in [Nano-seconds]
spice["fall_time"] = 0.05 #fall time in [Nano-seconds] spice["nom_corner"] = ("TT", 5.0, 25) # Nominal process corner
spice["temp"] = 25 #temperature in [Celsius]
#parasitics of metal for bit/word lines
spice["bitline_res"] = 0.1 #bitline resistance in [Ohms/micro-meter]
spice["bitline_cap"] = 0.2 #bitline capacitance in [Femto-farad/micro-meter]
spice["wordline_res"] = 0.1 #wordline resistance in [Ohms/micro-meter]
spice["wordline_cap"] = 0.2 #wordline capacitance in [Femto-farad/micro-meter]
spice["FF_in_cap"] = 9.8242 #Input capacitance of ms_flop (Din) [Femto-farad]
spice["tri_gate_out_cap"] = 1.4980 #Output capacitance of tri_gate (tri_out) [Femto-farad]
#sram signal names #sram signal names
spice["vdd_name"] = "vdd" spice["vdd_name"] = "vdd"
@ -222,8 +214,6 @@ spice["gnd_name"] = "gnd"
spice["control_signals"] = ["CSb", "WEb", "OEb"] spice["control_signals"] = ["CSb", "WEb", "OEb"]
spice["data_name"] = "DATA" spice["data_name"] = "DATA"
spice["addr_name"] = "ADDR" spice["addr_name"] = "ADDR"
spice["pmos_name"] = spice["pmos"]
spice["nmos_name"] = spice["nmos"]
spice["minwidth_tx"] = drc["minwidth_tx"] spice["minwidth_tx"] = drc["minwidth_tx"]
spice["channel"] = drc["minlength_channel"] spice["channel"] = drc["minlength_channel"]
spice["clk"] = "clk" spice["clk"] = "clk"
@ -239,6 +229,7 @@ spice["msflop_setup"] = 9 # DFF setup time in ps
spice["msflop_hold"] = 1 # DFF hold time in ps spice["msflop_hold"] = 1 # DFF hold time in ps
spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps
spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load
spice["msflop_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad]
################################################### ###################################################