Add passthru test for ICache

This test constrains the address range (giving the cache a chance to
do some caching), but leaves the cache disabled. Seed changes are more
frequent than usual, to give us a good chance to spot any caching that
shouldn't have happened.
This commit is contained in:
Rupert Swarbrick 2020-05-14 15:13:39 +01:00 committed by Rupert Swarbrick
parent ec42eb4409
commit d750d3e53e
12 changed files with 159 additions and 30 deletions

View file

@ -36,7 +36,7 @@
could check this, but the unconstrained branch addresses mean it's
very unlikely to see much caching going on.'''
milestone: V2
tests: []
tests: ["ibex_icache_passthru"]
}
{

View file

@ -20,6 +20,7 @@ filesets:
- seq_lib/ibex_icache_vseq_list.sv: {is_include_file: true}
- seq_lib/ibex_icache_base_vseq.sv: {is_include_file: true}
- seq_lib/ibex_icache_sanity_vseq.sv: {is_include_file: true}
- seq_lib/ibex_icache_passthru_vseq.sv: {is_include_file: true}
file_type: systemVerilogSource
targets:

View file

@ -0,0 +1,34 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
class ibex_icache_passthru_vseq extends ibex_icache_base_vseq;
`uvm_object_utils(ibex_icache_passthru_vseq)
`uvm_object_new
// A passthru sequence for the core agent and a basic slave sequence for the memory agent
ibex_icache_core_passthru_seq core_seq;
ibex_icache_mem_resp_seq mem_seq;
task body();
// Start the core and memory sequences. We use fork/join_any so that we don't wait for the
// memory sequence (which is reactive so will never finish).
fork
begin
`uvm_create_on(core_seq, p_sequencer.core_sequencer_h)
`DV_CHECK_RANDOMIZE_FATAL(core_seq)
core_seq.start(p_sequencer.core_sequencer_h);
end
begin
`uvm_create_on(mem_seq, p_sequencer.mem_sequencer_h)
// Increase the frequency of seed updates
mem_seq.gap_between_seeds = 49;
`DV_CHECK_RANDOMIZE_FATAL(mem_seq)
mem_seq.start(p_sequencer.mem_sequencer_h);
end
join_any
endtask : body
endclass : ibex_icache_passthru_vseq

View file

@ -4,3 +4,4 @@
`include "ibex_icache_base_vseq.sv"
`include "ibex_icache_sanity_vseq.sv"
`include "ibex_icache_passthru_vseq.sv"

View file

@ -24,6 +24,7 @@ filesets:
- ibex_icache_core_agent.sv: {is_include_file: true}
- seq_lib/ibex_icache_core_base_seq.sv: {is_include_file: true}
- seq_lib/ibex_icache_core_sanity_seq.sv: {is_include_file: true}
- seq_lib/ibex_icache_core_passthru_seq.sv: {is_include_file: true}
- seq_lib/ibex_icache_core_seq_list.sv: {is_include_file: true}
file_type: systemVerilogSource

View file

@ -12,8 +12,85 @@ class ibex_icache_core_base_seq extends dv_base_seq #(
`uvm_object_new
// Number of test items (note that a single test item may contain many instruction fetches)
protected rand int count;
constraint c_count { count inside {[800:1000]}; }
// The base address used when constrain_branches is true.
protected rand bit[31:0] base_addr;
// If this is set, the next request should be constrained to have trans_type
// ICacheCoreTransTypeBranch. It's initially 1 because the core must start with a branch to tell
// the cache where to fetch from in the first place.
protected bit force_branch = 1'b1;
// If this is set, any branch target address should be within 64 bytes of base_addr and runs of
// instructions should have a maximum length of 100.
protected bit constrain_branches = 1'b0;
// A count of the number of instructions fetched since the last branch. This is only important
// when constrain_branches is true, in which case we want to ensure that we don't fetch too much
// in a straight line between branches.
protected int unsigned insns_since_branch = 0;
// If this bit is set, we will never enable the cache
protected bit force_disable = 1'b0;
virtual task body();
`uvm_fatal(`gtn, "Need to override this when you extend from this class!")
endtask
// Generate and run a single item using class parameters
protected task run_req(ibex_icache_core_req_item req, ibex_icache_core_rsp_item rsp);
start_item(req);
if (constrain_branches && insns_since_branch >= 100)
force_branch = 1'b1;
`DV_CHECK_RANDOMIZE_WITH_FATAL(
req,
// Force a branch if necessary
force_branch -> req.trans_type == ICacheCoreTransTypeBranch;
// If this is a branch and constrain_branches is true then constrain any branch target.
(constrain_branches && (req.trans_type == ICacheCoreTransTypeBranch)) ->
req.branch_addr inside {[base_addr:(base_addr + 64)]};
// If this is a branch and constrain_branches is true, we can ask for up to 100 instructions
// (independent of insns_since_branch)
(constrain_branches && (req.trans_type == ICacheCoreTransTypeBranch)) ->
num_insns <= 100;
// If this isn't a branch and constrain_branches is true, we can ask for up to 100 -
// insns_since_branch instructions. This will be positive because of the check above.
(constrain_branches && (req.trans_type != ICacheCoreTransTypeBranch)) ->
num_insns <= 100 - insns_since_branch;
force_disable -> !toggle_enable;
)
finish_item(req);
get_response(rsp);
// The next transaction must start with a branch if this one ended with an error
force_branch = rsp.saw_error;
// Update insns_since_branch. Note that this will be an overestimate if we saw an error, but
// that doesn't matter because we'll force a branch next time either way.
insns_since_branch = (((req.trans_type == ICacheCoreTransTypeBranch) ? 0 : insns_since_branch) +
req.num_insns);
endtask
// Generate and run count items. Subclasses probably want to configure the parameters above before
// running this.
protected task run_reqs();
req = ibex_icache_core_req_item::type_id::create("req");
rsp = ibex_icache_core_rsp_item::type_id::create("rsp");
repeat (count - 1) begin
run_req(req, rsp);
end
endtask
endclass

View file

@ -0,0 +1,21 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Passthru test sequence
//
// This is used for the passthru test. We constrain branch targets and leave the cache disabled.
class ibex_icache_core_passthru_seq extends ibex_icache_core_base_seq;
`uvm_object_utils(ibex_icache_core_passthru_seq)
`uvm_object_new
task body();
// Overrides for base sequence
constrain_branches = 1'b1;
force_disable = 1'b1;
run_reqs();
endtask
endclass

View file

@ -5,34 +5,14 @@
// Sanity test seq
//
// This is unlikely to find many cache hits (since it branches all over the 4GiB address space).
class ibex_icache_core_sanity_seq extends ibex_icache_core_base_seq;
`uvm_object_utils(ibex_icache_core_sanity_seq)
`uvm_object_new
rand int count;
constraint c_count { count inside {[800:1000]}; }
task body();
// If this is set, the next request will be constrained to have trans_type
// ICacheCoreTransTypeBranch. It's initially 1 because the core must start with a branch to tell
// the cache where to fetch from in the first place.
bit force_branch = 1'b1;
req = ibex_icache_core_req_item::type_id::create("req");
rsp = ibex_icache_core_rsp_item::type_id::create("rsp");
repeat (count - 1) begin
start_item(req);
if (force_branch) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(req, req.trans_type == ICacheCoreTransTypeBranch;)
end else begin
`DV_CHECK_RANDOMIZE_FATAL(req)
end
finish_item(req);
get_response(rsp);
// The next transaction must start with a branch if this one ended with an error.
force_branch = rsp.saw_error;
end
// No overrides needed in base sequence
run_reqs();
endtask
endclass

View file

@ -4,3 +4,4 @@
`include "ibex_icache_core_base_seq.sv"
`include "ibex_icache_core_sanity_seq.sv"
`include "ibex_icache_core_passthru_seq.sv"

View file

@ -9,6 +9,7 @@ class ibex_icache_mem_resp_item extends uvm_sequence_item;
int unsigned min_response_delay = 0;
int unsigned mid_response_delay = 5;
int unsigned max_response_delay = 50;
int unsigned gap_between_seeds = 499;
// True if this is a granted request. Otherwise, this is the first time we've seen an address (and
// we might need to drive the PMP line).
@ -40,7 +41,7 @@ class ibex_icache_mem_resp_item extends uvm_sequence_item;
constraint c_seed_dist {
seed dist {
32'd0 :/ 499,
32'd0 :/ gap_between_seeds,
[1:32'hffffffff] :/ 1
};
}

View file

@ -6,7 +6,12 @@
class ibex_icache_mem_resp_seq extends ibex_icache_mem_base_seq;
ibex_icache_mem_model #(.BusWidth (32)) mem_model;
// Knobs
//
// gap_between_seeds is the expected number of memory fetches between each seed update.
int unsigned gap_between_seeds = 499;
protected ibex_icache_mem_model #(.BusWidth (32)) mem_model;
// We pick new seeds when we spot a request (rather than when we spot a grant) to ensure that
// any given fetch corresponds to exactly one seed. Unfortunately, there's a race if these two
@ -37,6 +42,9 @@ class ibex_icache_mem_resp_seq extends ibex_icache_mem_base_seq;
ibex_icache_mem_req_item req_item = new("req_item");
ibex_icache_mem_resp_item resp_item = new("resp_item");
// Set knob to control randomization
resp_item.gap_between_seeds = gap_between_seeds;
forever begin
// Wait for a transaction request.
p_sequencer.request_fifo.get(req_item);

View file

@ -22,7 +22,6 @@
// Import additional common sim cfg files.
// TODO: remove imported cfgs that do not apply.
import_cfgs: [// Project wide common sim cfg file
"{proj_root}/dv/uvm/data/common_sim_cfg.hjson"]
@ -45,14 +44,19 @@
run_opts: ["+test_timeout_ns=1000000000"]
}
// TODO: add more tests here
{
name: ibex_icache_passthru
uvm_test_seq: ibex_icache_passthru_vseq
run_opts: ["+test_timeout_ns=1000000000"]
}
]
// List of regressions.
regressions: [
{
name: sanity
tests: ["ibex_icache_sanity"]
tests: ["ibex_icache_sanity",
"ibex_icache_passthru"]
}
]
}