mirror of https://github.com/VLSIDA/OpenRAM.git
Finished functional test
This commit is contained in:
parent
26c6232564
commit
8a56dd2ac9
|
|
@ -40,10 +40,11 @@ class functional():
|
|||
self.set_stimulus_variables()
|
||||
|
||||
# Number of checks can be changed
|
||||
self.num_checks = 1
|
||||
self.num_checks = 2
|
||||
|
||||
# set to 1 if functional simulation fails during any check
|
||||
self.functional_fail = 0
|
||||
self.error = ""
|
||||
|
||||
def set_corner(self,corner):
|
||||
""" Set the corner values """
|
||||
|
|
@ -70,49 +71,175 @@ class functional():
|
|||
self.eo_period = []
|
||||
|
||||
# Three dimensional list to handle each addr and data bits for wach port over the number of checks
|
||||
self.addresses = [[[] for bit in range(self.addr_size)] for port in range(self.total_write)]
|
||||
self.addresses = [[[] for bit in range(self.addr_size)] for port in range(self.total_ports)]
|
||||
self.data = [[[] for bit in range(self.word_size)] for port in range(self.total_write)]
|
||||
|
||||
# stored written values and read values for functional check
|
||||
#self.addr_keys = []
|
||||
self.written_values = []
|
||||
self.sp_read_value = ["" for port in range(self.total_read)]
|
||||
|
||||
def run(self):
|
||||
""" Main function to generate random writes/reads, run spice, and analyze results """
|
||||
# Need a NOOP to enable the chip
|
||||
# Old code. Is this still necessary?
|
||||
self.first_run()
|
||||
self.noop()
|
||||
|
||||
# Generate write and read signals for spice stimulus
|
||||
for i in range(self.num_checks):
|
||||
addr = self.gen_addr()
|
||||
word = self.gen_data()
|
||||
#self.addr_keys.append(addr)
|
||||
#self.written_values[addr] = word
|
||||
|
||||
self.write(addr,word)
|
||||
self.read(addr,word)
|
||||
self.overwrite_test()
|
||||
self.write_read_test()
|
||||
|
||||
# Run SPICE simulation
|
||||
self.write_functional_stimulus()
|
||||
self.stim.run_sim()
|
||||
|
||||
# Extrat DOUT values from spice timing.lis
|
||||
for port in range(self.total_read):
|
||||
for bit in range(self.word_size):
|
||||
value = parse_spice_list("timing", "vdout{0}.{1}.".format(port,bit))
|
||||
if value > 0.75 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "1" + self.sp_read_value[port]
|
||||
elif value < 0.25 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "0" + self.sp_read_value[port]
|
||||
for i in range(2*self.num_checks):
|
||||
self.sp_read_value = ["" for port in range(self.total_read)]
|
||||
for port in range(self.total_read):
|
||||
for bit in range(self.word_size):
|
||||
value = parse_spice_list("timing", "vdout{0}.{1}..ch{2}".format(self.read_index[port],bit,i))
|
||||
if value > 0.75 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "1" + self.sp_read_value[port]
|
||||
elif value < 0.25 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "0" + self.sp_read_value[port]
|
||||
else:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: dout value ({}) does not fall within noise margins.".format(self.sp_read_value[port])
|
||||
|
||||
if i < self.num_checks:
|
||||
self.read_values_over_test[i].append(self.sp_read_value[port])
|
||||
else:
|
||||
self.read_values_test[i-self.num_checks].append(self.sp_read_value[port])
|
||||
|
||||
# Compare written values to read values
|
||||
for i in range(self.num_checks):
|
||||
debug.info(1, "Stored Word - Overwrite Test: {}".format(self.stored_values_over_test[i]))
|
||||
debug.info(1, "Read Word - Overwrite Test: {}".format(self.read_values_over_test[i]))
|
||||
for port in range(self.total_read):
|
||||
if self.stored_values_over_test[i] != self.read_values_over_test[i][port]:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: read value {0} does not match writen value {1}.".format(self.read_values_over_test[i][port], self.stored_values_over_test[i])
|
||||
|
||||
print("READ VALUE dout{} = {}".format(port,self.sp_read_value[port]))
|
||||
for i in range(self.num_checks):
|
||||
debug.info(1, "Stored Word - Standard W/R Test: {}".format(self.stored_values_test[i]))
|
||||
debug.info(1, "Read Word - Standard W/R Test: {}".format(self.read_values_test[i]))
|
||||
for port in range(self.total_read):
|
||||
if self.stored_values_test[i] != self.read_values_test[i][port]:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: read value {0} does not match writen value {1}.".format(self.read_values_test[i][port], self.stored_values_test[i])
|
||||
|
||||
return (self.functional_fail, self.error)
|
||||
|
||||
def first_run(self):
|
||||
""" First cycle as noop to enable chip """
|
||||
def multiport_run(self):
|
||||
""" Main function to generate random writes/reads, run spice, and analyze results. This function includes a multiport check. """
|
||||
self.noop()
|
||||
|
||||
self.multi_read_test()
|
||||
self.overwrite_test()
|
||||
self.write_read_test()
|
||||
|
||||
# Run SPICE simulation
|
||||
self.write_functional_stimulus()
|
||||
self.stim.run_sim()
|
||||
|
||||
# Extrat DOUT values from spice timing.lis
|
||||
for i in range(3*self.num_checks):
|
||||
self.sp_read_value = ["" for port in range(self.total_read)]
|
||||
for port in range(self.total_read):
|
||||
for bit in range(self.word_size):
|
||||
value = parse_spice_list("timing", "vdout{0}.{1}..ch{2}".format(self.read_index[port],bit,i))
|
||||
if value > 0.75 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "1" + self.sp_read_value[port]
|
||||
elif value < 0.25 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "0" + self.sp_read_value[port]
|
||||
else:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: dout value ({}) does not fall within noise margins.".format(self.sp_read_value[port])
|
||||
|
||||
if i < self.num_checks:
|
||||
self.read_values_multi_test[i][self.multi_addrs[i][port]] = self.sp_read_value[port]
|
||||
elif i < 2*self.num_checks:
|
||||
self.read_values_over_test[i-self.num_checks].append(self.sp_read_value[port])
|
||||
else:
|
||||
self.read_values_test[i-2*self.num_checks].append(self.sp_read_value[port])
|
||||
|
||||
# Compare written values to read values
|
||||
for i in range(self.num_checks):
|
||||
debug.info(1, "Stored Words - Multi Test (addr:word): {}".format(self.stored_values_multi_test[i]))
|
||||
debug.info(1, "Read Words - Mutlti Test (addr:word): {}".format(self.read_values_multi_test[i]))
|
||||
for addr in self.multi_addrs[i]:
|
||||
if self.stored_values_multi_test[i][addr] != self.read_values_multi_test[i][addr]:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: read value {0} does not match writen value {1}.".format(self.read_values_multi_test[i][addr], self.stored_values_multi_test[i][addr])
|
||||
|
||||
for i in range(self.num_checks):
|
||||
debug.info(1, "Stored Word - Overwrite Test: {}".format(self.stored_values_over_test[i]))
|
||||
debug.info(1, "Read Word - Overwrite Test: {}".format(self.read_values_over_test[i]))
|
||||
for port in range(self.total_read):
|
||||
if self.stored_values_over_test[i] != self.read_values_over_test[i][port]:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: read value {0} does not match writen value {1}.".format(self.read_values_over_test[i][port], self.stored_values_over_test[i])
|
||||
|
||||
for i in range(self.num_checks):
|
||||
debug.info(1, "Stored Word - Standard W/R Test: {}".format(self.stored_values_test[i]))
|
||||
debug.info(1, "Read Word - Standard W/R Test: {}".format(self.read_values_test[i]))
|
||||
for port in range(self.total_read):
|
||||
if self.stored_values_test[i] != self.read_values_test[i][port]:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: read value {0} does not match writen value {1}.".format(self.read_values_test[i][port], self.stored_values_test[i])
|
||||
|
||||
return (self.functional_fail, self.error)
|
||||
|
||||
def multi_read_test(self):
|
||||
""" Multiport functional test to see if mutliple words on multiple addresses can be accessed at the same time. """
|
||||
self.stored_values_multi_test = [{} for i in range(self.num_checks)]
|
||||
self.read_values_multi_test = [{} for i in range(self.num_checks)]
|
||||
self.multi_addrs = []
|
||||
|
||||
for i in range(self.num_checks):
|
||||
# Write random words to a random addresses until there are as many stored words as there are RW and R ports
|
||||
while(len(self.stored_values_multi_test[i]) < self.total_read):
|
||||
addr = self.gen_addr()
|
||||
word = self.gen_data()
|
||||
self.write(addr,word)
|
||||
self.stored_values_multi_test[i][addr] = word
|
||||
|
||||
# Each RW and R port will read from a different address
|
||||
stored_addrs = []
|
||||
stored_words = []
|
||||
for (addr,word) in self.stored_values_multi_test[i].items():
|
||||
stored_addrs.append(addr)
|
||||
stored_words.append(word)
|
||||
|
||||
self.multi_addrs.append(stored_addrs)
|
||||
self.multi_read(stored_addrs,stored_words)
|
||||
|
||||
def overwrite_test(self):
|
||||
""" Functional test to see if a word at a particular address can be overwritten without being corrupted. """
|
||||
self.stored_values_over_test = []
|
||||
self.read_values_over_test = [[] for i in range(self.num_checks)]
|
||||
|
||||
for i in range(self.num_checks):
|
||||
# Write a random word to a random address 3 different times, overwriting the stored word twice
|
||||
addr = self.gen_addr()
|
||||
for j in range(3):
|
||||
word = self.gen_data()
|
||||
self.write(addr,word)
|
||||
self.stored_values_over_test.append(word)
|
||||
|
||||
# Read word from address (use all RW and R ports)
|
||||
self.read(addr,word)
|
||||
|
||||
def write_read_test(self):
|
||||
""" A standard functional test for writing to an address and reading back the value. """
|
||||
self.stored_values_test = []
|
||||
self.read_values_test = [[] for i in range(self.num_checks)]
|
||||
|
||||
for i in range(self.num_checks):
|
||||
# Write a random word to a random address
|
||||
addr = self.gen_addr()
|
||||
word = self.gen_data()
|
||||
self.write(addr,word)
|
||||
self.stored_values_test.append(word)
|
||||
|
||||
# Read word from address (use all RW and R ports)
|
||||
self.read(addr,word)
|
||||
|
||||
def noop(self):
|
||||
""" Noop cycle. """
|
||||
self.cycles = self.cycles + 1
|
||||
|
||||
for port in range(self.total_ports):
|
||||
|
|
@ -121,7 +248,7 @@ class functional():
|
|||
for port in range(self.total_write):
|
||||
self.we_b[port].append(1)
|
||||
|
||||
for port in range(self.total_write):
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
self.addresses[port][bit].append(0)
|
||||
|
||||
|
|
@ -130,8 +257,8 @@ class functional():
|
|||
self.data[port][bit].append(0)
|
||||
|
||||
def write(self,addr,word,write_port=0):
|
||||
""" Generates signals for a write cycle """
|
||||
print("Writing {0} to {1}...".format(word,addr))
|
||||
""" Generates signals for a write cycle. """
|
||||
debug.info(1, "Writing {0} to address {1} in cycle {2}...".format(word,addr,self.cycles))
|
||||
self.cycles = self.cycles + 1
|
||||
|
||||
# Write control signals
|
||||
|
|
@ -148,7 +275,7 @@ class functional():
|
|||
self.we_b[port].append(1)
|
||||
|
||||
# Write address
|
||||
for port in range(self.total_write):
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
current_address_bit = int(addr[self.addr_size-1-bit])
|
||||
self.addresses[port][bit].append(current_address_bit)
|
||||
|
|
@ -160,8 +287,8 @@ class functional():
|
|||
self.data[port][bit].append(current_word_bit)
|
||||
|
||||
def read(self,addr,word):
|
||||
""" Generates signals for a read cycle """
|
||||
print("Reading {0} from {1}...".format(word,addr))
|
||||
""" Generates signals for a read cycle. """
|
||||
debug.info(1, "Reading {0} from address {1} in cycle {2},{3}...".format(word,addr,self.cycles,self.cycles+1))
|
||||
self.cycles = self.cycles + 2
|
||||
|
||||
# Read control signals
|
||||
|
|
@ -172,7 +299,7 @@ class functional():
|
|||
self.we_b[port].append(1)
|
||||
|
||||
# Read address
|
||||
for port in range(self.total_write):
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
current_address_bit = int(addr[self.addr_size-1-bit])
|
||||
self.addresses[port][bit].append(current_address_bit)
|
||||
|
|
@ -192,7 +319,7 @@ class functional():
|
|||
self.we_b[port].append(1)
|
||||
|
||||
# Address doesn't matter during idle cycle, but keep the same as read cycle for easier debugging
|
||||
for port in range(self.total_write):
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
current_address_bit = int(addr[self.addr_size-1-bit])
|
||||
self.addresses[port][bit].append(current_address_bit)
|
||||
|
|
@ -206,27 +333,90 @@ class functional():
|
|||
# Record the end of the period that the read operation occured in
|
||||
self.eo_period.append(self.cycles * self.period)
|
||||
|
||||
def multi_read(self,addrs,words):
|
||||
""" Generates signals for a read cycle but all ports read from a different address. The inputs 'addrs' and 'words' are lists. """
|
||||
debug.info(1, "Reading {0} from addresses {1} in cycles {2},{3}...".format(words,addrs,self.cycles,self.cycles+1))
|
||||
self.cycles = self.cycles + 2
|
||||
|
||||
# Read control signals
|
||||
for port in range(self.total_ports):
|
||||
self.cs_b[port].append(0)
|
||||
|
||||
for port in range(self.total_write):
|
||||
self.we_b[port].append(1)
|
||||
|
||||
# Read address
|
||||
addr_index = 0
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
if self.port_id[port] == "w":
|
||||
current_address_bit = 0
|
||||
else:
|
||||
current_address_bit = int(addrs[addr_index][self.addr_size-1-bit])
|
||||
|
||||
self.addresses[port][bit].append(current_address_bit)
|
||||
|
||||
if self.port_id[port] != "w":
|
||||
addr_index += 1
|
||||
|
||||
# Data input doesn't matter during read cycle, so arbitrarily set to 0 for simulation
|
||||
for port in range(self.total_write):
|
||||
for bit in range(self.word_size):
|
||||
self.data[port][bit].append(0)
|
||||
|
||||
|
||||
# Add idle cycle since read may take more than 1 cycle
|
||||
# Idle control signals
|
||||
for port in range(self.total_ports):
|
||||
self.cs_b[port].append(1)
|
||||
|
||||
for port in range(self.total_write):
|
||||
self.we_b[port].append(1)
|
||||
|
||||
# Address doesn't matter during idle cycle, but keep the same as read cycle for easier debugging
|
||||
addr_index = 0
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
if self.port_id[port] == "w":
|
||||
current_address_bit = 0
|
||||
else:
|
||||
current_address_bit = int(addrs[addr_index][self.addr_size-1-bit])
|
||||
|
||||
self.addresses[port][bit].append(current_address_bit)
|
||||
|
||||
if self.port_id[port] != "w":
|
||||
addr_index += 1
|
||||
|
||||
# Data input doesn't matter during idle cycle, so arbitrarily set to 0 for simulation
|
||||
for port in range(self.total_write):
|
||||
for bit in range(self.word_size):
|
||||
self.data[port][bit].append(0)
|
||||
|
||||
|
||||
# Record the end of the period that the read operation occured in
|
||||
self.eo_period.append(self.cycles * self.period)
|
||||
|
||||
def gen_data(self):
|
||||
""" Generates a random word to write """
|
||||
""" Generates a random word to write. """
|
||||
rand = random.randint(0,(2**self.word_size)-1)
|
||||
data_bits = self.convert_to_bin(rand,False)
|
||||
return data_bits
|
||||
|
||||
def gen_addr(self):
|
||||
""" Generates a random address value to write to """
|
||||
""" Generates a random address value to write to. """
|
||||
rand = random.randint(0,(2**self.addr_size)-1)
|
||||
addr_bits = self.convert_to_bin(rand,True)
|
||||
return addr_bits
|
||||
|
||||
def get_data(self):
|
||||
""" Gets an available address and corresponding word """
|
||||
""" Gets an available address and corresponding word. """
|
||||
# Currently unused but may need later depending on how the functional test develops
|
||||
addr = random.choice(self.stored_words.keys())
|
||||
word = self.stored_words[addr]
|
||||
return (addr,word)
|
||||
|
||||
def convert_to_bin(self,value,is_addr):
|
||||
""" Converts addr & word to usable binary values """
|
||||
""" Converts addr & word to usable binary values. """
|
||||
new_value = str.replace(bin(value),"0b","")
|
||||
if(is_addr):
|
||||
expected_value = self.addr_size
|
||||
|
|
@ -235,11 +425,11 @@ class functional():
|
|||
for i in range (expected_value - len(new_value)):
|
||||
new_value = "0" + new_value
|
||||
|
||||
print("Binary Conversion: {} to {}".format(value, new_value))
|
||||
#print("Binary Conversion: {} to {}".format(value, new_value))
|
||||
return new_value
|
||||
|
||||
def obtain_cycle_times(self,period):
|
||||
""" Generate clock cycle times based on period and number of cycles """
|
||||
""" Generate clock cycle times based on period and number of cycles. """
|
||||
t_current = 0
|
||||
self.cycle_times = []
|
||||
for i in range(self.cycles):
|
||||
|
|
@ -247,7 +437,7 @@ class functional():
|
|||
t_current += period
|
||||
|
||||
def create_port_names(self):
|
||||
"""Generates the port names to be used in characterization and sets default simulation target ports"""
|
||||
"""Generates the port names to be used in characterization and sets default simulation target ports. """
|
||||
self.write_ports = []
|
||||
self.read_ports = []
|
||||
self.total_port_num = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
|
||||
|
|
@ -266,8 +456,7 @@ class functional():
|
|||
self.read_ports.append(read_port_num)
|
||||
|
||||
def write_functional_stimulus(self):
|
||||
#Write Stimulus
|
||||
|
||||
""" Writes SPICE stimulus. """
|
||||
self.obtain_cycle_times(self.period)
|
||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||
self.sf = open(temp_stim,"w")
|
||||
|
|
@ -294,7 +483,7 @@ class functional():
|
|||
self.sf.write("\n* SRAM output loads\n")
|
||||
for port in range(self.total_read):
|
||||
for bit in range(self.word_size):
|
||||
self.sf.write("CD{0}{1} DOUT{0}[{1}] 0 {2}f\n".format(port, bit, self.load))
|
||||
self.sf.write("CD{0}{1} DOUT{0}[{1}] 0 {2}f\n".format(self.read_index[port], bit, self.load))
|
||||
|
||||
# Generate data input bits
|
||||
self.sf.write("\n* Generation of data and address signals\n")
|
||||
|
|
@ -304,9 +493,9 @@ class functional():
|
|||
self.stim.gen_pwl(sig_name, self.cycle_times, self.data[port][bit], self.period, self.slew, 0.05)
|
||||
|
||||
# Generate address bits
|
||||
for port in range(self.total_write):
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
sig_name = "A{0}[{1}]".format(port,bit)
|
||||
sig_name = "ADDR{0}[{1}]".format(port,bit)
|
||||
self.stim.gen_pwl(sig_name, self.cycle_times, self.addresses[port][bit], self.period, self.slew, 0.05)
|
||||
|
||||
# Generate control signals
|
||||
|
|
@ -328,12 +517,17 @@ class functional():
|
|||
t_fall=self.slew)
|
||||
|
||||
# Generate DOUT value measurements
|
||||
if self.total_ports > 1:
|
||||
num_tests = 3
|
||||
else:
|
||||
num_tests = 2
|
||||
|
||||
self.sf.write("\n * Generation of dout measurements\n")
|
||||
for i in range(self.num_checks):
|
||||
for i in range(num_tests*self.num_checks):
|
||||
for port in range(self.total_read):
|
||||
for bit in range(self.word_size):
|
||||
self.stim.gen_meas_value(meas_name="VDOUT{0}[{1}]".format(port,bit),
|
||||
dout="DOUT{0}[{1}]".format(port,bit),
|
||||
self.stim.gen_meas_value(meas_name="VDOUT{0}[{1}]_ch{2}".format(self.read_index[port],bit,i),
|
||||
dout="DOUT{0}[{1}]".format(self.read_index[port],bit),
|
||||
eo_period=self.eo_period[i],
|
||||
slew=self.slew,
|
||||
setup=0.05)
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ class psram_func_test(openram_test):
|
|||
|
||||
from sram import sram
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=1,
|
||||
c = sram_config(word_size=2,
|
||||
num_words=16,
|
||||
num_banks=1)
|
||||
c.words_per_row=1
|
||||
|
||||
"""
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
OPTS.num_r_ports = 0
|
||||
|
|
@ -54,7 +54,7 @@ class psram_func_test(openram_test):
|
|||
"""
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_w_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_r_ports = 2
|
||||
|
||||
debug.info(1, "Functional test for 1bit, 16word SRAM, with 1 bank. Multiport with {}RW {}W {}R.".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports))
|
||||
s = sram(c, name="sram1")
|
||||
|
|
@ -64,10 +64,12 @@ class psram_func_test(openram_test):
|
|||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
f.run()
|
||||
"""
|
||||
(fail,error) = f.multiport_run()
|
||||
|
||||
#globals.end_openram()
|
||||
if fail:
|
||||
print(error)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# instantiate a copdsay of the class to actually run the test
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -41,7 +41,10 @@ class sram_func_test(openram_test):
|
|||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
f.run()
|
||||
(fail, error) = f.run()
|
||||
|
||||
if fail:
|
||||
print(error)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue