diff --git a/compiler/base/design.py b/compiler/base/design.py index 80a9e07e..a3f4dbbf 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -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 diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 6cd1e65d..75df4b69 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -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() diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 9b280849..c76d702e 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -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) \ No newline at end of file + 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) + \ No newline at end of file diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index c41d60a2..14f780c2 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -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 diff --git a/compiler/tests/20_psram_1bank_test.py b/compiler/tests/20_psram_1bank_test.py index 140c774b..7ca2e33c 100755 --- a/compiler/tests/20_psram_1bank_test.py +++ b/compiler/tests/20_psram_1bank_test.py @@ -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 diff --git a/compiler/tests/22_hspice_psram_func_test.py b/compiler/tests/22_hspice_psram_func_test.py index 0b3c66a7..0d2f775f 100644 --- a/compiler/tests/22_hspice_psram_func_test.py +++ b/compiler/tests/22_hspice_psram_func_test.py @@ -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() diff --git a/compiler/tests/22_hspice_sram_func_test.py b/compiler/tests/22_hspice_sram_func_test.py index 62d7679f..b8b0969e 100644 --- a/compiler/tests/22_hspice_sram_func_test.py +++ b/compiler/tests/22_hspice_sram_func_test.py @@ -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() diff --git a/compiler/tests/22_ngspice_psram_func_test.py b/compiler/tests/22_ngspice_psram_func_test.py index 2768066a..612bfbcf 100644 --- a/compiler/tests/22_ngspice_psram_func_test.py +++ b/compiler/tests/22_ngspice_psram_func_test.py @@ -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() diff --git a/compiler/tests/22_ngspice_sram_func_test.py b/compiler/tests/22_ngspice_sram_func_test.py index 1177d1c4..895729e2 100644 --- a/compiler/tests/22_ngspice_sram_func_test.py +++ b/compiler/tests/22_ngspice_sram_func_test.py @@ -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()