Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Jesse Cirimelli-Low 2021-07-01 15:22:29 -07:00
commit 8a0e3e5caf
6 changed files with 100 additions and 69 deletions

View File

@ -81,7 +81,11 @@ class functional(simulation):
self.create_graph()
self.set_internal_spice_names()
self.q_name, self.qbar_name = self.get_bit_name()
debug.info(2, "q name={0}\nqbar name={1}".format(self.q_name, self.qbar_name))
debug.info(2, "q:\t\t{0}".format(self.q_name))
debug.info(2, "qbar:\t{0}".format(self.qbar_name))
debug.info(2, "s_en:\t{0}".format(self.sen_name))
debug.info(2, "bl:\t{0}".format(self.bl_name))
debug.info(2, "br:\t{0}".format(self.br_name))
# Number of checks can be changed
self.num_cycles = cycles
@ -141,23 +145,25 @@ class functional(simulation):
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment)
# 1. Write all the write ports first to seed a bunch of locations.
for port in self.write_ports:
addr = self.gen_addr()
(word, spare) = self.gen_data()
combined_word = self.combine_word(spare, word)
comment = self.gen_cycle_comment("write", combined_word, addr, "1" * self.num_wmasks, port, self.t_current)
self.add_write_one_port(comment, addr, spare + word, "1" * self.num_wmasks, port)
self.stored_words[addr] = word
self.stored_spares[addr[:self.addr_spare_index]] = spare
# All other read-only ports are noops.
for port in self.read_ports:
if port not in self.write_ports:
self.add_noop_one_port(port)
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.check_lengths()
# 1. Write all the write ports 2x to seed a bunch of locations.
for i in range(3):
for port in self.write_ports:
addr = self.gen_addr()
(word, spare) = self.gen_data()
combined_word = self.combine_word(spare, word)
comment = self.gen_cycle_comment("write", combined_word, addr, "1" * self.num_wmasks, port, self.t_current)
self.add_write_one_port(comment, addr, spare + word, "1" * self.num_wmasks, port)
self.stored_words[addr] = word
self.stored_spares[addr[:self.addr_spare_index]] = spare
# All other read-only ports are noops.
for port in self.read_ports:
if port not in self.write_ports:
self.add_noop_one_port(port)
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.check_lengths()
# 2. Read at least once. For multiport, it is important that one
# read cycle uses all RW and R port to read from the same
@ -297,38 +303,6 @@ class functional(simulation):
self.read_results.append([sp_read_value, dout_port, eo_period, cycle])
return (1, "SUCCESS")
def combine_word(self, spare, word):
if len(spare) > 0:
return spare + "+" + word
return word
def format_value(self, value):
""" Format in better readable manner """
def delineate(word):
# Create list of chars in reverse order
split_word = list(reversed([x for x in word]))
# Add underscore every 4th char
split_word2 = [x + '_' * (n != 0 and n % 4 == 0) for n, x in enumerate(split_word)]
# Join the word unreversed back together
new_word = ''.join(reversed(split_word2))
return(new_word)
# Split extra cols
if self.num_spare_cols > 0:
vals = value[self.num_spare_cols:]
spare_vals = value[:self.num_spare_cols]
else:
vals = value
spare_vals = ""
# Insert underscores
vals = delineate(vals)
spare_vals = delineate(spare_vals)
return self.combine_word(spare_vals, vals)
def check_stim_results(self):
for i in range(len(self.read_check)):
if self.read_check[i][0] != self.read_results[i][0]:
@ -372,7 +346,8 @@ class functional(simulation):
def gen_data(self):
""" Generates a random word to write. """
random_value = random.randint(0, self.max_data)
# Don't use 0 or max value
random_value = random.randint(1, self.max_data - 1)
data_bits = binary_repr(random_value, self.word_size)
if self.num_spare_cols>0:
random_value = random.randint(0, self.max_col_data)
@ -431,11 +406,11 @@ class functional(simulation):
# Write important signals to stim file
self.sf.write("\n\n* Important signals for debug\n")
self.sf.write("* bl: {0}\n".format(self.bl_name.format(port)))
self.sf.write("* br: {0}\n".format(self.br_name.format(port)))
self.sf.write("* s_en: {0}\n".format(self.sen_name))
self.sf.write("* q: {0}\n".format(self.q_name))
self.sf.write("* qbar: {0}\n".format(self.qbar_name))
self.sf.write("* bl:\t{0}\n".format(self.bl_name.format(port)))
self.sf.write("* br:\t{0}\n".format(self.br_name.format(port)))
self.sf.write("* s_en:\t{0}\n".format(self.sen_name))
self.sf.write("* q:\t{0}\n".format(self.q_name))
self.sf.write("* qbar:\t{0}\n".format(self.qbar_name))
# Write debug comments to stim file
self.sf.write("\n\n* Sequence of operations\n")
@ -498,7 +473,7 @@ class functional(simulation):
for (word, dout_port, eo_period, cycle) in self.read_check:
t_initial = eo_period
t_final = eo_period
t_final = eo_period + 0.01 * self.period
num_bits = self.word_size + self.num_spare_cols
for bit in range(num_bits):
signal_name = "{0}_{1}".format(dout_port, bit)

