diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 2a8d5293..9b59c9e6 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -1063,7 +1063,8 @@ class delay(simulation): self.trimsp.set_configuration(self.num_banks, self.num_rows, self.num_cols, - self.word_size) + self.word_size, + self.num_spare_rows) self.trimsp.trim(self.probe_address,self.probe_data) else: # The non-reduced netlist file when it is disabled diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 6d827aa3..cbd9b8d5 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -287,7 +287,10 @@ class functional(simulation): def gen_addr(self): """ Generates a random address value to write to. """ - random_value = random.randint(0,(2**self.addr_size)-1) + if (self.num_spare_rows == 0): + random_value = random.randint(0,(2**self.addr_size)-1) + else: + random_value = random.randint(0,((2**(self.addr_size-1)-1))+(self.num_spare_rows * self.words_per_row)) addr_bits = self.convert_to_bin(random_value,True) return addr_bits diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index adbe5f5f..b21c0bd0 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -25,12 +25,14 @@ class simulation(): self.word_size = self.sram.word_size self.addr_size = self.sram.addr_size self.write_size = self.sram.write_size + self.num_spare_rows = self.sram.num_spare_rows self.sp_file = spfile self.all_ports = self.sram.all_ports self.readwrite_ports = self.sram.readwrite_ports self.read_ports = self.sram.read_ports self.write_ports = self.sram.write_ports + self.words_per_row = self.sram.words_per_row if self.write_size: self.num_wmasks = int(self.word_size/self.write_size) else: @@ -135,7 +137,10 @@ class simulation(): if c=="0": self.addr_values[port][bit].append(0) elif c=="1": - self.addr_values[port][bit].append(1) + if((self.num_spare_rows != 0) and (bit == (self.addr_size - 1))): + self.addr_values[port][bit].append(0) + else: + self.addr_values[port][bit].append(1) else: debug.error("Non-binary address string",1) bit -= 1 diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index ffc45a9c..d20dfe42 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -6,7 +6,7 @@ # All rights reserved. # import debug -from math import log +from math import log,ceil import re class trim_spice(): @@ -42,7 +42,7 @@ class trim_spice(): self.word_size = word_size self.words_per_row = self.num_columns / self.word_size - self.row_addr_size = int(log(self.num_rows, 2)) + self.row_addr_size = ceil(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.bank_addr_size = self.col_addr_size + self.row_addr_size self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index 0c5d988c..5e351577 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -52,11 +52,12 @@ import sram class fake_sram(sram.sram): """ This is an SRAM that doesn't actually create itself, just computes the sizes. """ - def __init__(self, word_size, num_words, num_banks, name): + def __init__(self, word_size, num_words, num_banks, name, num_spare_rows): self.name = name self.word_size = word_size self.num_words = num_words self.num_banks = num_banks + self.num_spare_rows = num_spare_rows c = reload(__import__(OPTS.bitcell)) self.mod_bitcell = getattr(c, OPTS.bitcell) self.bitcell = self.mod_bitcell() diff --git a/compiler/globals.py b/compiler/globals.py index e9b77d66..69ead34f 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -532,6 +532,8 @@ def report_status(): debug.error("{0} is not an integer in config file.".format(OPTS.sram_size)) if type(OPTS.write_size) is not int and OPTS.write_size is not None: debug.error("{0} is not an integer in config file.".format(OPTS.write_size)) + if type(OPTS.num_spare_rows) is not int and OPT.num_spare_rows is not None: + debug.error("{0} is not an integer in config file.".format(OPTS.num_spare_rows)) # If a write mask is specified by the user, the mask write size should be the same as # the word size so that an entire word is written at once. diff --git a/compiler/openram.py b/compiler/openram.py index 63a8cfd6..cf18f97b 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -53,7 +53,8 @@ from sram_config import sram_config # Configure the SRAM organization c = sram_config(word_size=OPTS.word_size, num_words=OPTS.num_words, - write_size=OPTS.write_size) + write_size=OPTS.write_size, + num_spare_rows=OPTS.num_spare_rows) debug.print_raw("Words per row: {}".format(c.words_per_row)) output_extensions = ["sp", "v", "lib", "py", "html", "log"] diff --git a/compiler/tests/21_ngspice_delay_extra_rows_test.py b/compiler/tests/21_ngspice_delay_extra_rows_test.py new file mode 100755 index 00000000..328dc630 --- /dev/null +++ b/compiler/tests/21_ngspice_delay_extra_rows_test.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class timing_sram_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + OPTS.spice_name="ngspice" + OPTS.analytical_delay = False + OPTS.netlist_only = True + + # This is a hack to reload the characterizer __init__ with the spice version + from importlib import reload + import characterizer + reload(characterizer) + from characterizer import delay + from sram_config import sram_config + c = sram_config(word_size=1, + num_words=16, + num_banks=1, + num_spare_rows=5) + c.words_per_row=1 + c.recompute_sizes() + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = factory.create(module_type="sram", sram_config=c) + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + probe_address = "0" + ("1" * (s.s.addr_size - 1)) + probe_data = s.s.word_size - 1 + debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data)) + + corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) + d = delay(s.s, tempspice, corner) + import tech + loads = [tech.spice["dff_in_cap"]*4] + slews = [tech.spice["rise_time"]*2] + data, port_data = d.analyze(probe_address, probe_data, slews, loads) + #Combine info about port into all data + data.update(port_data[0]) + + if OPTS.tech_name == "freepdk45": + golden_data = {'delay_hl': [0.2264205], + 'delay_lh': [0.2264205], + 'leakage_power': 0.0021017429999999997, + 'min_period': 0.859, + 'read0_power': [0.3339161], + 'read1_power': [0.31329440000000003], + 'slew_hl': [0.2590786], + 'slew_lh': [0.2590786], + 'write0_power': [0.36360849999999995], + 'write1_power': [0.3486931]} + elif OPTS.tech_name == "scn4m_subm": + golden_data = {'delay_hl': [1.85985], + 'delay_lh': [1.85985], + 'leakage_power': 0.006418553, + 'min_period': 6.875, + 'read0_power': [12.656310000000001], + 'read1_power': [12.11682], + 'slew_hl': [1.868942], + 'slew_lh': [1.868942], + 'write0_power': [13.978110000000001], + 'write1_power': [11.437930000000001]} + else: + self.assertTrue(False) # other techs fail + + # Check if no too many or too few results + self.assertTrue(len(data.keys())==len(golden_data.keys())) + + self.assertTrue(self.check_golden_data(data,golden_data,0.25)) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner())