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
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

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()