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:
Michael Timothy Grimes 2018-10-08 06:34:36 -07:00
parent 7b4e001885
commit 6ef1a3c755
9 changed files with 217 additions and 420 deletions

View File

@ -63,16 +63,16 @@ class design(hierarchy_design):
port_number = 0 port_number = 0
for port in range(OPTS.num_rw_ports): for port in range(OPTS.num_rw_ports):
self.write_index.append("{}".format(port_number)) self.write_index.append(port_number)
self.read_index.append("{}".format(port_number)) self.read_index.append(port_number)
self.port_id.append("rw") self.port_id.append("rw")
port_number += 1 port_number += 1
for port in range(OPTS.num_w_ports): 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") self.port_id.append("w")
port_number += 1 port_number += 1
for port in range(OPTS.num_r_ports): 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") self.port_id.append("r")
port_number += 1 port_number += 1

View File

@ -24,325 +24,136 @@ class functional(simulation):
self.set_corner(corner) self.set_corner(corner)
self.set_spice_constants() self.set_spice_constants()
self.set_stimulus_variables() self.set_stimulus_variables()
self.create_signal_names()
# Number of checks can be changed # Number of checks can be changed
self.num_checks = 1 self.num_cycles = 2
self.cycles = 0 self.stored_words = {}
self.eo_period = [] self.write_check = []
self.read_check = []
# set to 1 if functional simulation fails during any check
self.functional_fail = 0
self.error = ""
def run(self): def run(self):
""" Main function to generate random writes/reads, run spice, and analyze results """ # Generate a random sequence of reads and writes
self.noop() self.write_random_memory_sequence()
self.overwrite_test()
self.write_read_test()
self.noop()
# Run SPICE simulation # Run SPICE simulation
self.write_functional_stimulus() self.write_functional_stimulus()
self.stim.run_sim() self.stim.run_sim()
# Extrat DOUT values from spice timing.lis # read DOUT values from SPICE simulation. If the values do not fall within the noise margins, return the error.
for i in range(2*self.num_checks): (success, error) = self.read_stim_results()
self.sp_read_value = ["" for port in range(self.total_read)] if not success:
for port in range(self.total_read): return (0, error)
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 word from address (use all RW and R ports) # Check read values with written values. If the values do not match, return an error.
self.read(addr,word) return self.check_stim_results()
def write_read_test(self): def write_random_memory_sequence(self):
""" A standard functional test for writing to an address and reading back the value. """ rw_ops = ["noop", "write", "read"]
self.stored_values_test = [] w_ops = ["noop", "write"]
self.read_values_test = [[] for i in range(self.num_checks)] r_ops = ["noop", "read"]
rw_read_data = "0"*self.word_size
check = 0
for i in range(self.num_checks): # First cycle idle
# Write a random word to a random address self.add_noop_all_ports("Idle at time {0}n".format(self.t_current),
addr = self.gen_addr() "0"*self.addr_size, "0"*self.word_size)
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
for port in range(self.total_ports): # Write at least once
self.csb_values[port].append(1) addr = self.gen_addr()
word = self.gen_data()
for port in range(self.total_write): self.add_write("Writing {0} to address {1} (from port {2}) at time {3}n".format(word, addr, 0, self.t_current),
self.web_values[port].append(1) addr, word, 0)
self.stored_words[addr] = word
for port in range(self.total_ports):
for bit in range(self.addr_size):
self.addr_values[port][bit].append(0)
for port in range(self.total_write): # 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.
for bit in range(self.word_size): # This will test the viablilty of the transistor sizing in the bitcell.
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
for port in range(self.total_ports): for port in range(self.total_ports):
if self.port_id[port] == "w": 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: else:
self.csb_values[port].append(0) 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)
for port in range(self.total_write): self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
self.web_values[port].append(1) check += 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.cycle_times.append(self.t_current) self.cycle_times.append(self.t_current)
self.t_current += self.period self.t_current += self.period
# Read control signals # Perform a random sequence of writes and reads on random ports, using random addresses and random words
for port in range(self.total_ports): for i in range(self.num_cycles):
if self.port_id[port] == "w": w_addrs = []
self.csb_values[port].append(1) for port in range(self.total_ports):
else: if self.port_id[port] == "rw":
self.csb_values[port].append(0) op = random.choice(rw_ops)
elif self.port_id[port] == "w":
for port in range(self.total_write): op = random.choice(w_ops)
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
else: 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) self.cycle_times.append(self.t_current)
self.t_current += self.period
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)
# Record the end of the period that the read operation occured in # Last cycle idle needed to correctly measure the value on the second to last clock edge
self.eo_period.append(self.t_current) 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): def gen_data(self):
""" Generates a random word to write. """ """ Generates a random word to write. """
@ -375,18 +186,14 @@ class functional(simulation):
#print("Binary Conversion: {} to {}".format(value, new_value)) #print("Binary Conversion: {} to {}".format(value, new_value))
return new_value return new_value
def obtain_cycle_times(self,period): def create_signal_names(self):
""" Generate clock cycle times based on period and number of cycles. """ self.addr_name = "A"
t_current = 0 self.din_name = "DIN"
self.cycle_times = [] self.dout_name = "DOUT"
for i in range(self.cycles):
self.cycle_times.append(t_current)
t_current += period
def write_functional_stimulus(self): def write_functional_stimulus(self):
""" Writes SPICE stimulus. """ """ Writes SPICE stimulus. """
#self.obtain_cycle_times(self.period)
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.sf = open(temp_stim,"w") self.sf = open(temp_stim,"w")
self.sf.write("* Functional test stimulus file for {}ns period\n\n".format(self.period)) 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 #Instantiate the SRAM
self.sf.write("\n* Instantiation of the SRAM\n") self.sf.write("\n* Instantiation of the SRAM\n")
self.stim.inst_full_sram(sram=self.sram, self.stim.inst_sram(sram=self.sram,
sram_name=self.name) port_signal_names=(self.addr_name,self.din_name,self.dout_name),
#self.stim.inst_sram(abits=self.addr_size, port_info=(self.total_ports, self.write_index, self.read_index),
# dbits=self.word_size, abits=self.addr_size,
# port_info=(self.total_ports,self.total_write,self.read_index,self.write_index), dbits=self.word_size,
# sram_name=self.name) sram_name=self.name)
# Add load capacitance to each of the read ports # Add load capacitance to each of the read ports
self.sf.write("\n* SRAM output loads\n") self.sf.write("\n* SRAM output loads\n")
for port in range(self.total_read): for port in range(self.total_read):
for bit in range(self.word_size): 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 # Generate data input bits
self.sf.write("\n* Generation of data and address signals\n") self.sf.write("\n* Generation of data and address signals\n")
for port in range(self.total_write): for port in range(self.total_write):
for bit in range(self.word_size): 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) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05)
# Generate address bits # Generate address bits
for port in range(self.total_ports): for port in range(self.total_ports):
for bit in range(self.addr_size): 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) self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][bit], self.period, self.slew, 0.05)
# Generate control signals # Generate control signals
@ -434,12 +242,12 @@ class functional(simulation):
for port in range(self.total_ports): 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) 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) self.stim.gen_pwl("WEB{}".format(port), self.cycle_times , self.web_values[port], self.period, self.slew, 0.05)
# Generate CLK signals # Generate CLK signals
for port in range(self.total_ports): 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, v1=self.gnd_voltage,
v2=self.vdd_voltage, v2=self.vdd_voltage,
offset=self.period, offset=self.period,
@ -448,21 +256,15 @@ class functional(simulation):
t_fall=self.slew) t_fall=self.slew)
# Generate DOUT value measurements # 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") self.sf.write("\n * Generation of dout measurements\n")
for i in range(num_tests*self.num_checks): for (word, dout_port, eo_period, check) in self.write_check:
t_intital = self.eo_period[i] - 0.01*self.period t_intital = eo_period - 0.01*self.period
t_final = self.eo_period[i] + 0.01*self.period t_final = eo_period + 0.01*self.period
for port in range(self.total_read): for bit in range(self.word_size):
for bit in range(self.word_size): self.stim.gen_meas_value(meas_name="V{0}_{1}ck{2}".format(dout_port,bit,check),
self.stim.gen_meas_value(meas_name="VDOUT{0}[{1}]ck{2}".format(self.read_index[port],bit,i), dout="{0}_{1}".format(dout_port,bit),
dout="DOUT{0}[{1}]".format(self.read_index[port],bit), t_intital=t_intital,
t_intital=t_intital, t_final=t_final)
t_final=t_final)
self.stim.write_control(self.cycle_times[-1] + self.period) self.stim.write_control(self.cycle_times[-1] + self.period)
self.sf.close() self.sf.close()

