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:
Rupert Swarbrick 2020-06-03 16:47:43 +01:00 committed by Rupert Swarbrick
parent f53d5ac645
commit de05509cb6
10 changed files with 116 additions and 12 deletions

View file

@ -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"]
}
{

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View 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

View file

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

View file

@ -18,5 +18,6 @@ package ibex_icache_test_pkg;
// package sources
`include "ibex_icache_base_test.sv"
`include "ibex_icache_oldval_test.sv"
endpackage