mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-23 13:27:10 -04:00
Add test to check that disabling doesn't invalidate the icache
This is an entry in the testplan. Renaming it to "oldval", because suffixing every class name with "disable_without_invalidation" was getting ridiculous.
This commit is contained in:
parent
f53d5ac645
commit
de05509cb6
10 changed files with 116 additions and 12 deletions
|
@ -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"]
|
||||
}
|
||||
|
||||
{
|
||||
|
|
1
dv/uvm/icache/dv/env/ibex_icache_env.core
vendored
1
dv/uvm/icache/dv/env/ibex_icache_env.core
vendored
|
@ -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:
|
||||
|
|
41
dv/uvm/icache/dv/env/ibex_icache_scoreboard.sv
vendored
41
dv/uvm/icache/dv/env/ibex_icache_scoreboard.sv
vendored
|
@ -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
|
||||
|
|
24
dv/uvm/icache/dv/env/seq_lib/ibex_icache_oldval_vseq.sv
vendored
Normal file
24
dv/uvm/icache/dv/env/seq_lib/ibex_icache_oldval_vseq.sv
vendored
Normal file
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
44
dv/uvm/icache/dv/tests/ibex_icache_oldval_test.sv
Normal file
44
dv/uvm/icache/dv/tests/ibex_icache_oldval_test.sv
Normal file
|
@ -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
|
||||
|
|
@ -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:
|
||||
|
|
|
@ -18,5 +18,6 @@ package ibex_icache_test_pkg;
|
|||
|
||||
// package sources
|
||||
`include "ibex_icache_base_test.sv"
|
||||
`include "ibex_icache_oldval_test.sv"
|
||||
|
||||
endpackage
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue