diff --git a/dv/uvm/icache/data/ibex_icache_testplan.hjson b/dv/uvm/icache/data/ibex_icache_testplan.hjson index 96cd5468..7b1e9ff6 100644 --- a/dv/uvm/icache/data/ibex_icache_testplan.hjson +++ b/dv/uvm/icache/data/ibex_icache_testplan.hjson @@ -67,7 +67,7 @@ } { - name: disable_without_invalidation + name: oldval desc: '''Check the cache can be enabled and disabled without invalidation Set up requests as in the passthru test to ensure lots of cache @@ -77,7 +77,7 @@ Compare bus transactions and instructions fetched to make sure that cached instructions survive enable/disable toggles.''' milestone: V2 - tests: [] + tests: ["ibex_icache_oldval"] } { diff --git a/dv/uvm/icache/dv/env/ibex_icache_env.core b/dv/uvm/icache/dv/env/ibex_icache_env.core index 53d1f8d8..a672785f 100644 --- a/dv/uvm/icache/dv/env/ibex_icache_env.core +++ b/dv/uvm/icache/dv/env/ibex_icache_env.core @@ -23,6 +23,7 @@ filesets: - seq_lib/ibex_icache_passthru_vseq.sv: {is_include_file: true} - seq_lib/ibex_icache_caching_vseq.sv: {is_include_file: true} - seq_lib/ibex_icache_invalidation_vseq.sv: {is_include_file: true} + - seq_lib/ibex_icache_oldval_vseq.sv: {is_include_file: true} file_type: systemVerilogSource targets: diff --git a/dv/uvm/icache/dv/env/ibex_icache_scoreboard.sv b/dv/uvm/icache/dv/env/ibex_icache_scoreboard.sv index 029395af..e845641d 100644 --- a/dv/uvm/icache/dv/env/ibex_icache_scoreboard.sv +++ b/dv/uvm/icache/dv/env/ibex_icache_scoreboard.sv @@ -101,6 +101,15 @@ class ibex_icache_scoreboard // Address range seen in the current window protected bit [31:0] window_range_lo, window_range_hi; + // Set by check_compatible_1/check_compatible_2 on a hit. Gives the "age" of the seed + // corresponding to the last fetch. If the hit matched the most recent version of the seed, the + // "age" is zero. For the next most recent version, it's one and so on. + protected int unsigned last_fetch_age; + + // The number of fetches that might have had an old seed and the number that actually did. + int unsigned possible_old_count = 0; + int unsigned actual_old_count = 0; + `uvm_component_new function void build_phase(uvm_phase phase); @@ -453,12 +462,12 @@ class ibex_icache_scoreboard function automatic bit check_compatible_1(logic [31:0] address, logic [31:0] seen_insn_data, logic seen_err, + int unsigned min_idx, bit chatty); - int unsigned min_idx = no_cache ? last_branch_seed : 0; - assert(min_idx < mem_seeds.size); for (int unsigned i = min_idx; i < mem_seeds.size; i++) begin if (is_seed_compatible_1(address, seen_insn_data, seen_err, mem_seeds[i], chatty)) begin + last_fetch_age = mem_seeds.size - 1 - i; return 1'b1; end end @@ -471,16 +480,15 @@ class ibex_icache_scoreboard function automatic bit check_compatible_2(logic [31:0] address, logic [31:0] seen_insn_data, logic seen_err_plus2, + int unsigned min_idx, bit chatty); // We want to iterate over all pairs of seeds. We can do this with a nested pair of foreach // loops, but we expect that usually we'll get a hit on the "diagonal", so we check that first. - int unsigned min_idx = no_cache ? last_branch_seed : 0; - assert(min_idx < mem_seeds.size); - for (int unsigned i = min_idx; i < mem_seeds.size; i++) begin if (is_seed_compatible_2(address, seen_insn_data, seen_err_plus2, mem_seeds[i], mem_seeds[i], chatty)) begin + last_fetch_age = mem_seeds.size - 1 - i; return 1'b1; end end @@ -489,6 +497,7 @@ class ibex_icache_scoreboard if (i != j) begin if (is_seed_compatible_2(address, seen_insn_data, seen_err_plus2, mem_seeds[i], mem_seeds[j], chatty)) begin + last_fetch_age = mem_seeds.size - 1 - (i < j ? i : j); return 1'b1; end end @@ -501,17 +510,22 @@ class ibex_icache_scoreboard logic misaligned; logic good_bottom_word; logic uncompressed; + int unsigned min_idx; misaligned = (item.address & 3) != 0; good_bottom_word = (~item.err) | item.err_plus2; uncompressed = item.insn_data[1:0] == 2'b11; + min_idx = no_cache ? last_branch_seed : 0; + `DV_CHECK_LT_FATAL(min_idx, mem_seeds.size); + if (misaligned && good_bottom_word && uncompressed) begin // It looks like this was a misaligned fetch (so came from two fetches from memory) and the // bottom word's fetch succeeded, giving an uncompressed result (so we care about the upper // fetch). - if (!check_compatible_2(item.address, item.insn_data, item.err & item.err_plus2, 1'b0)) begin - void'(check_compatible_2(item.address, item.insn_data, item.err & item.err_plus2, 1'b1)); + logic err = item.err & item.err_plus2; + if (!check_compatible_2(item.address, item.insn_data, err, min_idx, 1'b0)) begin + void'(check_compatible_2(item.address, item.insn_data, err, min_idx, 1'b1)); `uvm_error(`gfn, $sformatf("Fetch at address 0x%08h got data incompatible with available seeds.", item.address)); @@ -520,13 +534,22 @@ class ibex_icache_scoreboard // The easier case: either the fetch was aligned, the lower word seems to have caused an error // or the bits in the lower word are a compressed instruction (so we don't care about the // upper 16 bits anyway). - if (!check_compatible_1(item.address, item.insn_data, item.err, 1'b0)) begin - void'(check_compatible_1(item.address, item.insn_data, item.err, 1'b1)); + if (!check_compatible_1(item.address, item.insn_data, item.err, min_idx, 1'b0)) begin + void'(check_compatible_1(item.address, item.insn_data, item.err, min_idx, 1'b1)); `uvm_error(`gfn, $sformatf("Fetch at address 0x%08h got data incompatible with available seeds.", item.address)); end end + + // All is well. The call to check_compatible_* will have set last_fetch_age. The maximum + // possible value is mem_seeds.size - 1 - min_idx. If this is positive, count whether we got a + // value corresponding to an old seed or not. + if (mem_seeds.size > min_idx + 1) begin + possible_old_count += 1; + if (last_fetch_age > 0) actual_old_count += 1; + end + endtask // Check that the busy line isn't low when there are outstanding memory transactions diff --git a/dv/uvm/icache/dv/env/seq_lib/ibex_icache_oldval_vseq.sv b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_oldval_vseq.sv new file mode 100644 index 00000000..eb4e8611 --- /dev/null +++ b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_oldval_vseq.sv @@ -0,0 +1,24 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class ibex_icache_oldval_vseq extends ibex_icache_base_vseq; + + `uvm_object_utils(ibex_icache_oldval_vseq) + `uvm_object_new + + virtual task pre_start(); + super.pre_start(); + + // Constrain branch targets + core_seq.constrain_branches = 1'b1; + + // Increase the frequency of cache enable/disable toggling + core_seq.gap_between_toggle_enable = 2; + + // Don't invalidate the cache (we want old values!) + core_seq.no_invalidate = 1'b1; + + endtask : pre_start + +endclass : ibex_icache_oldval_vseq diff --git a/dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv index 7c1210d2..64ab7d5c 100644 --- a/dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv +++ b/dv/uvm/icache/dv/env/seq_lib/ibex_icache_vseq_list.sv @@ -7,3 +7,4 @@ `include "ibex_icache_passthru_vseq.sv" `include "ibex_icache_caching_vseq.sv" `include "ibex_icache_invalidation_vseq.sv" +`include "ibex_icache_oldval_vseq.sv" diff --git a/dv/uvm/icache/dv/ibex_icache_core_agent/seq_lib/ibex_icache_core_base_seq.sv b/dv/uvm/icache/dv/ibex_icache_core_agent/seq_lib/ibex_icache_core_base_seq.sv index 0c11d1c6..acc02c0b 100644 --- a/dv/uvm/icache/dv/ibex_icache_core_agent/seq_lib/ibex_icache_core_base_seq.sv +++ b/dv/uvm/icache/dv/ibex_icache_core_agent/seq_lib/ibex_icache_core_base_seq.sv @@ -37,6 +37,9 @@ class ibex_icache_core_base_seq extends dv_base_seq #( // invalidation. int unsigned gap_between_invalidations = 49; + // The expected number of items between enable/disable toggles. + int unsigned gap_between_toggle_enable = 49; + // Number of test items (note that a single test item may contain many instruction fetches) protected rand int count; @@ -102,7 +105,7 @@ class ibex_icache_core_base_seq extends dv_base_seq #( // Toggle the cache enable line one time in 50. This should allow us a reasonable amount of // time in each mode (note that each transaction here results in multiple instruction // fetches) - enable dist { cache_enabled :/ 49, ~cache_enabled :/ 1 }; + enable dist { cache_enabled :/ gap_between_toggle_enable, ~cache_enabled :/ 1 }; // If no_invalidate is set, we shouldn't ever touch the invalidate line. no_invalidate -> invalidate == 1'b0; diff --git a/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson b/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson index c99f5c77..8b2a1664 100644 --- a/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson +++ b/dv/uvm/icache/dv/ibex_icache_sim_cfg.hjson @@ -59,6 +59,12 @@ name: ibex_icache_invalidation uvm_test_seq: ibex_icache_invalidation_vseq } + + { + name: ibex_icache_oldval + uvm_test_seq: ibex_icache_oldval_vseq + uvm_test: ibex_icache_oldval_test + } ] // List of regressions. diff --git a/dv/uvm/icache/dv/tests/ibex_icache_oldval_test.sv b/dv/uvm/icache/dv/tests/ibex_icache_oldval_test.sv new file mode 100644 index 00000000..bb4bc2cb --- /dev/null +++ b/dv/uvm/icache/dv/tests/ibex_icache_oldval_test.sv @@ -0,0 +1,44 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +class ibex_icache_oldval_test extends ibex_icache_base_test; + + `uvm_component_utils(ibex_icache_oldval_test) + `uvm_component_new + + // At end-of-test, look at the counters to see how often we've seen a value that isn't the most + // recent result possible. Check that possible_old_count is big enough to exclude statistical + // weirdness (arbitrarily taken as 1000 for now) and that check that at least 5% of these fetches + // did indeed get an "old" result. This is a really low-ball figure, but is enough to check that + // the cache does occasionally store an old result across a disable/enable cycle. + // + // Note that we can't do this in the scoreboard itself, because it only makes sense when we know + // that we're doing lots of caching + function void check_phase(uvm_phase phase); + int unsigned actual, possible; + int unsigned frac4; + + super.check_phase(phase); + + actual = env.scoreboard.actual_old_count; + possible = env.scoreboard.possible_old_count; + + `DV_CHECK(possible >= 1000, + $sformatf({"After an oldval test, we only saw %0d points where we ", + "could have returned an old value."}, + possible)) + + // Calculate a percentage to report with a decimal place (especially helpful if we fail). + `DV_CHECK_LE_FATAL(actual, possible); + frac4 = (1000 * actual + (possible / 2)) / possible; + + `DV_CHECK(frac4 >= 50, + $sformatf({"After an oldval test with %0d possible points for an old value, we got ", + "one %0d times (%0d.%0d%%). This is less than the 5% minimum threshold."}, + possible, actual, frac4 / 10, frac4 % 10)) + + endfunction : check_phase + +endclass : ibex_icache_oldval_test + diff --git a/dv/uvm/icache/dv/tests/ibex_icache_test.core b/dv/uvm/icache/dv/tests/ibex_icache_test.core index a1a68a43..b5802055 100644 --- a/dv/uvm/icache/dv/tests/ibex_icache_test.core +++ b/dv/uvm/icache/dv/tests/ibex_icache_test.core @@ -11,6 +11,7 @@ filesets: files: - ibex_icache_test_pkg.sv - ibex_icache_base_test.sv: {is_include_file: true} + - ibex_icache_oldval_test.sv: {is_include_file: true} file_type: systemVerilogSource targets: diff --git a/dv/uvm/icache/dv/tests/ibex_icache_test_pkg.sv b/dv/uvm/icache/dv/tests/ibex_icache_test_pkg.sv index c00229ad..4d62127b 100644 --- a/dv/uvm/icache/dv/tests/ibex_icache_test_pkg.sv +++ b/dv/uvm/icache/dv/tests/ibex_icache_test_pkg.sv @@ -18,5 +18,6 @@ package ibex_icache_test_pkg; // package sources `include "ibex_icache_base_test.sv" + `include "ibex_icache_oldval_test.sv" endpackage