mirror of https://github.com/VLSIDA/OpenRAM.git
Improvements to functional test. Now will read or write in a random sequence, using randomly generated words and addresses, and using random ports in the multiported cases. Functional test still has some bugs that are being worked out so it will sometimes fail and sometimes not fail.
This commit is contained in:
parent
7b4e001885
commit
6ef1a3c755
|
|
@ -63,16 +63,16 @@ class design(hierarchy_design):
|
|||
port_number = 0
|
||||
|
||||
for port in range(OPTS.num_rw_ports):
|
||||
self.write_index.append("{}".format(port_number))
|
||||
self.read_index.append("{}".format(port_number))
|
||||
self.write_index.append(port_number)
|
||||
self.read_index.append(port_number)
|
||||
self.port_id.append("rw")
|
||||
port_number += 1
|
||||
for port in range(OPTS.num_w_ports):
|
||||
self.write_index.append("{}".format(port_number))
|
||||
self.write_index.append(port_number)
|
||||
self.port_id.append("w")
|
||||
port_number += 1
|
||||
for port in range(OPTS.num_r_ports):
|
||||
self.read_index.append("{}".format(port_number))
|
||||
self.read_index.append(port_number)
|
||||
self.port_id.append("r")
|
||||
port_number += 1
|
||||
|
||||
|
|
|
|||
|
|
@ -24,325 +24,136 @@ class functional(simulation):
|
|||
self.set_corner(corner)
|
||||
self.set_spice_constants()
|
||||
self.set_stimulus_variables()
|
||||
self.create_signal_names()
|
||||
|
||||
# Number of checks can be changed
|
||||
self.num_checks = 1
|
||||
self.cycles = 0
|
||||
self.eo_period = []
|
||||
|
||||
# set to 1 if functional simulation fails during any check
|
||||
self.functional_fail = 0
|
||||
self.error = ""
|
||||
self.num_cycles = 2
|
||||
self.stored_words = {}
|
||||
self.write_check = []
|
||||
self.read_check = []
|
||||
|
||||
def run(self):
|
||||
""" Main function to generate random writes/reads, run spice, and analyze results """
|
||||
self.noop()
|
||||
|
||||
self.overwrite_test()
|
||||
self.write_read_test()
|
||||
|
||||
self.noop()
|
||||
|
||||
# Generate a random sequence of reads and writes
|
||||
self.write_random_memory_sequence()
|
||||
|
||||
# Run SPICE simulation
|
||||
self.write_functional_stimulus()
|
||||
self.stim.run_sim()
|
||||
|
||||
# Extrat DOUT values from spice timing.lis
|
||||
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}.ck{2}".format(self.read_index[port],bit,i))
|
||||
if value > 0.9 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "1" + self.sp_read_value[port]
|
||||
elif value < 0.1 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "0" + self.sp_read_value[port]
|
||||
else:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: DOUT{0}[{1}] value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(port,
|
||||
bit,
|
||||
value,
|
||||
self.eo_period[i],
|
||||
0.1*self.vdd_voltage,
|
||||
0.9*self.vdd_voltage)
|
||||
|
||||
if self.functional_fail:
|
||||
return (self.functional_fail, self.error)
|
||||
|
||||
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: Overwrite Test - 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: Standard W/R Test - 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 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()
|
||||
|
||||
self.noop()
|
||||
|
||||
# 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}.ck{2}".format(self.read_index[port],bit,i))
|
||||
if value > 0.9 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "1" + self.sp_read_value[port]
|
||||
elif value < 0.1 * self.vdd_voltage:
|
||||
self.sp_read_value[port] = "0" + self.sp_read_value[port]
|
||||
else:
|
||||
self.functional_fail = 1
|
||||
self.error ="FAILED: DOUT{0}[{1}] value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(port,
|
||||
bit,
|
||||
value,
|
||||
self.eo_period[i],
|
||||
0.1*self.vdd_voltage,
|
||||
0.9*self.vdd_voltage)
|
||||
|
||||
if self.functional_fail:
|
||||
return (self.functional_fail, self.error)
|
||||
|
||||
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: Multi Test - 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: Overwrite Test - 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: Standard W/R Test - 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(2):
|
||||
word = self.gen_data()
|
||||
self.write(addr,word)
|
||||
self.stored_values_over_test.append(word)
|
||||
# read DOUT values from SPICE simulation. If the values do not fall within the noise margins, return the error.
|
||||
(success, error) = self.read_stim_results()
|
||||
if not success:
|
||||
return (0, error)
|
||||
|
||||
# Read word from address (use all RW and R ports)
|
||||
self.read(addr,word)
|
||||
# Check read values with written values. If the values do not match, return an error.
|
||||
return self.check_stim_results()
|
||||
|
||||
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)]
|
||||
def write_random_memory_sequence(self):
|
||||
rw_ops = ["noop", "write", "read"]
|
||||
w_ops = ["noop", "write"]
|
||||
r_ops = ["noop", "read"]
|
||||
rw_read_data = "0"*self.word_size
|
||||
check = 0
|
||||
|
||||
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.cycle_times.append(self.t_current)
|
||||
self.t_current += self.period
|
||||
# First cycle idle
|
||||
self.add_noop_all_ports("Idle at time {0}n".format(self.t_current),
|
||||
"0"*self.addr_size, "0"*self.word_size)
|
||||
|
||||
for port in range(self.total_ports):
|
||||
self.csb_values[port].append(1)
|
||||
|
||||
for port in range(self.total_write):
|
||||
self.web_values[port].append(1)
|
||||
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
self.addr_values[port][bit].append(0)
|
||||
# Write at least once
|
||||
addr = self.gen_addr()
|
||||
word = self.gen_data()
|
||||
self.add_write("Writing {0} to address {1} (from port {2}) at time {3}n".format(word, addr, 0, self.t_current),
|
||||
addr, word, 0)
|
||||
self.stored_words[addr] = word
|
||||
|
||||
for port in range(self.total_write):
|
||||
for bit in range(self.word_size):
|
||||
self.data_values[port][bit].append(0)
|
||||
|
||||
def write(self,addr,word,write_port=0):
|
||||
""" Generates signals for a write cycle. """
|
||||
debug.info(1, "Writing {0} to address {1} in cycle {2}...".format(word,addr,self.cycles))
|
||||
self.cycles += 1
|
||||
self.cycle_times.append(self.t_current)
|
||||
self.t_current += self.period
|
||||
|
||||
# Write control signals
|
||||
for port in range(self.total_ports):
|
||||
if port == write_port:
|
||||
self.csb_values[port].append(0)
|
||||
else:
|
||||
self.csb_values[port].append(1)
|
||||
|
||||
for port in range(self.total_write):
|
||||
if port == write_port:
|
||||
self.web_values[port].append(0)
|
||||
else:
|
||||
self.web_values[port].append(1)
|
||||
|
||||
# Write address
|
||||
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.addr_values[port][bit].append(current_address_bit)
|
||||
|
||||
# Write data
|
||||
for port in range(self.total_write):
|
||||
for bit in range(self.word_size):
|
||||
current_word_bit = int(word[self.word_size-1-bit])
|
||||
self.data_values[port][bit].append(current_word_bit)
|
||||
|
||||
def read(self,addr,word):
|
||||
""" Generates signals for a read cycle. """
|
||||
debug.info(1, "Reading {0} from address {1} in cycle {2}...".format(word,addr,self.cycles))
|
||||
self.cycles += 1
|
||||
self.cycle_times.append(self.t_current)
|
||||
self.t_current += self.period
|
||||
|
||||
# Read control signals
|
||||
# Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously.
|
||||
# This will test the viablilty of the transistor sizing in the bitcell.
|
||||
for port in range(self.total_ports):
|
||||
if self.port_id[port] == "w":
|
||||
self.csb_values[port].append(1)
|
||||
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
||||
else:
|
||||
self.csb_values[port].append(0)
|
||||
|
||||
for port in range(self.total_write):
|
||||
self.web_values[port].append(1)
|
||||
|
||||
# Read address
|
||||
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.addr_values[port][bit].append(current_address_bit)
|
||||
|
||||
# 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_values[port][bit].append(0)
|
||||
|
||||
# Record the end of the period that the read operation occured in
|
||||
self.eo_period.append(self.t_current)
|
||||
|
||||
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 cycle {2}...".format(words,addrs,self.cycles))
|
||||
self.cycles += 1
|
||||
self.add_read_one_port("Reading {0} from address {1} (from port {2}) at time {3}n".format(word, addr, port, self.t_current),
|
||||
addr, rw_read_data, port)
|
||||
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
|
||||
check += 1
|
||||
self.cycle_times.append(self.t_current)
|
||||
self.t_current += self.period
|
||||
|
||||
# Read control signals
|
||||
for port in range(self.total_ports):
|
||||
if self.port_id[port] == "w":
|
||||
self.csb_values[port].append(1)
|
||||
else:
|
||||
self.csb_values[port].append(0)
|
||||
|
||||
for port in range(self.total_write):
|
||||
self.web_values[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
|
||||
# Perform a random sequence of writes and reads on random ports, using random addresses and random words
|
||||
for i in range(self.num_cycles):
|
||||
w_addrs = []
|
||||
for port in range(self.total_ports):
|
||||
if self.port_id[port] == "rw":
|
||||
op = random.choice(rw_ops)
|
||||
elif self.port_id[port] == "w":
|
||||
op = random.choice(w_ops)
|
||||
else:
|
||||
current_address_bit = int(addrs[addr_index][self.addr_size-1-bit])
|
||||
op = random.choice(r_ops)
|
||||
|
||||
if op == "noop":
|
||||
addr = "0"*self.addr_size
|
||||
word = "0"*self.word_size
|
||||
self.add_noop_one_port(addr, word, port)
|
||||
elif op == "write":
|
||||
addr = self.gen_addr()
|
||||
word = self.gen_data()
|
||||
# two ports cannot write to the same address
|
||||
if addr in w_addrs:
|
||||
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
||||
else:
|
||||
self.add_write_one_port("Writing {0} to address {1} (from port {2}) at time {3}n".format(word, addr, port, self.t_current),
|
||||
addr, word, port)
|
||||
self.stored_words[addr] = word
|
||||
w_addrs.append(addr)
|
||||
else:
|
||||
(addr,word) = random.choice(list(self.stored_words.items()))
|
||||
# cannot read from an address that is currently being written to
|
||||
if addr in w_addrs:
|
||||
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
|
||||
else:
|
||||
self.add_read_one_port("Reading {0} from address {1} (from port {2}) at time {3}n".format(word, addr, port, self.t_current),
|
||||
addr, rw_read_data, port)
|
||||
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
|
||||
check += 1
|
||||
|
||||
self.addr_values[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_values[port][bit].append(0)
|
||||
self.cycle_times.append(self.t_current)
|
||||
self.t_current += self.period
|
||||
|
||||
# Record the end of the period that the read operation occured in
|
||||
self.eo_period.append(self.t_current)
|
||||
# Last cycle idle needed to correctly measure the value on the second to last clock edge
|
||||
self.add_noop_all_ports("Idle at time {0}n".format(self.t_current),
|
||||
"0"*self.addr_size, "0"*self.word_size)
|
||||
|
||||
def read_stim_results(self):
|
||||
# Extrat DOUT values from spice timing.lis
|
||||
for (word, dout_port, eo_period, check) in self.write_check:
|
||||
sp_read_value = ""
|
||||
for bit in range(self.word_size):
|
||||
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check))
|
||||
if value > 0.9 * self.vdd_voltage:
|
||||
sp_read_value = "1" + sp_read_value
|
||||
elif value < 0.1 * self.vdd_voltage:
|
||||
sp_read_value = "0" + sp_read_value
|
||||
else:
|
||||
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
|
||||
bit,
|
||||
value,
|
||||
eo_period,
|
||||
0.1*self.vdd_voltage,
|
||||
0.9*self.vdd_voltage)
|
||||
return (0, error)
|
||||
|
||||
self.read_check.append([sp_read_value, dout_port, eo_period, check])
|
||||
return (1, "SUCCESS")
|
||||
|
||||
def check_stim_results(self):
|
||||
for i in range(len(self.write_check)):
|
||||
if self.write_check[i][0] != self.read_check[i][0]:
|
||||
error = "FAILED: {0} value {1} does not match written value {2} read at time {3}n".format(self.read_check[i][1],
|
||||
self.read_check[i][0],
|
||||
self.write_check[i][0],
|
||||
self.read_check[i][2])
|
||||
return(0, error)
|
||||
return(1, "SUCCESS")
|
||||
|
||||
def gen_data(self):
|
||||
""" Generates a random word to write. """
|
||||
|
|
@ -375,18 +186,14 @@ class functional(simulation):
|
|||
|
||||
#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. """
|
||||
t_current = 0
|
||||
self.cycle_times = []
|
||||
for i in range(self.cycles):
|
||||
self.cycle_times.append(t_current)
|
||||
t_current += period
|
||||
|
||||
def create_signal_names(self):
|
||||
self.addr_name = "A"
|
||||
self.din_name = "DIN"
|
||||
self.dout_name = "DOUT"
|
||||
|
||||
def write_functional_stimulus(self):
|
||||
""" Writes SPICE stimulus. """
|
||||
#self.obtain_cycle_times(self.period)
|
||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||
self.sf = open(temp_stim,"w")
|
||||
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period))
|
||||
|
|
@ -403,30 +210,31 @@ class functional(simulation):
|
|||
|
||||
#Instantiate the SRAM
|
||||
self.sf.write("\n* Instantiation of the SRAM\n")
|
||||
self.stim.inst_full_sram(sram=self.sram,
|
||||
sram_name=self.name)
|
||||
#self.stim.inst_sram(abits=self.addr_size,
|
||||
# dbits=self.word_size,
|
||||
# port_info=(self.total_ports,self.total_write,self.read_index,self.write_index),
|
||||
# sram_name=self.name)
|
||||
self.stim.inst_sram(sram=self.sram,
|
||||
port_signal_names=(self.addr_name,self.din_name,self.dout_name),
|
||||
port_info=(self.total_ports, self.write_index, self.read_index),
|
||||
abits=self.addr_size,
|
||||
dbits=self.word_size,
|
||||
sram_name=self.name)
|
||||
|
||||
# Add load capacitance to each of the read ports
|
||||
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(self.read_index[port], bit, self.load))
|
||||
sig_name="{0}{1}_{2} ".format(self.dout_name, self.read_index[port], bit)
|
||||
self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(self.read_index[port], bit, sig_name, self.load))
|
||||
|
||||
# Generate data input bits
|
||||
self.sf.write("\n* Generation of data and address signals\n")
|
||||
for port in range(self.total_write):
|
||||
for bit in range(self.word_size):
|
||||
sig_name = "DIN{0}[{1}]".format(port,bit)
|
||||
sig_name="{0}{1}_{2} ".format(self.din_name, port, bit)
|
||||
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05)
|
||||
|
||||
# Generate address bits
|
||||
for port in range(self.total_ports):
|
||||
for bit in range(self.addr_size):
|
||||
sig_name = "ADDR{0}[{1}]".format(port,bit)
|
||||
sig_name="{0}{1}_{2} ".format(self.addr_name, port, bit)
|
||||
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][bit], self.period, self.slew, 0.05)
|
||||
|
||||
# Generate control signals
|
||||
|
|
@ -434,12 +242,12 @@ class functional(simulation):
|
|||
for port in range(self.total_ports):
|
||||
self.stim.gen_pwl("CSB{}".format(port), self.cycle_times , self.csb_values[port], self.period, self.slew, 0.05)
|
||||
|
||||
for port in range(self.total_write):
|
||||
for port in range(self.num_rw_ports):
|
||||
self.stim.gen_pwl("WEB{}".format(port), self.cycle_times , self.web_values[port], self.period, self.slew, 0.05)
|
||||
|
||||
# Generate CLK signals
|
||||
for port in range(self.total_ports):
|
||||
self.stim.gen_pulse(sig_name="CLK{}".format(port),
|
||||
self.stim.gen_pulse(sig_name="{0}{1}".format(tech.spice["clk"], port),
|
||||
v1=self.gnd_voltage,
|
||||
v2=self.vdd_voltage,
|
||||
offset=self.period,
|
||||
|
|
@ -448,21 +256,15 @@ class functional(simulation):
|
|||
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(num_tests*self.num_checks):
|
||||
t_intital = self.eo_period[i] - 0.01*self.period
|
||||
t_final = self.eo_period[i] + 0.01*self.period
|
||||
for port in range(self.total_read):
|
||||
for bit in range(self.word_size):
|
||||
self.stim.gen_meas_value(meas_name="VDOUT{0}[{1}]ck{2}".format(self.read_index[port],bit,i),
|
||||
dout="DOUT{0}[{1}]".format(self.read_index[port],bit),
|
||||
t_intital=t_intital,
|
||||
t_final=t_final)
|
||||
for (word, dout_port, eo_period, check) in self.write_check:
|
||||
t_intital = eo_period - 0.01*self.period
|
||||
t_final = eo_period + 0.01*self.period
|
||||
for bit in range(self.word_size):
|
||||
self.stim.gen_meas_value(meas_name="V{0}_{1}ck{2}".format(dout_port,bit,check),
|
||||
dout="{0}_{1}".format(dout_port,bit),
|
||||
t_intital=t_intital,
|
||||
t_final=t_final)
|
||||
|
||||
self.stim.write_control(self.cycle_times[-1] + self.period)
|
||||
self.sf.close()
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ class simulation():
|
|||
self.total_read = self.sram.total_read
|
||||
self.read_index = self.sram.read_index
|
||||
self.write_index = self.sram.write_index
|
||||
self.num_rw_ports = self.sram.num_rw_ports
|
||||
self.port_id = self.sram.port_id
|
||||
|
||||
def set_corner(self,corner):
|
||||
|
|
@ -49,11 +50,14 @@ class simulation():
|
|||
|
||||
# control signals: only one cs_b for entire multiported sram, one we_b for each write port
|
||||
self.csb_values = [[] for port in range(self.total_ports)]
|
||||
self.web_values = [[] for port in range(self.total_write)]
|
||||
self.web_values = [[] for port in range(self.num_rw_ports)]
|
||||
|
||||
# Three dimensional list to handle each addr and data bits for wach port over the number of checks
|
||||
self.addr_values = [[[] for bit in range(self.addr_size)] for port in range(self.total_ports)]
|
||||
self.data_values = [[[] for bit in range(self.word_size)] for port in range(self.total_write)]
|
||||
|
||||
# For generating comments in SPICE stimulus
|
||||
self.cycle_comments = []
|
||||
|
||||
def add_control_one_port(self, port, op):
|
||||
"""Appends control signals for operation to a given port"""
|
||||
|
|
@ -61,7 +65,7 @@ class simulation():
|
|||
web_val = 1
|
||||
csb_val = 1
|
||||
if op == "read":
|
||||
self.cs_b = 0
|
||||
csb_val = 0
|
||||
elif op == "write":
|
||||
csb_val = 0
|
||||
web_val = 0
|
||||
|
|
@ -105,7 +109,8 @@ class simulation():
|
|||
|
||||
def add_write(self, comment, address, data, port):
|
||||
""" Add the control values for a write cycle. """
|
||||
debug.check(port in self.write_ports, "Cannot add read cycle to a read port.")
|
||||
debug.info(1, comment)
|
||||
debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index))
|
||||
self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
||||
self.t_current,
|
||||
comment,
|
||||
|
|
@ -120,27 +125,14 @@ class simulation():
|
|||
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
|
||||
noop_data = "0"*self.word_size
|
||||
#Add noops to all other ports.
|
||||
for unselected_port in range(self.total_port_num):
|
||||
for unselected_port in range(self.total_ports):
|
||||
if unselected_port != port:
|
||||
self.add_noop_one_port(address, noop_data, unselected_port)
|
||||
|
||||
def add_write_one_port(self, comment, address, data, port):
|
||||
""" Add the control values for a write cycle. """
|
||||
debug.check(port in self.write_ports, "Cannot add read cycle to a read port.")
|
||||
self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
||||
self.t_current,
|
||||
comment,
|
||||
port))
|
||||
self.cycle_times.append(self.t_current)
|
||||
self.t_current += self.period
|
||||
|
||||
self.add_control_one_port(port, "write")
|
||||
self.add_data(data,port)
|
||||
self.add_address(address,port)
|
||||
|
||||
def add_read(self, comment, address, data, port):
|
||||
""" Add the control values for a read cycle. """
|
||||
debug.check(port in self.read_ports, "Cannot add read cycle to a write port.")
|
||||
debug.info(1, comment)
|
||||
debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index))
|
||||
self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
||||
self.t_current,
|
||||
comment,
|
||||
|
|
@ -150,48 +142,59 @@ class simulation():
|
|||
self.add_control_one_port(port, "read")
|
||||
|
||||
#If the port is also a readwrite then add data.
|
||||
if port in self.write_ports:
|
||||
if port in self.write_index:
|
||||
self.add_data(data,port)
|
||||
self.add_address(address, port)
|
||||
|
||||
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
|
||||
noop_data = "0"*self.word_size
|
||||
#Add noops to all other ports.
|
||||
for unselected_port in range(self.total_port_num):
|
||||
for unselected_port in range(self.total_ports):
|
||||
if unselected_port != port:
|
||||
self.add_noop_one_port(address, noop_data, unselected_port)
|
||||
|
||||
def add_read_one_port(self, comment, address, data, port):
|
||||
""" Add the control values for a read cycle. """
|
||||
debug.check(port in self.read_ports, "Cannot add read cycle to a write port.")
|
||||
self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
||||
self.t_current,
|
||||
comment,
|
||||
port))
|
||||
self.cycle_times.append(self.t_current)
|
||||
self.t_current += self.period
|
||||
self.add_control_one_port(port, "read")
|
||||
|
||||
#If the port is also a readwrite then add data.
|
||||
if port in self.write_ports:
|
||||
self.add_data(data,port)
|
||||
self.add_address(address, port)
|
||||
|
||||
def add_noop_one_port(self, address, data, port):
|
||||
""" Add the control values for a noop to a single port. """
|
||||
#This is to be used as a helper function for the other add functions. Cycle and comments are omitted.
|
||||
self.add_control_one_port(port, "noop")
|
||||
if port in self.write_ports:
|
||||
self.add_data(data,port)
|
||||
self.add_address(address, port)
|
||||
|
||||
|
||||
def add_noop_all_ports(self, comment, address, data):
|
||||
""" Add the control values for a noop to all ports. """
|
||||
debug.info(1, comment)
|
||||
self.cycle_comments.append("Cycle {0:2d}\tPort All\t{1:5.2f}ns:\t{2}".format(len(self.cycle_times),
|
||||
self.t_current,
|
||||
comment))
|
||||
self.cycle_times.append(self.t_current)
|
||||
self.t_current += self.period
|
||||
|
||||
for port in range(self.total_port_num):
|
||||
self.add_noop_one_port(address, data, port)
|
||||
for port in range(self.total_ports):
|
||||
self.add_noop_one_port(address, data, port)
|
||||
|
||||
def add_write_one_port(self, comment, address, data, port):
|
||||
""" Add the control values for a write cycle. Does not increment the period. """
|
||||
debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index))
|
||||
debug.info(1, comment)
|
||||
self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
||||
self.t_current,
|
||||
comment,
|
||||
port))
|
||||
self.add_control_one_port(port, "write")
|
||||
self.add_data(data,port)
|
||||
self.add_address(address,port)
|
||||
|
||||
def add_read_one_port(self, comment, address, data, port):
|
||||
""" Add the control values for a read cycle. Does not increment the period. """
|
||||
debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index))
|
||||
debug.info(1, comment)
|
||||
self.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
|
||||
self.t_current,
|
||||
comment,
|
||||
port))
|
||||
self.add_control_one_port(port, "read")
|
||||
#If the port is also a readwrite then add data.
|
||||
if port in self.write_index:
|
||||
self.add_data(data,port)
|
||||
self.add_address(address, port)
|
||||
|
||||
def add_noop_one_port(self, address, data, port):
|
||||
""" Add the control values for a noop to a single port. Does not increment the period. """
|
||||
self.add_control_one_port(port, "noop")
|
||||
if port in self.write_index:
|
||||
self.add_data(data,port)
|
||||
self.add_address(address, port)
|
||||
|
||||
|
|
@ -28,24 +28,12 @@ class stimuli():
|
|||
|
||||
(self.process, self.voltage, self.temperature) = corner
|
||||
self.device_models = tech.spice["fet_models"][self.process]
|
||||
|
||||
|
||||
def inst_full_sram(self, sram, sram_name):
|
||||
""" Function to instatiate an SRAM subckt. """
|
||||
self.sf.write("Xsram ")
|
||||
for pin in sram.pins:
|
||||
if (pin=="vdd") or (pin=="gnd"):
|
||||
self.sf.write("{0} ".format(pin))
|
||||
else:
|
||||
self.sf.write("{0} ".format(pin.upper()))
|
||||
self.sf.write("{0}\n".format(sram_name))
|
||||
|
||||
|
||||
def inst_sram(self, sram, port_signal_names, port_info, abits, dbits, sram_name):
|
||||
""" Function to instatiate an SRAM subckt. """
|
||||
pin_names = self.gen_pin_names(port_signal_names, port_info, abits, dbits)
|
||||
#Only checking length. This should check functionality as well (TODO) and/or import that information from the SRAM
|
||||
debug.check(len(sram.pins) == len(pin_names), "Number of pins generated for characterization do match pins of SRAM")
|
||||
debug.check(len(sram.pins) == len(pin_names), "Number of pins generated for characterization do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(sram.pins,pin_names))
|
||||
|
||||
self.sf.write("Xsram ")
|
||||
for pin in pin_names:
|
||||
|
|
@ -59,27 +47,27 @@ class stimuli():
|
|||
#will cause issues here.
|
||||
pin_names = []
|
||||
(addr_name, din_name, dout_name) = port_signal_names
|
||||
(total_port_num, write_ports, read_ports) = port_info
|
||||
(total_ports, write_index, read_index) = port_info
|
||||
|
||||
for write_input in write_ports:
|
||||
for write_input in write_index:
|
||||
for i in range(dbits):
|
||||
pin_names.append("{0}{1}_{2}".format(din_name,write_input, i))
|
||||
|
||||
for port in range(total_port_num):
|
||||
for port in range(total_ports):
|
||||
for i in range(abits):
|
||||
pin_names.append("{0}{1}_{2}".format(addr_name,port,i))
|
||||
|
||||
#Control signals not finalized.
|
||||
for port in range(total_port_num):
|
||||
for port in range(total_ports):
|
||||
pin_names.append("CSB{0}".format(port))
|
||||
for port in range(total_port_num):
|
||||
if port in read_ports and port in write_ports:
|
||||
for port in range(total_ports):
|
||||
if (port in read_index) and (port in write_index):
|
||||
pin_names.append("WEB{0}".format(port))
|
||||
|
||||
for port in range(total_port_num):
|
||||
for port in range(total_ports):
|
||||
pin_names.append("{0}{1}".format(tech.spice["clk"], port))
|
||||
|
||||
for read_output in read_ports:
|
||||
for read_output in read_index:
|
||||
for i in range(dbits):
|
||||
pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i))
|
||||
|
||||
|
|
@ -179,7 +167,7 @@ class stimuli():
|
|||
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.")
|
||||
debug.check(len(clk_times)==len(data_values),"Clock and data value lengths don't match. {0} clock values, {1} data values for {2}".format(len(clk_times), len(data_values), sig_name))
|
||||
|
||||
# shift signal times earlier for setup time
|
||||
times = np.array(clk_times) - setup*period
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import globals
|
|||
from globals import OPTS
|
||||
import debug
|
||||
|
||||
@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
|
||||
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
|
||||
class sram_1bank_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
|
|
@ -40,7 +40,7 @@ class sram_1bank_test(openram_test):
|
|||
debug.info(1, "Single bank two way column mux with control logic")
|
||||
a = sram(c, "sram2")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
"""
|
||||
c.num_words=64
|
||||
c.words_per_row=4
|
||||
debug.info(1, "Single bank, four way column mux with control logic")
|
||||
|
|
@ -69,7 +69,7 @@ class sram_1bank_test(openram_test):
|
|||
debug.info(1, "Single bank, no column mux with control logic")
|
||||
a = sram(c, "sram1")
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
"""
|
||||
OPTS.num_rw_ports = 0
|
||||
OPTS.num_w_ports = 2
|
||||
OPTS.num_r_ports = 2
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ class psram_func_test(openram_test):
|
|||
c.words_per_row=2
|
||||
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_w_ports = 2
|
||||
OPTS.num_r_ports = 4
|
||||
OPTS.num_w_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
|
||||
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")
|
||||
|
|
@ -49,9 +49,10 @@ class psram_func_test(openram_test):
|
|||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
(success,error) = f.multiport_run()
|
||||
f.num_cycles = 5
|
||||
(fail,error) = f.run()
|
||||
|
||||
self.assertTrue(not success,error)
|
||||
self.assertTrue(fail,error)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -41,9 +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)
|
||||
(success, error) = f.run()
|
||||
f.num_cycles = 10
|
||||
(fail, error) = f.run()
|
||||
|
||||
self.assertTrue(not success,error)
|
||||
self.assertTrue(fail,error)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ class psram_func_test(openram_test):
|
|||
c.words_per_row=2
|
||||
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_w_ports = 2
|
||||
OPTS.num_r_ports = 4
|
||||
OPTS.num_w_ports = 1
|
||||
OPTS.num_r_ports = 1
|
||||
|
||||
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")
|
||||
|
|
@ -49,9 +49,10 @@ class psram_func_test(openram_test):
|
|||
|
||||
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
|
||||
f = functional(s.s, tempspice, corner)
|
||||
(success,error) = f.multiport_run()
|
||||
f.num_cycles = 5
|
||||
(fail,error) = f.run()
|
||||
|
||||
self.assertTrue(not success,error)
|
||||
self.assertTrue(fail,error)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
|
|
@ -41,9 +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)
|
||||
(success, error) = f.run()
|
||||
f.num_cycles = 10
|
||||
(fail, error) = f.run()
|
||||
|
||||
self.assertTrue(not success,error)
|
||||
self.assertTrue(fail,error)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue