mirror of https://github.com/VLSIDA/OpenRAM.git
Begin modifications for corner-based characterization. Made stimuli.py a class. Golden output files are not updated.
This commit is contained in:
parent
b7be042c7f
commit
f86985821a
|
|
@ -14,16 +14,21 @@ class delay():
|
|||
data bit.
|
||||
"""
|
||||
|
||||
def __init__(self,sram,spfile):
|
||||
def __init__(self,sram,spfile, corner):
|
||||
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.vdd = tech.spice["supply_voltage"]
|
||||
self.gnd = tech.spice["gnd_voltage"]
|
||||
|
||||
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 check_arguments(self):
|
||||
"""Checks if arguments given for write_stimulus() meets requirements"""
|
||||
|
|
@ -53,22 +58,20 @@ class delay():
|
|||
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
|
||||
model_list = tech.spice["fet_models"] + [self.sram_sp_file]
|
||||
stimuli.write_include(stim_file=self.sf, models=model_list)
|
||||
self.stim.write_include(self.sram_sp_file)
|
||||
|
||||
# add vdd/gnd statements
|
||||
|
||||
self.sf.write("\n* Global Power Supplies\n")
|
||||
stimuli.write_supply(self.sf)
|
||||
self.stim.write_supply()
|
||||
|
||||
# instantiate the sram
|
||||
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.stim.inst_sram(abits=self.addr_size,
|
||||
dbits=self.word_size,
|
||||
sram_name=self.name)
|
||||
|
||||
self.sf.write("\n* SRAM output loads\n")
|
||||
for i in range(self.word_size):
|
||||
|
|
@ -76,7 +79,7 @@ class delay():
|
|||
|
||||
# add access transistors for data-bus
|
||||
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
|
||||
self.sf.write("\n* Generation of data and address signals\n")
|
||||
|
|
@ -87,14 +90,13 @@ class delay():
|
|||
period=period,
|
||||
slew=slew)
|
||||
else:
|
||||
stimuli.gen_constant(stim_file=self.sf,
|
||||
sig_name="d[{0}]".format(i),
|
||||
v_val=self.gnd)
|
||||
self.stim.gen_constant(sig_name="d[{0}]".format(i),
|
||||
v_val=self.gnd_voltage)
|
||||
|
||||
self.gen_addr(clk_times=self.cycle_times,
|
||||
addr=self.probe_address,
|
||||
period=period,
|
||||
slew=slew)
|
||||
addr=self.probe_address,
|
||||
period=period,
|
||||
slew=slew)
|
||||
|
||||
# generate control signals
|
||||
self.sf.write("\n* Generation of control signals\n")
|
||||
|
|
@ -103,19 +105,18 @@ class delay():
|
|||
self.gen_oeb(self.cycle_times, period, slew)
|
||||
|
||||
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)
|
||||
self.stim.gen_pulse(sig_name="CLK",
|
||||
v1=self.gnd_voltage,
|
||||
v2=self.vdd_voltage,
|
||||
offset=period,
|
||||
period=period,
|
||||
t_rise=slew,
|
||||
t_fall=slew)
|
||||
|
||||
self.write_measures(period)
|
||||
|
||||
# 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()
|
||||
|
||||
|
|
@ -134,81 +135,73 @@ class delay():
|
|||
# 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
|
||||
trig_val = targ_val = 0.5 * self.vdd_voltage
|
||||
|
||||
# Delay the target to measure after the negative edge
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="DELAY0",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir="FALL",
|
||||
targ_dir="FALL",
|
||||
trig_td=self.cycle_times[self.read0_cycle],
|
||||
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
|
||||
self.stim.gen_meas_delay(meas_name="DELAY0",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir="FALL",
|
||||
targ_dir="FALL",
|
||||
trig_td=self.cycle_times[self.read0_cycle],
|
||||
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
|
||||
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="DELAY1",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir="FALL",
|
||||
targ_dir="RISE",
|
||||
trig_td=self.cycle_times[self.read1_cycle],
|
||||
targ_td=self.cycle_times[self.read1_cycle]+0.5*period)
|
||||
self.stim.gen_meas_delay(meas_name="DELAY1",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir="FALL",
|
||||
targ_dir="RISE",
|
||||
trig_td=self.cycle_times[self.read1_cycle],
|
||||
targ_td=self.cycle_times[self.read1_cycle]+0.5*period)
|
||||
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="SLEW0",
|
||||
trig_name=targ_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=0.9*self.vdd,
|
||||
targ_val=0.1*self.vdd,
|
||||
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)
|
||||
self.stim.gen_meas_delay(meas_name="SLEW0",
|
||||
trig_name=targ_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=0.9*self.vdd_voltage,
|
||||
targ_val=0.1*self.vdd_voltage,
|
||||
trig_dir="FALL",
|
||||
targ_dir="FALL",
|
||||
trig_td=self.cycle_times[self.read0_cycle],
|
||||
targ_td=self.cycle_times[self.read0_cycle]+0.5*period)
|
||||
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="SLEW1",
|
||||
trig_name=targ_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=0.1*self.vdd,
|
||||
targ_val=0.9*self.vdd,
|
||||
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)
|
||||
self.stim.gen_meas_delay(meas_name="SLEW1",
|
||||
trig_name=targ_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=0.1*self.vdd_voltage,
|
||||
targ_val=0.9*self.vdd_voltage,
|
||||
trig_dir="RISE",
|
||||
targ_dir="RISE",
|
||||
trig_td=self.cycle_times[self.read1_cycle],
|
||||
targ_td=self.cycle_times[self.read1_cycle]+0.5*period)
|
||||
|
||||
# add measure statements for power
|
||||
t_initial = self.cycle_times[self.write0_cycle]
|
||||
t_final = self.cycle_times[self.write0_cycle+1]
|
||||
stimuli.gen_meas_power(stim_file=self.sf,
|
||||
meas_name="WRITE0_POWER",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
self.stim.gen_meas_power(meas_name="WRITE0_POWER",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
|
||||
t_initial = self.cycle_times[self.write1_cycle]
|
||||
t_final = self.cycle_times[self.write1_cycle+1]
|
||||
stimuli.gen_meas_power(stim_file=self.sf,
|
||||
meas_name="WRITE1_POWER",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
self.stim.gen_meas_power(meas_name="WRITE1_POWER",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
|
||||
t_initial = self.cycle_times[self.read0_cycle]
|
||||
t_final = self.cycle_times[self.read0_cycle+1]
|
||||
stimuli.gen_meas_power(stim_file=self.sf,
|
||||
meas_name="READ0_POWER",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
self.stim.gen_meas_power(meas_name="READ0_POWER",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
|
||||
t_initial = self.cycle_times[self.read1_cycle]
|
||||
t_final = self.cycle_times[self.read1_cycle+1]
|
||||
stimuli.gen_meas_power(stim_file=self.sf,
|
||||
meas_name="READ1_POWER",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
self.stim.gen_meas_power(meas_name="READ1_POWER",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
|
||||
def find_feasible_period(self, load, slew):
|
||||
"""
|
||||
|
|
@ -249,7 +242,7 @@ class delay():
|
|||
|
||||
# Checking from not data_value to data_value
|
||||
self.write_stimulus(period, load, slew)
|
||||
stimuli.run_sim()
|
||||
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"))
|
||||
|
|
@ -334,7 +327,7 @@ class delay():
|
|||
|
||||
# Checking from not data_value to data_value
|
||||
self.write_stimulus(period,load,slew)
|
||||
stimuli.run_sim()
|
||||
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"))
|
||||
|
|
@ -575,7 +568,7 @@ class delay():
|
|||
# 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 ]
|
||||
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):
|
||||
"""
|
||||
|
|
@ -589,9 +582,9 @@ class delay():
|
|||
for i in range(len(addr)):
|
||||
sig_name = "A[{0}]".format(i)
|
||||
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:
|
||||
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):
|
||||
|
|
@ -599,24 +592,24 @@ class delay():
|
|||
# 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)
|
||||
self.stim.gen_pwl("csb", clk_times, values, period, slew, 0.05)
|
||||
|
||||
def gen_web(self, clk_times, period, slew):
|
||||
""" 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]
|
||||
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
|
||||
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]
|
||||
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):
|
||||
""" 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]
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -12,15 +12,23 @@ from globals import OPTS
|
|||
class lib:
|
||||
""" 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.sp_file = sp_file
|
||||
self.use_model = use_model
|
||||
self.name = sram.name
|
||||
self.num_words = sram.num_words
|
||||
self.word_size = sram.word_size
|
||||
self.addr_size = sram.addr_size
|
||||
|
||||
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)
|
||||
|
|
@ -34,12 +42,14 @@ class lib:
|
|||
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. """
|
||||
# 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.25, 1, 8])
|
||||
#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
|
||||
debug.info(1,"Loads: {0}".format(self.loads))
|
||||
|
||||
|
|
@ -49,9 +59,43 @@ class lib:
|
|||
self.slew = tech.spice["rise_time"]
|
||||
self.slews = self.slew_scales*self.slew
|
||||
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()
|
||||
|
||||
|
|
@ -67,7 +111,7 @@ class lib:
|
|||
|
||||
def write_header(self):
|
||||
""" 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(" delay_model : \"table_lookup\";\n")
|
||||
|
||||
|
|
@ -79,12 +123,12 @@ class lib:
|
|||
|
||||
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(" memory(){ \n")
|
||||
self.lib.write(" type : ram;\n")
|
||||
self.lib.write(" address_width : {0};\n".format(self.addr_size))
|
||||
self.lib.write(" word_width : {0};\n".format(self.word_size))
|
||||
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(" }\n")
|
||||
self.lib.write(" interface_timing : 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(" leakage_power_unit : \"1mW\" ;\n")
|
||||
self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n")
|
||||
self.lib.write(" operating_conditions(TT){\n")
|
||||
self.lib.write(" voltage : {0} ;\n".format(tech.spice["supply_voltage"]))
|
||||
self.lib.write(" temperature : 25.000 ;\n")
|
||||
self.lib.write(" operating_conditions({}){{\n".format(self.process))
|
||||
self.lib.write(" voltage : {} ;\n".format(self.voltage))
|
||||
self.lib.write(" temperature : {};\n".format(self.temperature))
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
def write_defaults(self):
|
||||
|
|
@ -206,17 +250,17 @@ class lib:
|
|||
self.lib.write(" type (DATA){\n")
|
||||
self.lib.write(" base_type : array;\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_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(" type (ADDR){\n")
|
||||
self.lib.write(" base_type : array;\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_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")
|
||||
|
||||
|
||||
|
|
@ -260,7 +304,7 @@ class lib:
|
|||
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["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(" memory_write(){ \n")
|
||||
self.lib.write(" address : ADDR; \n")
|
||||
|
|
@ -269,7 +313,7 @@ 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.word_size - 1))
|
||||
self.lib.write(" pin(DATA[{0}:0])".format(self.sram.word_size - 1))
|
||||
self.lib.write("{\n")
|
||||
|
||||
self.lib.write(" internal_power(){\n")
|
||||
|
|
@ -324,10 +368,10 @@ 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["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(" 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.write_FF_setuphold()
|
||||
|
|
@ -343,7 +387,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["FF_in_cap"]))
|
||||
self.lib.write(" capacitance : {0}; \n".format(tech.spice["msflop_in_cap"]))
|
||||
self.write_FF_setuphold()
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
|
|
@ -356,7 +400,7 @@ class lib:
|
|||
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["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_period = ch.round_time(self.delay["min_period"])
|
||||
self.lib.write(" timing(){ \n")
|
||||
|
|
@ -388,12 +432,12 @@ class lib:
|
|||
try:
|
||||
self.d
|
||||
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:
|
||||
self.delay = self.d.analytical_model(self.sram,self.slews,self.loads)
|
||||
else:
|
||||
probe_address = "1" * self.addr_size
|
||||
probe_data = self.word_size - 1
|
||||
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)
|
||||
|
|
@ -405,7 +449,7 @@ class lib:
|
|||
try:
|
||||
self.sh
|
||||
except AttributeError:
|
||||
self.sh = setup_hold.setup_hold()
|
||||
self.sh = setup_hold.setup_hold(self.corner)
|
||||
if self.use_model:
|
||||
self.times = self.sh.analytical_model(self.slews,self.loads)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -13,18 +13,23 @@ class setup_hold():
|
|||
(Bisection Methodology)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, corner):
|
||||
# This must match the spice model order
|
||||
self.pins = ["data", "dout", "dout_bar", "clk", "vdd", "gnd"]
|
||||
self.model_name = "ms_flop"
|
||||
self.model_location = OPTS.openram_tech + "sp_lib/ms_flop.sp"
|
||||
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))
|
||||
|
||||
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):
|
||||
|
|
@ -33,14 +38,14 @@ class setup_hold():
|
|||
# creates and opens the stimulus file for writing
|
||||
temp_stim = OPTS.openram_temp + "stim.sp"
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.stim = stimuli.stimuli(self.sf, self.corner)
|
||||
|
||||
self.write_header(correct_value)
|
||||
|
||||
# instantiate the master-slave d-flip-flop
|
||||
self.sf.write("\n* Instantiation of the Master-Slave D-flip-flop\n")
|
||||
stimuli.inst_model(stim_file=self.sf,
|
||||
pins=self.pins,
|
||||
model_name=self.model_name)
|
||||
self.stim.inst_model(pins=self.pins,
|
||||
model_name=self.model_name)
|
||||
|
||||
self.write_data(mode=mode,
|
||||
target_time=target_time,
|
||||
|
|
@ -52,7 +57,7 @@ class setup_hold():
|
|||
correct_value=correct_value)
|
||||
|
||||
|
||||
stimuli.write_control(self.sf,4*self.period)
|
||||
self.stim.write_control(4*self.period)
|
||||
|
||||
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))
|
||||
|
||||
# include files in stimulus file
|
||||
self.model_list = tech.spice["fet_models"] + [self.model_location]
|
||||
stimuli.write_include(stim_file=self.sf,
|
||||
models=self.model_list)
|
||||
self.stim.write_include(self.model_location)
|
||||
|
||||
# add vdd/gnd statements
|
||||
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):
|
||||
|
|
@ -77,7 +80,7 @@ class setup_hold():
|
|||
|
||||
"""
|
||||
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":
|
||||
init_value = incorrect_value
|
||||
start_value = correct_value
|
||||
|
|
@ -87,29 +90,27 @@ class setup_hold():
|
|||
start_value = incorrect_value
|
||||
end_value = correct_value
|
||||
|
||||
stimuli.gen_pwl(stim_file=self.sf,
|
||||
sig_name="data",
|
||||
clk_times=[0, self.period, target_time],
|
||||
data_values=[init_value, start_value, end_value],
|
||||
period=target_time,
|
||||
slew=self.constrained_input_slew,
|
||||
setup=0)
|
||||
self.stim.gen_pwl(sig_name="data",
|
||||
clk_times=[0, self.period, target_time],
|
||||
data_values=[init_value, start_value, end_value],
|
||||
period=target_time,
|
||||
slew=self.constrained_input_slew,
|
||||
setup=0)
|
||||
|
||||
def write_clock(self):
|
||||
""" Create the clock signal for setup/hold analysis. First period initializes the FF
|
||||
while the second is used for characterization."""
|
||||
|
||||
stimuli.gen_pwl(stim_file=self.sf,
|
||||
sig_name="clk",
|
||||
# initial clk edge is right after the 0 time to initialize a flop
|
||||
# without using .IC on an internal node.
|
||||
# Return input to value after one period.
|
||||
# The second pulse is the characterization one at 2*period
|
||||
clk_times=[0, 0.1*self.period,self.period,2*self.period],
|
||||
data_values=[0, 1, 0, 1],
|
||||
period=2*self.period,
|
||||
slew=self.constrained_input_slew,
|
||||
setup=0)
|
||||
self.stim.gen_pwl(sig_name="clk",
|
||||
# initial clk edge is right after the 0 time to initialize a flop
|
||||
# without using .IC on an internal node.
|
||||
# Return input to value after one period.
|
||||
# The second pulse is the characterization one at 2*period
|
||||
clk_times=[0, 0.1*self.period,self.period,2*self.period],
|
||||
data_values=[0, 1, 0, 1],
|
||||
period=2*self.period,
|
||||
slew=self.constrained_input_slew,
|
||||
setup=0)
|
||||
|
||||
|
||||
|
||||
|
|
@ -135,32 +136,30 @@ class setup_hold():
|
|||
self.sf.write("\n* Measure statements for pass/fail verification\n")
|
||||
trig_name = "clk"
|
||||
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
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="clk2q_delay",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir="RISE",
|
||||
targ_dir=dout_rise_or_fall,
|
||||
trig_td=1.9*self.period,
|
||||
targ_td=1.9*self.period)
|
||||
|
||||
self.stim.gen_meas_delay(meas_name="clk2q_delay",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir="RISE",
|
||||
targ_dir=dout_rise_or_fall,
|
||||
trig_td=1.9*self.period,
|
||||
targ_td=1.9*self.period)
|
||||
|
||||
targ_name = "data"
|
||||
# Start triggers right after initialize value is returned to normal
|
||||
# at one period
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="setup_hold_time",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir="RISE",
|
||||
targ_dir=din_rise_or_fall,
|
||||
trig_td=1.2*self.period,
|
||||
targ_td=1.2*self.period)
|
||||
self.stim.gen_meas_delay(meas_name="setup_hold_time",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir="RISE",
|
||||
targ_dir=din_rise_or_fall,
|
||||
trig_td=1.2*self.period,
|
||||
targ_td=1.2*self.period)
|
||||
|
||||
|
||||
|
||||
|
|
@ -186,7 +185,7 @@ class setup_hold():
|
|||
self.write_stimulus(mode=mode,
|
||||
target_time=feasible_bound,
|
||||
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"))
|
||||
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))
|
||||
|
|
@ -219,7 +218,7 @@ class setup_hold():
|
|||
feasible_bound))
|
||||
|
||||
|
||||
stimuli.run_sim()
|
||||
self.stim.run_sim()
|
||||
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"))
|
||||
if type(clk_to_q)==float and (clk_to_q<1.1*ideal_clk_to_q) and type(setuphold_time)==float:
|
||||
|
|
|
|||
|
|
@ -12,290 +12,302 @@ import sys
|
|||
import numpy as np
|
||||
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):
|
||||
""" Function to instatiate an SRAM subckt. """
|
||||
stim_file.write("Xsram ")
|
||||
for i in range(dbits):
|
||||
stim_file.write("D[{0}] ".format(i))
|
||||
for i in range(abits):
|
||||
stim_file.write("A[{0}] ".format(i))
|
||||
for i in tech.spice["control_signals"]:
|
||||
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".format(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"]
|
||||
|
||||
def inst_model(stim_file, pins, model_name):
|
||||
""" 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))
|
||||
stim_file.write("{0}\n".format(model_name))
|
||||
|
||||
|
||||
def create_inverter(stim_file, size=1, beta=2.5):
|
||||
""" 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,
|
||||
beta * size * tx_width,
|
||||
tx_length))
|
||||
stim_file.write("mninv out in {0} {0} {1} w={2}u l={3}u\n".format(gnd_name,
|
||||
nmos_name,
|
||||
size * tx_width,
|
||||
tx_length))
|
||||
stim_file.write(".ENDS test_inv\n")
|
||||
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
stim_file.write(".SUBCKT test_{2} in out {0} {1}\n".format(vdd_name,
|
||||
gnd_name,
|
||||
buffer_name))
|
||||
stim_file.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name,
|
||||
pmos_name,
|
||||
beta * size[0] * tx_width,
|
||||
tx_length))
|
||||
stim_file.write("mninv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(gnd_name,
|
||||
nmos_name,
|
||||
size[0] * tx_width,
|
||||
tx_length))
|
||||
stim_file.write("mpinv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(vdd_name,
|
||||
pmos_name,
|
||||
beta * size[1] * tx_width,
|
||||
tx_length))
|
||||
stim_file.write("mninv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(gnd_name,
|
||||
nmos_name,
|
||||
size[1] * tx_width,
|
||||
tx_length))
|
||||
stim_file.write(".ENDS test_{0}\n\n".format(buffer_name))
|
||||
|
||||
|
||||
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) """
|
||||
for signal in signal_list:
|
||||
stim_file.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal,
|
||||
"test"+vdd_name,
|
||||
"test"+gnd_name,
|
||||
buffer_name))
|
||||
|
||||
|
||||
def inst_inverter(stim_file, signal_list):
|
||||
""" 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,
|
||||
"test"+gnd_name))
|
||||
|
||||
|
||||
def inst_accesstx(stim_file, dbits):
|
||||
""" 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"
|
||||
stim_file.write(pmos_access_string.format(i,
|
||||
"test"+vdd_name,
|
||||
pmos_name,
|
||||
2 * tx_width,
|
||||
tx_length))
|
||||
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,
|
||||
"test"+gnd_name,
|
||||
nmos_name,
|
||||
2 * tx_width,
|
||||
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%.
|
||||
"""
|
||||
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,
|
||||
v2,
|
||||
offset,
|
||||
t_rise,
|
||||
t_fall,
|
||||
0.5*period-0.5*t_rise-0.5*t_fall,
|
||||
period))
|
||||
|
||||
|
||||
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 first clk_time should be 0 and is the initial time that corresponds
|
||||
to the initial value.
|
||||
"""
|
||||
# the initial value is not a clock time
|
||||
debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match.")
|
||||
|
||||
# shift signal times earlier for setup time
|
||||
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)):
|
||||
stim_file.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,
|
||||
values[i-1],
|
||||
times[i]+half_slew,
|
||||
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 """
|
||||
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
|
||||
elif value <= 0.5*vdd_voltage:
|
||||
return vdd_voltage
|
||||
else:
|
||||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
||||
|
||||
def get_inverse_value(value):
|
||||
if value > 0.5:
|
||||
return 0
|
||||
elif value <= 0.5:
|
||||
return 1
|
||||
else:
|
||||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
||||
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 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 """
|
||||
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,
|
||||
trig_val,
|
||||
trig_dir,
|
||||
trig_td,
|
||||
targ_name,
|
||||
targ_val,
|
||||
targ_dir,
|
||||
targ_td))
|
||||
|
||||
def gen_meas_power(stim_file, meas_name, t_initial, t_final):
|
||||
""" 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"
|
||||
else:
|
||||
power_exp = "par('(-1*v(" + str(vdd_name) + ")*I(v" + str(vdd_name) + "))')"
|
||||
stim_file.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name,
|
||||
power_exp,
|
||||
t_initial,
|
||||
t_final))
|
||||
|
||||
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":
|
||||
# ngspice sometimes has convergence problems if not using gear 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
|
||||
# unless you figure out what these are.
|
||||
stim_file.write(".OPTIONS POST=1 RUNLVL=4 PROBE method=gear\n")
|
||||
else:
|
||||
stim_file.write(".OPTIONS POST=1 RUNLVL=4 PROBE\n")
|
||||
|
||||
# create plots for all signals
|
||||
stim_file.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
||||
if OPTS.debug_level>0:
|
||||
if OPTS.spice_name in ["hspice","xa"]:
|
||||
stim_file.write(".probe V(*)\n")
|
||||
else:
|
||||
stim_file.write(".plot V(*)\n")
|
||||
else:
|
||||
stim_file.write("*.probe V(*)\n")
|
||||
stim_file.write("*.plot V(*)\n")
|
||||
|
||||
# end the stimulus file
|
||||
stim_file.write(".end\n\n")
|
||||
def inst_sram(self, abits, dbits, sram_name):
|
||||
""" Function to instatiate an SRAM subckt. """
|
||||
self.sf.write("Xsram ")
|
||||
for i in range(dbits):
|
||||
self.sf.write("D[{0}] ".format(i))
|
||||
for i in range(abits):
|
||||
self.sf.write("A[{0}] ".format(i))
|
||||
for i in tech.spice["control_signals"]:
|
||||
self.sf.write("{0} ".format(i))
|
||||
self.sf.write("{0} ".format(tech.spice["clk"]))
|
||||
self.sf.write("{0} {1} ".format(self.vdd_name, self.gnd_name))
|
||||
self.sf.write("{0}\n".format(sram_name))
|
||||
|
||||
|
||||
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".format(item))
|
||||
else:
|
||||
debug.error("Could not find spice model: {0}\nSet SPICE_MODEL_DIR to over-ride path.\n".format(item))
|
||||
def inst_model(self, pins, model_name):
|
||||
""" Function to instantiate a generic model with a set of pins """
|
||||
self.sf.write("X{0} ".format(model_name))
|
||||
for pin in pins:
|
||||
self.sf.write("{0} ".format(pin))
|
||||
self.sf.write("{0}\n".format(model_name))
|
||||
|
||||
|
||||
def write_supply(stim_file):
|
||||
""" 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".format("test"+gnd_name, gnd_voltage))
|
||||
def create_inverter(self, size=1, beta=2.5):
|
||||
""" Generates inverter for the top level signals (only for sim purposes) """
|
||||
self.sf.write(".SUBCKT test_inv in out {0} {1}\n".format(self.vdd_name, self.gnd_name))
|
||||
self.sf.write("mpinv out in {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name,
|
||||
self.pmos_name,
|
||||
beta * size * self.tx_width,
|
||||
self.tx_length))
|
||||
self.sf.write("mninv out in {0} {0} {1} w={2}u l={3}u\n".format(self.gnd_name,
|
||||
self.nmos_name,
|
||||
size * self.tx_width,
|
||||
self.tx_length))
|
||||
self.sf.write(".ENDS test_inv\n")
|
||||
|
||||
|
||||
def run_sim():
|
||||
""" 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()
|
||||
debug.check(OPTS.spice_exe!="","No spice simulator has been found.")
|
||||
|
||||
if OPTS.spice_name == "xa":
|
||||
# Output the xa configurations here. FIXME: Move this to write it once.
|
||||
xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w")
|
||||
xa_cfg.write("set_sim_level -level 7\n")
|
||||
xa_cfg.write("set_powernet_level 7 -node vdd\n")
|
||||
xa_cfg.close()
|
||||
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 2".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
valid_retcode=0
|
||||
elif OPTS.spice_name == "hspice":
|
||||
# TODO: Should make multithreading parameter a configuration option
|
||||
cmd = "{0} -mt 2 -i {1} -o {2}timing".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
valid_retcode=0
|
||||
else:
|
||||
cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
# for some reason, ngspice-25 returns 1 when it only has acceptable warnings
|
||||
valid_retcode=1
|
||||
def create_buffer(self, 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.
|
||||
"""
|
||||
|
||||
self.sf.write(".SUBCKT test_{2} in out {0} {1}\n".format(self.vdd_name,
|
||||
self.gnd_name,
|
||||
buffer_name))
|
||||
self.sf.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name,
|
||||
self.pmos_name,
|
||||
beta * size[0] * self.tx_width,
|
||||
self.tx_length))
|
||||
self.sf.write("mninv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(self.gnd_name,
|
||||
self.nmos_name,
|
||||
size[0] * self.tx_width,
|
||||
self.tx_length))
|
||||
self.sf.write("mpinv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(self.vdd_name,
|
||||
self.pmos_name,
|
||||
beta * size[1] * self.tx_width,
|
||||
self.tx_length))
|
||||
self.sf.write("mninv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(self.gnd_name,
|
||||
self.nmos_name,
|
||||
size[1] * self.tx_width,
|
||||
self.tx_length))
|
||||
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
|
||||
|
||||
|
||||
def inst_buffer(self, buffer_name, signal_list):
|
||||
""" Adds buffers to each top level signal that is in signal_list (only for sim purposes) """
|
||||
for signal in signal_list:
|
||||
self.sf.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal,
|
||||
"test"+self.vdd_name,
|
||||
"test"+self.gnd_name,
|
||||
buffer_name))
|
||||
|
||||
|
||||
def inst_inverter(self, signal_list):
|
||||
""" Adds inv for each signal that needs its inverted version (only for sim purposes) """
|
||||
for signal in signal_list:
|
||||
self.sf.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal,
|
||||
"test"+self.vdd_name,
|
||||
"test"+self.gnd_name))
|
||||
|
||||
|
||||
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
|
||||
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
|
||||
def inst_accesstx(self, dbits):
|
||||
""" Adds transmission gate for inputs to data-bus (only for sim purposes) """
|
||||
self.sf.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"
|
||||
self.sf.write(pmos_access_string.format(i,
|
||||
"test"+self.vdd_name,
|
||||
self.pmos_name,
|
||||
2 * self.tx_width,
|
||||
self.tx_length))
|
||||
nmos_access_string="mn{0} DATA[{0}] acc_en_inv D[{0}] {1} {2} w={3}u l={4}u\n"
|
||||
self.sf.write(nmos_access_string.format(i,
|
||||
"test"+self.gnd_name,
|
||||
self.nmos_name,
|
||||
2 * self.tx_width,
|
||||
self.tx_length))
|
||||
|
||||
debug.info(3, cmd)
|
||||
retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
|
||||
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
|
||||
from 50% to 50%.
|
||||
"""
|
||||
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"
|
||||
self.sf.write(pulse_string.format(sig_name,
|
||||
v1,
|
||||
v2,
|
||||
offset,
|
||||
t_rise,
|
||||
t_fall,
|
||||
0.5*period-0.5*t_rise-0.5*t_fall,
|
||||
period))
|
||||
|
||||
spice_stdout.close()
|
||||
spice_stderr.close()
|
||||
|
||||
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.
|
||||
Automatically creates slews and ensures each data occurs a setup before the clock
|
||||
edge. The first clk_time should be 0 and is the initial time that corresponds
|
||||
to the initial value.
|
||||
"""
|
||||
# the initial value is not a clock time
|
||||
debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match.")
|
||||
|
||||
if (retcode > valid_retcode):
|
||||
debug.error("Spice simulation error: " + cmd, -1)
|
||||
else:
|
||||
end_time = datetime.datetime.now()
|
||||
delta_time = round((end_time-start_time).total_seconds(),1)
|
||||
debug.info(2,"*** Spice: {} seconds".format(delta_time))
|
||||
# shift signal times earlier for setup time
|
||||
times = np.array(clk_times) - setup*period
|
||||
values = np.array(data_values) * self.vdd_voltage
|
||||
half_slew = 0.5 * slew
|
||||
self.sf.write("* (time, data): {}\n".format(zip(clk_times, data_values)))
|
||||
self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
|
||||
for i in range(1,len(times)):
|
||||
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,
|
||||
values[i-1],
|
||||
times[i]+half_slew,
|
||||
values[i]))
|
||||
self.sf.write(")\n")
|
||||
|
||||
def gen_constant(self, sig_name, v_val):
|
||||
""" Generates a constant signal with reference voltage and the voltage value """
|
||||
self.sf.write("V{0} {0} 0 DC {1}\n".format(sig_name, v_val))
|
||||
|
||||
def get_inverse_voltage(self, value):
|
||||
if value > 0.5*self.vdd_voltage:
|
||||
return self.gnd_voltage
|
||||
elif value <= 0.5*self.vdd_voltage:
|
||||
return self.vdd_voltage
|
||||
else:
|
||||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
||||
|
||||
def get_inverse_value(self, value):
|
||||
if value > 0.5:
|
||||
return 0
|
||||
elif value <= 0.5:
|
||||
return 1
|
||||
else:
|
||||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
||||
|
||||
|
||||
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 """
|
||||
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"
|
||||
self.sf.write(measure_string.format(meas_name,
|
||||
trig_name,
|
||||
trig_val,
|
||||
trig_dir,
|
||||
trig_td,
|
||||
targ_name,
|
||||
targ_val,
|
||||
targ_dir,
|
||||
targ_td))
|
||||
|
||||
def gen_meas_power(self, meas_name, t_initial, t_final):
|
||||
""" 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"
|
||||
else:
|
||||
power_exp = "par('(-1*v(" + str(self.vdd_name) + ")*I(v" + str(self.vdd_name) + "))')"
|
||||
self.sf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name,
|
||||
power_exp,
|
||||
t_initial,
|
||||
t_final))
|
||||
|
||||
def write_control(self, end_time):
|
||||
""" Write the control cards to run and end the simulation """
|
||||
# UIC is needed for ngspice to converge
|
||||
self.sf.write(".TRAN 5p {0}n UIC\n".format(end_time))
|
||||
if OPTS.spice_name == "ngspice":
|
||||
# ngspice sometimes has convergence problems if not using gear 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
|
||||
# unless you figure out what these are.
|
||||
self.sf.write(".OPTIONS POST=1 RUNLVL=4 PROBE method=gear TEMP={}\n".format(self.temperature))
|
||||
else:
|
||||
self.sf.write(".OPTIONS POST=1 RUNLVL=4 PROBE TEMP={}\n".format(self.temperature))
|
||||
|
||||
# create plots for all signals
|
||||
self.sf.write("* probe is used for hspice/xa, while plot is used in ngspice\n")
|
||||
if OPTS.debug_level>0:
|
||||
if OPTS.spice_name in ["hspice","xa"]:
|
||||
self.sf.write(".probe V(*)\n")
|
||||
else:
|
||||
self.sf.write(".plot V(*)\n")
|
||||
else:
|
||||
self.sf.write("*.probe V(*)\n")
|
||||
self.sf.write("*.plot V(*)\n")
|
||||
|
||||
# end the stimulus file
|
||||
self.sf.write(".end\n\n")
|
||||
|
||||
|
||||
def write_include(self, circuit):
|
||||
"""Writes include statements, inputs are lists of model files"""
|
||||
includes = self.device_models + [circuit]
|
||||
self.sf.write("* {} process corner\n".format(self.process))
|
||||
for item in list(includes):
|
||||
if os.path.isfile(item):
|
||||
self.sf.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(self):
|
||||
""" Writes supply voltage statements """
|
||||
self.sf.write("V{0} {0} 0.0 {1}\n".format(self.vdd_name, self.vdd_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
|
||||
self.sf.write("V{0} {0} 0.0 {1}\n".format("test"+self.vdd_name, self.vdd_voltage))
|
||||
self.sf.write("V{0} {0} 0.0 {1}\n".format("test"+self.gnd_name, self.gnd_voltage))
|
||||
|
||||
|
||||
def run_sim(self):
|
||||
""" 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()
|
||||
debug.check(OPTS.spice_exe!="","No spice simulator has been found.")
|
||||
|
||||
if OPTS.spice_name == "xa":
|
||||
# Output the xa configurations here. FIXME: Move this to write it once.
|
||||
xa_cfg = open("{}xa.cfg".format(OPTS.openram_temp), "w")
|
||||
xa_cfg.write("set_sim_level -level 7\n")
|
||||
xa_cfg.write("set_powernet_level 7 -node vdd\n")
|
||||
xa_cfg.close()
|
||||
cmd = "{0} {1} -c {2}xa.cfg -o {2}xa -mt 2".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
valid_retcode=0
|
||||
elif OPTS.spice_name == "hspice":
|
||||
# TODO: Should make multithreading parameter a configuration option
|
||||
cmd = "{0} -mt 2 -i {1} -o {2}timing".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
valid_retcode=0
|
||||
else:
|
||||
cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
# for some reason, ngspice-25 returns 1 when it only has acceptable warnings
|
||||
valid_retcode=1
|
||||
|
||||
|
||||
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
|
||||
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
|
||||
|
||||
debug.info(3, cmd)
|
||||
retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
|
||||
|
||||
spice_stdout.close()
|
||||
spice_stderr.close()
|
||||
|
||||
if (retcode > valid_retcode):
|
||||
debug.error("Spice simulation error: " + cmd, -1)
|
||||
else:
|
||||
end_time = datetime.datetime.now()
|
||||
delta_time = round((end_time-start_time).total_seconds(),1)
|
||||
debug.info(2,"*** Spice: {} seconds".format(delta_time))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ num_words = 16
|
|||
num_banks = 1
|
||||
|
||||
tech_name = "scn3me_subm"
|
||||
supply_voltages = [ 5.0 ]
|
||||
temperatures = [ 25.0 ]
|
||||
|
||||
|
||||
output_path = "temp"
|
||||
output_name = "sram_2_16_1_scn3me_subm"
|
||||
|
|
|
|||
|
|
@ -44,6 +44,13 @@ class options(optparse.Values):
|
|||
analytical_delay = True
|
||||
# Purge the temp directory after a successful run (doesn't purge on errors, anyhow)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1040,8 +1040,7 @@ class sram(design.design):
|
|||
# Characterize the design
|
||||
start_time = datetime.datetime.now()
|
||||
from characterizer import lib
|
||||
libname = OPTS.output_path + self.name + ".lib"
|
||||
print("LIB: Writing to {0}".format(libname))
|
||||
print("LIB: Characterizing... ")
|
||||
if OPTS.analytical_delay:
|
||||
print("Using analytical delay models (no characterization)")
|
||||
else:
|
||||
|
|
@ -1049,7 +1048,7 @@ class sram(design.design):
|
|||
print("Performing simulation-based characterization with {}".format(OPTS.spice_name))
|
||||
if OPTS.trim_netlist:
|
||||
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)
|
||||
|
||||
# Write the layout
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class timing_sram_test(openram_test):
|
|||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
import sram
|
||||
|
||||
import tech
|
||||
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=OPTS.word_size,
|
||||
num_words=OPTS.num_words,
|
||||
|
|
@ -43,9 +43,9 @@ class timing_sram_test(openram_test):
|
|||
probe_data = s.word_size - 1
|
||||
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
|
||||
loads = [tech.spice["FF_in_cap"]*4]
|
||||
loads = [tech.spice["msflop_in_cap"]*4]
|
||||
slews = [tech.spice["rise_time"]*2]
|
||||
data = d.analyze(probe_address, probe_data,slews,loads)
|
||||
#print data
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class timing_setup_test(openram_test):
|
|||
import tech
|
||||
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)
|
||||
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class timing_sram_test(openram_test):
|
|||
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
||||
|
||||
import sram
|
||||
|
||||
import tech
|
||||
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
||||
s = sram.sram(word_size=OPTS.word_size,
|
||||
num_words=OPTS.num_words,
|
||||
|
|
@ -41,9 +41,9 @@ class timing_sram_test(openram_test):
|
|||
probe_data = s.word_size - 1
|
||||
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
|
||||
loads = [tech.spice["FF_in_cap"]*4]
|
||||
loads = [tech.spice["msflop_in_cap"]*4]
|
||||
slews = [tech.spice["rise_time"]*2]
|
||||
data = d.analyze(probe_address, probe_data,slews,loads)
|
||||
#print data
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class timing_setup_test(openram_test):
|
|||
import tech
|
||||
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)
|
||||
|
||||
if OPTS.tech_name == "freepdk45":
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Check the .lib file for an SRAM
|
|||
|
||||
import unittest
|
||||
from testutils import header,openram_test
|
||||
import sys,os
|
||||
import sys,os,re
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
|
|
@ -30,14 +30,20 @@ class lib_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
filename = s.name + "_analytical.lib"
|
||||
libname = OPTS.openram_temp + filename
|
||||
lib.lib(libname=libname,sram=s,sp_file=tempspice,use_model=True)
|
||||
lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=True)
|
||||
|
||||
# let's diff the result with a golden model
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
|
||||
self.isapproxdiff(libname,golden,0.15)
|
||||
# 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:
|
||||
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)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Check the .lib file for an SRAM
|
|||
|
||||
import unittest
|
||||
from testutils import header,openram_test
|
||||
import sys,os
|
||||
import sys,os,re
|
||||
sys.path.append(os.path.join(sys.path[0],".."))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
|
|
@ -39,13 +39,18 @@ class lib_test(openram_test):
|
|||
tempspice = OPTS.openram_temp + "temp.sp"
|
||||
s.sp_write(tempspice)
|
||||
|
||||
filename = s.name + ".lib"
|
||||
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
|
||||
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
|
||||
self.isapproxdiff(libname,golden,0.15)
|
||||
lib.lib(out_dir=OPTS.openram_temp, sram=s, sp_file=tempspice, use_model=False)
|
||||
|
||||
# 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)
|
||||
self.isapproxdiff(libname,golden,0.15)
|
||||
|
||||
OPTS.analytical_delay = True
|
||||
OPTS.trim_netlist = True
|
||||
|
|
|
|||
|
|
@ -237,26 +237,16 @@ drc["metal4_extend_via4"] = 0.07
|
|||
spice = {}
|
||||
spice["nmos"] = "nmos_vtg"
|
||||
spice["pmos"] = "pmos_vtg"
|
||||
# This is a map of corners to model files
|
||||
SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR")
|
||||
spice["fet_models"] = [SPICE_MODEL_DIR+"/NMOS_VTG.inc",
|
||||
SPICE_MODEL_DIR+"/PMOS_VTG.inc"]
|
||||
spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/NMOS_VTG.inc",SPICE_MODEL_DIR+"/PMOS_VTG.inc"]}
|
||||
|
||||
#spice stimulus related variables
|
||||
spice["feasible_period"] = 5 # estimated feasible period in ns
|
||||
spice["supply_voltage"] = 1.0 #vdd in [Volts]
|
||||
spice["gnd_voltage"] = 0.0 #gnd in [Volts]
|
||||
spice["rise_time"] = 0.005 #rise time in [Nano-seconds]
|
||||
spice["fall_time"] = 0.005 #fall time in [Nano-seconds]
|
||||
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]
|
||||
|
||||
spice["supply_voltage"] = 1.0 # ideal vdd in [Volts]
|
||||
spice["rise_time"] = 0.005 # rise time in [Nano-seconds]
|
||||
spice["fall_time"] = 0.005 # fall time in [Nano-seconds]
|
||||
spice["nom_corner"] = ("TT", 1.0, 25) # Nominal process corner
|
||||
|
||||
#sram signal names
|
||||
spice["vdd_name"] = "vdd"
|
||||
|
|
@ -264,8 +254,6 @@ spice["gnd_name"] = "gnd"
|
|||
spice["control_signals"] = ["CSb", "WEb", "OEb"]
|
||||
spice["data_name"] = "DATA"
|
||||
spice["addr_name"] = "ADDR"
|
||||
spice["pmos_name"] = spice["pmos"]
|
||||
spice["nmos_name"] = spice["nmos"]
|
||||
spice["minwidth_tx"] = drc["minwidth_tx"]
|
||||
spice["channel"] = drc["minlength_channel"]
|
||||
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_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_in_cap"] = 0.2091 # Input capacitance of ms_flop (Din) [Femto-farad]
|
||||
|
||||
|
||||
###################################################
|
||||
|
|
|
|||
|
|
@ -197,24 +197,16 @@ drc["minarea_metal3"] = 0
|
|||
spice={}
|
||||
spice["nmos"]="n"
|
||||
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["feasible_period"] = 5 # estimated feasible period in ns
|
||||
spice["supply_voltage"] = 5.0 #vdd in [Volts]
|
||||
spice["gnd_voltage"] = 0.0 #gnd in [Volts]
|
||||
spice["rise_time"] = 0.05 #rise time in [Nano-seconds]
|
||||
spice["fall_time"] = 0.05 #fall time in [Nano-seconds]
|
||||
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]
|
||||
|
||||
spice["supply_voltage"] = 5.0 # ideal vdd in [Volts]
|
||||
spice["rise_time"] = 0.05 # rise time in [Nano-seconds]
|
||||
spice["fall_time"] = 0.05 # fall time in [Nano-seconds]
|
||||
spice["nom_corner"] = ("TT", 5.0, 25) # Nominal process corner
|
||||
|
||||
#sram signal names
|
||||
spice["vdd_name"] = "vdd"
|
||||
|
|
@ -222,8 +214,6 @@ spice["gnd_name"] = "gnd"
|
|||
spice["control_signals"] = ["CSb", "WEb", "OEb"]
|
||||
spice["data_name"] = "DATA"
|
||||
spice["addr_name"] = "ADDR"
|
||||
spice["pmos_name"] = spice["pmos"]
|
||||
spice["nmos_name"] = spice["nmos"]
|
||||
spice["minwidth_tx"] = drc["minwidth_tx"]
|
||||
spice["channel"] = drc["minlength_channel"]
|
||||
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_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_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad]
|
||||
|
||||
|
||||
###################################################
|
||||
|
|
|
|||
Loading…
Reference in New Issue