View File

@ -372,6 +372,38 @@ class simulation():
time_spacing,
comment))
def combine_word(self, spare, word):
if len(spare) > 0:
return spare + "+" + word
return word
def format_value(self, value):
""" Format in better readable manner """
def delineate(word):
# Create list of chars in reverse order
split_word = list(reversed([x for x in word]))
# Add underscore every 4th char
split_word2 = [x + '_' * (n != 0 and n % 4 == 0) for n, x in enumerate(split_word)]
# Join the word unreversed back together
new_word = ''.join(reversed(split_word2))
return(new_word)
# Split extra cols
if self.num_spare_cols > 0:
vals = value[self.num_spare_cols:]
spare_vals = value[:self.num_spare_cols]
else:
vals = value
spare_vals = ""
# Insert underscores
vals = delineate(vals)
spare_vals = delineate(spare_vals)
return self.combine_word(spare_vals, vals)
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
if op == "noop":
str = "\tIdle during cycle {0} ({1}ns - {2}ns)"
@ -483,8 +515,6 @@ class simulation():
self.sen_name = sen_with_port
debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.")
debug.info(2, "s_en name = {}".format(self.sen_name))
column_addr = self.get_column_addr()
bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port)
port_pos = -1 - len(str(column_addr)) - len(str(port))
@ -505,11 +535,12 @@ class simulation():
'{}{}_{}'.format(self.dout_name, port, self.probe_data))
self.sen_name = self.get_sen_name(self.graph.all_paths)
debug.info(2, "s_en name = {}".format(self.sen_name))
#debug.info(2, "s_en {}".format(self.sen_name))
self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1)
self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1)
debug.info(2, "bl name={}, br name={}".format(self.bl_name, self.br_name))
# debug.info(2, "bl name={0}".format(self.bl_name))
# debug.info(2, "br name={0}".format(self.br_name))
def get_sen_name(self, paths, assumed_port=None):
"""

View File

@ -222,7 +222,7 @@ class stimuli():
def gen_meas_value(self, meas_name, dout, t_initial, t_final):
measure_string=".meas tran {0} FIND v({1}) AT={2}n\n\n".format(meas_name.lower(), dout, (t_initial + t_final) / 2)
# measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name.lower(), dout, t_initial, t_final)
#measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name.lower(), dout, t_initial, t_final)
self.sf.write(measure_string)
def write_control(self, end_time, runlvl=4):
@ -237,12 +237,13 @@ class stimuli():
reltol = 0.005 # 0.5%
else:
reltol = 0.001 # 0.1%
timestep = 10 # ps, was 5ps but ngspice was complaining the timestep was too small in certain tests.
timestep = 10 # ps
if OPTS.spice_name == "ngspice":
self.sf.write(".TEMP {}\n".format(self.temperature))
# UIC is needed for ngspice to converge
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
# Format: .tran tstep tstop < tstart < tmax >>
self.sf.write(".TRAN {0}p {1}n 0n {0}p UIC\n".format(timestep, end_time))
# 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
@ -268,7 +269,8 @@ class stimuli():
self.sf.write("simulator lang=spice\n")
elif OPTS.spice_name in ["hspice", "xa"]:
self.sf.write(".TEMP {}\n".format(self.temperature))
self.sf.write(".TRAN {0}p {1}n UIC\n".format(timestep, end_time))
# Format: .tran tstep tstop < tstart < tmax >>
self.sf.write(".TRAN {0}p {1}n 0n {0}p UIC\n".format(timestep, end_time))
self.sf.write(".OPTIONS POST=1 RUNLVL={0} PROBE\n".format(runlvl))
self.sf.write(".OPTIONS PSF=1 \n")
self.sf.write(".OPTIONS HIER_DELIM=1 \n")
@ -276,7 +278,9 @@ class stimuli():
self.sf.write(".OPTIONS DEVICE TEMP={}\n".format(self.temperature))
self.sf.write(".OPTIONS MEASURE MEASFAIL=1\n")
self.sf.write(".OPTIONS LINSOL type=klu\n")
self.sf.write(".TRAN {0}p {1}n\n".format(timestep, end_time))
self.sf.write(".OPTIONS TIMEINT RELTOL=1e-6 ABSTOL=1e-10 method=gear minorder=2\n")
# Format: .TRAN <initial step> <final time> <start time> <step ceiling>
self.sf.write(".TRAN {0}p {1}n 0n {0}p\n".format(timestep, end_time))
elif OPTS.spice_name:
debug.error("Unkown spice simulator {}".format(OPTS.spice_name), -1)

View File

@ -23,7 +23,15 @@ class array_test(openram_test):
globals.init_openram(config_file)
debug.info(2, "Testing 8x8 array for 6t_cell")
a = factory.create(module_type="bitcell_array", cols=8, rows=8)
if OPTS.tech_name == "sky130":
num_spare_rows = 1
num_spare_cols = 1
else:
num_spare_rows = 0
num_spare_cols = 0
a = factory.create(module_type="bitcell_array", cols=8 + num_spare_cols, rows=8 + num_spare_rows)
self.local_check(a)
globals.end_openram()

View File

@ -24,6 +24,8 @@ class riscv_func_test(openram_test):
globals.init_openram(config_file)
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
@ -48,7 +50,7 @@ class riscv_func_test(openram_test):
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, corner=corner, cycles=50)
f = functional(s.s, corner=corner, cycles=25)
(fail, error) = f.run()
self.assertTrue(fail, error)

View File

@ -24,6 +24,15 @@ class riscv_func_test(openram_test):
globals.init_openram(config_file)
OPTS.analytical_delay = False
OPTS.netlist_only = True
OPTS.trim_netlist = False
if OPTS.tech_name == "sky130":
num_spare_rows = 1
num_spare_cols = 1
else:
num_spare_rows = 0
num_spare_cols = 0
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
@ -38,7 +47,9 @@ class riscv_func_test(openram_test):
c = sram_config(word_size=32,
write_size=8,
num_words=32,
num_banks=1)
num_banks=1,
num_spare_cols=num_spare_cols,
num_spare_rows=num_spare_rows)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Functional test RISC-V memory"
@ -48,7 +59,7 @@ class riscv_func_test(openram_test):
c.num_banks))
s = factory.create(module_type="sram", sram_config=c)
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, corner=corner, cycles=50)
f = functional(s.s, corner=corner, cycles=25)
(fail, error) = f.run()
self.assertTrue(fail, error)