View File

@ -28,6 +28,7 @@ class simulation():
self.total_read = self.sram.total_read self.total_read = self.sram.total_read
self.read_index = self.sram.read_index self.read_index = self.sram.read_index
self.write_index = self.sram.write_index self.write_index = self.sram.write_index
self.num_rw_ports = self.sram.num_rw_ports
self.port_id = self.sram.port_id self.port_id = self.sram.port_id
def set_corner(self,corner): 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 # 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.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 # 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.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)] 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): def add_control_one_port(self, port, op):
"""Appends control signals for operation to a given port""" """Appends control signals for operation to a given port"""
@ -61,7 +65,7 @@ class simulation():
web_val = 1 web_val = 1
csb_val = 1 csb_val = 1
if op == "read": if op == "read":
self.cs_b = 0 csb_val = 0
elif op == "write": elif op == "write":
csb_val = 0 csb_val = 0
web_val = 0 web_val = 0
@ -105,7 +109,8 @@ class simulation():
def add_write(self, comment, address, data, port): def add_write(self, comment, address, data, port):
""" Add the control values for a write cycle. """ """ 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.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
self.t_current, self.t_current,
comment, 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 #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
noop_data = "0"*self.word_size noop_data = "0"*self.word_size
#Add noops to all other ports. #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: if unselected_port != port:
self.add_noop_one_port(address, noop_data, unselected_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): def add_read(self, comment, address, data, port):
""" Add the control values for a read cycle. """ """ 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.cycle_comments.append("Cycle {0:2d}\tPort {3}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
self.t_current, self.t_current,
comment, comment,
@ -150,48 +142,59 @@ class simulation():
self.add_control_one_port(port, "read") self.add_control_one_port(port, "read")
#If the port is also a readwrite then add data. #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_data(data,port)
self.add_address(address, port) self.add_address(address, port)
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_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 noop_data = "0"*self.word_size
#Add noops to all other ports. #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: if unselected_port != port:
self.add_noop_one_port(address, noop_data, unselected_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): def add_noop_all_ports(self, comment, address, data):
""" Add the control values for a noop to all ports. """ """ 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.cycle_comments.append("Cycle {0:2d}\tPort All\t{1:5.2f}ns:\t{2}".format(len(self.cycle_times),
self.t_current, self.t_current,
comment)) comment))
self.cycle_times.append(self.t_current) self.cycle_times.append(self.t_current)
self.t_current += self.period self.t_current += self.period
for port in range(self.total_port_num): for port in range(self.total_ports):
self.add_noop_one_port(address, data, port) 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)

View File

@ -28,24 +28,12 @@ class stimuli():
(self.process, self.voltage, self.temperature) = corner (self.process, self.voltage, self.temperature) = corner
self.device_models = tech.spice["fet_models"][self.process] 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): def inst_sram(self, sram, port_signal_names, port_info, abits, dbits, sram_name):
""" Function to instatiate an SRAM subckt. """ """ Function to instatiate an SRAM subckt. """
pin_names = self.gen_pin_names(port_signal_names, port_info, abits, dbits) 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 #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 ") self.sf.write("Xsram ")
for pin in pin_names: for pin in pin_names:
@ -59,27 +47,27 @@ class stimuli():
#will cause issues here. #will cause issues here.
pin_names = [] pin_names = []
(addr_name, din_name, dout_name) = port_signal_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): for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(din_name,write_input, i)) 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): for i in range(abits):
pin_names.append("{0}{1}_{2}".format(addr_name,port,i)) pin_names.append("{0}{1}_{2}".format(addr_name,port,i))
#Control signals not finalized. #Control signals not finalized.
for port in range(total_port_num): for port in range(total_ports):
pin_names.append("CSB{0}".format(port)) pin_names.append("CSB{0}".format(port))
for port in range(total_port_num): for port in range(total_ports):
if port in read_ports and port in write_ports: if (port in read_index) and (port in write_index):
pin_names.append("WEB{0}".format(port)) 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)) 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): for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i)) pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i))
@ -179,7 +167,7 @@ class stimuli():
to the initial value. to the initial value.
""" """
# the initial value is not a clock time # 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 # shift signal times earlier for setup time
times = np.array(clk_times) - setup*period times = np.array(clk_times) - setup*period

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS from globals import OPTS
import debug 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): class sram_1bank_test(openram_test):
def runTest(self): 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") debug.info(1, "Single bank two way column mux with control logic")
a = sram(c, "sram2") a = sram(c, "sram2")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)
"""
c.num_words=64 c.num_words=64
c.words_per_row=4 c.words_per_row=4
debug.info(1, "Single bank, four way column mux with control logic") 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") debug.info(1, "Single bank, no column mux with control logic")
a = sram(c, "sram1") a = sram(c, "sram1")
self.local_check(a, final_verification=True) self.local_check(a, final_verification=True)
"""
OPTS.num_rw_ports = 0 OPTS.num_rw_ports = 0
OPTS.num_w_ports = 2 OPTS.num_w_ports = 2
OPTS.num_r_ports = 2 OPTS.num_r_ports = 2

View File

@ -38,8 +38,8 @@ class psram_func_test(openram_test):
c.words_per_row=2 c.words_per_row=2
OPTS.num_rw_ports = 1 OPTS.num_rw_ports = 1
OPTS.num_w_ports = 2 OPTS.num_w_ports = 1
OPTS.num_r_ports = 4 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)) 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") 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]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) 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() globals.end_openram()

View File

@ -41,9 +41,10 @@ class sram_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) 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() globals.end_openram()

View File

@ -38,8 +38,8 @@ class psram_func_test(openram_test):
c.words_per_row=2 c.words_per_row=2
OPTS.num_rw_ports = 1 OPTS.num_rw_ports = 1
OPTS.num_w_ports = 2 OPTS.num_w_ports = 1
OPTS.num_r_ports = 4 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)) 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") 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]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) 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() globals.end_openram()

View File

@ -41,9 +41,10 @@ class sram_func_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
f = functional(s.s, tempspice, corner) 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() globals.end_openram()