mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
Minimal code for the 'core agent' in icache UVM testbench
This fills in the sequencer, driver etc. to actually drive signals. You can "run" a test with make -C dv/uvm/icache/dv run This won't do anything useful (it will stop with a timeout) because there is no memory agent yet.
This commit is contained in:
parent
754a8f3d09
commit
38422a03bc
13 changed files with 305 additions and 49 deletions
|
@ -19,22 +19,22 @@ SEED=1
|
|||
|
||||
ibex-top := ../../../..
|
||||
dvsim-py := $(ibex-top)/vendor/lowrisc_ip/dvsim/dvsim.py
|
||||
dvsim-std-args := --skip-ral --scratch-root $(ibex-top)/build -v h
|
||||
dvsim-std-args := --skip-ral --scratch-root $(scratch-root)
|
||||
|
||||
waves-arg := $(if $(filter-out 0,$(WAVES)),--waves,)
|
||||
verbosity-arg := $(if $(VERBOSITY),--verbosity $(VERBOSITY),)
|
||||
seed-arg := $(if $(SEED),--fixed-seed $(SEED),)
|
||||
|
||||
dvsim-mk-args := $(waves-arg) $(verbosity-arg) $(seed-arg)
|
||||
run-dvsim := $(dvsim-py) $(dvsim-std-args) $(dvsim-mk-args)
|
||||
dvsim-mk-args := $(waves-arg) $(verbosity-arg) $(seed-arg)
|
||||
run-icache-dvsim := $(dvsim-py) ibex_icache_sim_cfg.hjson $(dvsim-std-args) $(dvsim-mk-args)
|
||||
|
||||
.PHONY: all
|
||||
all: run
|
||||
|
||||
.PHONY: just-build
|
||||
just-build:
|
||||
$(run-dvsim) --build-only ibex_icache_sim_cfg.hjson
|
||||
$(run-icache-dvsim) --build-only
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
$(run-dvsim) ibex_icache_sim_cfg.hjson
|
||||
$(run-icache-dvsim)
|
||||
|
|
6
dv/uvm/icache/dv/env/ibex_icache_env.sv
vendored
6
dv/uvm/icache/dv/env/ibex_icache_env.sv
vendored
|
@ -10,7 +10,7 @@ class ibex_icache_env extends dv_base_env #(
|
|||
);
|
||||
`uvm_component_utils(ibex_icache_env)
|
||||
|
||||
ibex_icache_agent m_ibex_icache_agent;
|
||||
ibex_icache_agent m_ibex_icache_agent;
|
||||
ibex_mem_intf_slave_agent m_ibex_mem_intf_slave_agent;
|
||||
|
||||
`uvm_component_new
|
||||
|
@ -31,10 +31,10 @@ class ibex_icache_env extends dv_base_env #(
|
|||
m_ibex_mem_intf_slave_agent.monitor.addr_ph_port.connect(scoreboard.ibex_mem_intf_slave_fifo.analysis_export);
|
||||
end
|
||||
if (cfg.is_active && cfg.m_ibex_icache_agent_cfg.is_active) begin
|
||||
virtual_sequencer.ibex_icache_sequencer_h = m_ibex_icache_agent.sequencer;
|
||||
virtual_sequencer.core_sequencer_h = m_ibex_icache_agent.sequencer;
|
||||
end
|
||||
if (cfg.is_active && m_ibex_mem_intf_slave_agent.get_is_active()) begin
|
||||
virtual_sequencer.ibex_mem_intf_slave_sequencer_h = m_ibex_mem_intf_slave_agent.sequencer;
|
||||
virtual_sequencer.mem_sequencer_h = m_ibex_mem_intf_slave_agent.sequencer;
|
||||
end
|
||||
endfunction
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ class ibex_icache_virtual_sequencer extends dv_base_virtual_sequencer #(
|
|||
);
|
||||
`uvm_component_utils(ibex_icache_virtual_sequencer)
|
||||
|
||||
ibex_icache_sequencer ibex_icache_sequencer_h;
|
||||
ibex_mem_intf_slave_sequencer ibex_mem_intf_slave_sequencer_h;
|
||||
ibex_icache_sequencer core_sequencer_h;
|
||||
ibex_mem_intf_slave_sequencer mem_sequencer_h;
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
|
|
|
@ -8,15 +8,10 @@ class ibex_icache_base_vseq extends dv_base_vseq #(
|
|||
.VIRTUAL_SEQUENCER_T (ibex_icache_virtual_sequencer)
|
||||
);
|
||||
`uvm_object_utils(ibex_icache_base_vseq)
|
||||
|
||||
// various knobs to enable certain routines
|
||||
bit do_ibex_icache_init = 1'b1;
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
virtual task dut_init(string reset_kind = "HARD");
|
||||
super.dut_init();
|
||||
if (do_ibex_icache_init) ibex_icache_init();
|
||||
endtask
|
||||
|
||||
virtual task dut_shutdown();
|
||||
|
@ -24,9 +19,4 @@ class ibex_icache_base_vseq extends dv_base_vseq #(
|
|||
// TODO
|
||||
endtask
|
||||
|
||||
// setup basic ibex_icache features
|
||||
virtual task ibex_icache_init();
|
||||
`uvm_error(`gfn, "FIXME")
|
||||
endtask
|
||||
|
||||
endclass : ibex_icache_base_vseq
|
||||
|
|
|
@ -2,14 +2,21 @@
|
|||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// basic sanity test vseq
|
||||
// Basic sanity test
|
||||
|
||||
class ibex_icache_sanity_vseq extends ibex_icache_base_vseq;
|
||||
`uvm_object_utils(ibex_icache_sanity_vseq)
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
// A sanity sequence for the core agent
|
||||
ibex_icache_sanity_seq core_seq;
|
||||
|
||||
task body();
|
||||
`uvm_error(`gfn, "FIXME")
|
||||
// TODO: This currently just drives the core sequence (which clearly isn't going to work!)
|
||||
`uvm_create_on(core_seq, p_sequencer.core_sequencer_h)
|
||||
`DV_CHECK_RANDOMIZE_FATAL(core_seq)
|
||||
core_seq.start(p_sequencer.core_sequencer_h);
|
||||
endtask : body
|
||||
|
||||
endclass : ibex_icache_sanity_vseq
|
||||
|
|
|
@ -19,6 +19,7 @@ filesets:
|
|||
- ibex_icache_monitor.sv: {is_include_file: true}
|
||||
- ibex_icache_agent.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_base_seq.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_sanity_seq.sv: {is_include_file: true}
|
||||
- seq_lib/ibex_icache_seq_list.sv: {is_include_file: true}
|
||||
file_type: systemVerilogSource
|
||||
|
||||
|
|
|
@ -8,6 +8,11 @@ package ibex_icache_agent_pkg;
|
|||
import dv_utils_pkg::*;
|
||||
import dv_lib_pkg::*;
|
||||
|
||||
typedef enum {
|
||||
ICacheTransTypeBranch,
|
||||
ICacheTransTypeReq
|
||||
} ibex_icache_trans_type_e;
|
||||
|
||||
// macro includes
|
||||
`include "uvm_macros.svh"
|
||||
`include "dv_macros.svh"
|
||||
|
@ -19,7 +24,7 @@ package ibex_icache_agent_pkg;
|
|||
typedef class ibex_icache_item;
|
||||
typedef class ibex_icache_agent_cfg;
|
||||
|
||||
// reuse dv_base_seqeuencer as is with the right parameter set
|
||||
// reuse dv_base_sequencer as is with the right parameter set
|
||||
typedef dv_base_sequencer #(.ITEM_T(ibex_icache_item),
|
||||
.CFG_T (ibex_icache_agent_cfg)) ibex_icache_sequencer;
|
||||
|
||||
|
|
|
@ -4,34 +4,124 @@
|
|||
|
||||
class ibex_icache_driver extends dv_base_driver #(ibex_icache_item, ibex_icache_agent_cfg);
|
||||
`uvm_component_utils(ibex_icache_driver)
|
||||
|
||||
// the base class provides the following handles for use:
|
||||
// ibex_icache_agent_cfg: cfg
|
||||
|
||||
`uvm_component_new
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
// base class forks off reset_signals() and get_and_drive() tasks
|
||||
super.run_phase(phase);
|
||||
endtask
|
||||
|
||||
// reset signals
|
||||
virtual task reset_signals();
|
||||
virtual task automatic reset_signals();
|
||||
cfg.vif.reset();
|
||||
endtask
|
||||
|
||||
// drive trans received from sequencer
|
||||
virtual task get_and_drive();
|
||||
virtual task automatic get_and_drive();
|
||||
forever begin
|
||||
seq_item_port.get_next_item(req);
|
||||
$cast(rsp, req.clone());
|
||||
rsp.set_id_info(req);
|
||||
`uvm_info(`gfn, $sformatf("rcvd item:\n%0s", req.sprint()), UVM_HIGH)
|
||||
// TODO: do the driving part
|
||||
//
|
||||
// send rsp back to seq
|
||||
|
||||
case (req.trans_type)
|
||||
ICacheTransTypeBranch: drive_branch_trans(req);
|
||||
ICacheTransTypeReq: drive_req_trans(req);
|
||||
default: `uvm_fatal(`gfn, "Unknown transaction type")
|
||||
endcase
|
||||
|
||||
`uvm_info(`gfn, "item sent", UVM_HIGH)
|
||||
seq_item_port.item_done(rsp);
|
||||
seq_item_port.item_done();
|
||||
end
|
||||
endtask
|
||||
|
||||
// Drive the cache for a "branch" transaction.
|
||||
//
|
||||
// This concurrently asserts branch with a given address for a cycle while doing the usual
|
||||
// (enable/disable, invalidate, read instructions).
|
||||
virtual task automatic drive_branch_trans(ibex_icache_item req);
|
||||
// Make sure that req is enabled (has no effect unless this is the first transaction)
|
||||
cfg.vif.req <= 1'b1;
|
||||
|
||||
fork
|
||||
cfg.vif.branch_to(req.branch_addr);
|
||||
if (req.toggle_enable) toggle_enable();
|
||||
if (req.invalidate) invalidate();
|
||||
read_insns(req.num_insns);
|
||||
join
|
||||
endtask
|
||||
|
||||
// Drive the cache for a "req transaction".
|
||||
//
|
||||
// This lowers req for zero or more cycles, at the same time as setting the enable pin and (maybe)
|
||||
// pulsing the invalidate line. Once that is done, it reads zero or more instructions.
|
||||
virtual task automatic drive_req_trans(ibex_icache_item req);
|
||||
int unsigned req_low_cycles;
|
||||
bit allow_no_low_cycles;
|
||||
|
||||
// How many cycles should we lower the request line? If there aren't any instructions to read,
|
||||
// this is constrained to be positive (to avoid a confusing zero-time transaction).
|
||||
allow_no_low_cycles = req.num_insns > 0;
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(req_low_cycles,
|
||||
req_low_cycles dist {
|
||||
0 :/ (allow_no_low_cycles ? 20 : 0),
|
||||
[1:33] :/ 5,
|
||||
[100:200] :/ 2,
|
||||
[1000:1200] :/ 1 };)
|
||||
|
||||
fork
|
||||
if (req_low_cycles > 0) lower_req(req_low_cycles);
|
||||
if (req.toggle_enable) toggle_enable();
|
||||
if (req.invalidate) invalidate();
|
||||
join
|
||||
read_insns(req.num_insns);
|
||||
endtask
|
||||
|
||||
// Toggle whether the cache is enabled
|
||||
virtual task automatic toggle_enable();
|
||||
cfg.vif.enable <= ~cfg.vif.enable;
|
||||
endtask
|
||||
|
||||
// Read up to num_insns instructions from the cache, stopping early on an error
|
||||
virtual task automatic read_insns(int num_insns);
|
||||
for (int i = 0; i < num_insns; i++) begin
|
||||
read_insn();
|
||||
// Spot any error and exit early
|
||||
if (cfg.vif.err)
|
||||
break;
|
||||
end
|
||||
endtask
|
||||
|
||||
// Read a single instruction from the cache
|
||||
virtual task automatic read_insn();
|
||||
int unsigned delay;
|
||||
|
||||
// Maybe (1 time in 10) wait for a valid signal before even considering asserting ready.
|
||||
if ($urandom_range(9) == 0)
|
||||
wait (cfg.vif.valid);
|
||||
|
||||
// Then pick how long we wait before asserting that we are ready.
|
||||
//
|
||||
// TODO: Make this configurable and weight 0 more heavily.
|
||||
cfg.vif.wait_clks($urandom_range(3));
|
||||
|
||||
// Assert ready and then wait until valid
|
||||
cfg.vif.ready <= 1'b1;
|
||||
while (1'b1) begin
|
||||
@(posedge cfg.vif.clk);
|
||||
if (cfg.vif.valid)
|
||||
break;
|
||||
end
|
||||
|
||||
cfg.vif.ready <= 1'b0;
|
||||
endtask
|
||||
|
||||
// Lower the req line for the given number of cycles
|
||||
virtual task automatic lower_req(int unsigned num_cycles);
|
||||
cfg.vif.req <= 1'b0;
|
||||
repeat (num_cycles) @(posedge cfg.vif.clk);
|
||||
cfg.vif.req <= 1'b1;
|
||||
endtask
|
||||
|
||||
// Raise the invalidate line for a randomly chosen number of cycles > 0.
|
||||
virtual task automatic invalidate();
|
||||
int unsigned num_cycles;
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(num_cycles,
|
||||
num_cycles dist { 0 :/ 499, [1:20] :/ 1 };)
|
||||
cfg.vif.invalidate_pulse(num_cycles);
|
||||
endtask
|
||||
|
||||
endclass
|
||||
|
|
|
@ -2,10 +2,66 @@
|
|||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
interface ibex_icache_if ();
|
||||
interface ibex_icache_if (input clk);
|
||||
|
||||
// interface pins
|
||||
// Set when core is enabled (and might request instructions soon)
|
||||
logic req;
|
||||
|
||||
// debug signals
|
||||
// Branch request
|
||||
logic branch;
|
||||
logic [31:0] branch_addr;
|
||||
|
||||
// Passing instructions back to the core
|
||||
logic ready;
|
||||
logic valid;
|
||||
logic [31:0] rdata;
|
||||
logic [31:0] addr;
|
||||
logic err;
|
||||
logic err_plus2;
|
||||
|
||||
// Enable/disable or invalidate the cache
|
||||
logic enable;
|
||||
logic invalidate;
|
||||
|
||||
// Busy signal from the cache (either there's a request on the bus or the cache is invalidating
|
||||
// itself)
|
||||
logic busy;
|
||||
|
||||
// Drive the branch pin for a single cycle, redirecting the cache to the given instruction
|
||||
// address.
|
||||
task automatic branch_to(logic [31:0] addr);
|
||||
branch <= 1'b1;
|
||||
branch_addr <= addr;
|
||||
|
||||
@(posedge clk);
|
||||
|
||||
branch <= 1'b0;
|
||||
branch_addr <= 'X;
|
||||
endtask
|
||||
|
||||
// Raise a pulse on the invalidate line for the given number of cycles.
|
||||
//
|
||||
// A one-cycle pulse will start an invalidation, but testing might want a longer pulse (which the
|
||||
// cache should support)
|
||||
task automatic invalidate_pulse(int num_cycles);
|
||||
invalidate <= 1'b1;
|
||||
repeat (num_cycles) @(posedge clk);
|
||||
invalidate <= 1'b0;
|
||||
endtask
|
||||
|
||||
// A task that waits for num_clks posedges on the clk signal
|
||||
task automatic wait_clks(int num_clks);
|
||||
repeat (num_clks) @(posedge clk);
|
||||
endtask
|
||||
|
||||
// Reset all the signals from the core to the cache (the other direction is controlled by the
|
||||
// DUT)
|
||||
task automatic reset();
|
||||
req <= 1'b0;
|
||||
branch <= 1'b0;
|
||||
ready <= 1'b0;
|
||||
enable <= 1'b0;
|
||||
invalidate <= 1'b0;
|
||||
endtask
|
||||
|
||||
endinterface
|
||||
|
|
|
@ -4,9 +4,70 @@
|
|||
|
||||
class ibex_icache_item extends uvm_sequence_item;
|
||||
|
||||
// random variables
|
||||
// The type of transaction
|
||||
rand ibex_icache_trans_type_e trans_type;
|
||||
|
||||
// The branch address for a branch transaction (only has effect if trans_type is
|
||||
// ICacheTransTypeBranch)
|
||||
rand bit [31:0] branch_addr;
|
||||
|
||||
// Whether the cache enable/disable should be toggled
|
||||
rand bit toggle_enable;
|
||||
|
||||
// Whether to invalidate the cache
|
||||
rand bit invalidate;
|
||||
|
||||
// The number of instructions to read (always non-negative, but may be zero)
|
||||
rand int num_insns;
|
||||
|
||||
constraint c_non_branch_trans_addr {
|
||||
// If the transaction type is ICacheTransTypeBranch then branch_addr can be anything. To make
|
||||
// reading debug logs a bit easier, we force it to be zero otherwise.
|
||||
(trans_type != ICacheTransTypeBranch) -> branch_addr == 0;
|
||||
|
||||
// Pick trans_type before the other values. We need to do this because constraining branch_addr
|
||||
// to 0 for non-branch transactions would otherwise mean branch transactions got weighted 2^32
|
||||
// times higher.
|
||||
solve trans_type before branch_addr;
|
||||
}
|
||||
|
||||
constraint c_branch_addr_alignment {
|
||||
// The branch address is always required to be half-word aligned. We don't bother conditioning
|
||||
// this on the type of transaction because branch_addr is forced to be zero in anything but a
|
||||
// branch transaction.
|
||||
!branch_addr[0];
|
||||
}
|
||||
|
||||
constraint c_toggle_enable_dist {
|
||||
// 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)
|
||||
toggle_enable dist { 0 :/ 49, 1 :/ 1 };
|
||||
}
|
||||
|
||||
constraint c_invalidate_dist {
|
||||
// Poke the cache invalidate line one time in 500. This takes ages and we don't want to
|
||||
// accidentally spend most of the test waiting for invalidation.
|
||||
invalidate dist { 0 :/ 499, 1 :/ 1 };
|
||||
}
|
||||
|
||||
constraint c_num_insns_dist {
|
||||
// For branch transactions, we want to read zero instructions reasonably frequently. For req
|
||||
// transactions, much less so. Also, we don't bother with long sequences for req transactions:
|
||||
// they won't look any different from the tail end of branch transactions from the cache's point
|
||||
// of view.
|
||||
if (trans_type == ICacheTransTypeBranch)
|
||||
num_insns dist { 0 :/ 5, [1:20] :/ 20, [21:100] :/ 1 };
|
||||
else
|
||||
num_insns dist { 0 :/ 1, [1:20] :/ 20 };
|
||||
}
|
||||
|
||||
|
||||
`uvm_object_utils_begin(ibex_icache_item)
|
||||
`uvm_field_enum(ibex_icache_trans_type_e, trans_type, UVM_DEFAULT)
|
||||
`uvm_field_int (branch_addr, UVM_DEFAULT | UVM_HEX)
|
||||
`uvm_field_int (toggle_enable, UVM_DEFAULT)
|
||||
`uvm_field_int (invalidate, UVM_DEFAULT)
|
||||
`uvm_field_int (num_insns, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Sanity test seq
|
||||
//
|
||||
// This is unlikely to find many cache hits (since it branches all over the 4GiB address space).
|
||||
class ibex_icache_sanity_seq extends ibex_icache_base_seq;
|
||||
`uvm_object_utils(ibex_icache_sanity_seq)
|
||||
`uvm_object_new
|
||||
|
||||
rand int count;
|
||||
constraint c_count { count > 0; count < 100; }
|
||||
|
||||
task body();
|
||||
// Generate a request which is constrained to have trans_type ICacheTransTypeBranch: the core
|
||||
// must start with a branch to tell the cache where to fetch from in the first place.
|
||||
req = ibex_icache_item::type_id::create("req");
|
||||
start_item(req);
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(req, req.trans_type == ICacheTransTypeBranch;)
|
||||
finish_item(req);
|
||||
|
||||
// Generate and run count ibex_icache_item sequence items (with no other constraint) through the
|
||||
// req port.
|
||||
repeat (count - 1) begin
|
||||
start_item(req);
|
||||
`DV_CHECK_RANDOMIZE_FATAL(req)
|
||||
finish_item(req);
|
||||
end
|
||||
endtask
|
||||
endclass
|
|
@ -3,3 +3,4 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`include "ibex_icache_base_seq.sv"
|
||||
`include "ibex_icache_sanity_seq.sv"
|
||||
|
|
|
@ -17,13 +17,27 @@ module tb;
|
|||
|
||||
// interfaces
|
||||
clk_rst_if clk_rst_if(.clk(clk), .rst_n(rst_n));
|
||||
ibex_icache_if ibex_icache_if();
|
||||
ibex_mem_intf ibex_mem_intf();
|
||||
ibex_icache_if core_if (.clk(clk));
|
||||
ibex_mem_intf ibex_mem_intf();
|
||||
|
||||
// dut
|
||||
ibex_icache dut (
|
||||
.clk_i (clk ),
|
||||
.rst_ni (rst_n )
|
||||
.clk_i (clk),
|
||||
.rst_ni (rst_n),
|
||||
|
||||
// Connect icache <-> core interface
|
||||
.req_i (core_if.req),
|
||||
.branch_i (core_if.branch),
|
||||
.addr_i (core_if.branch_addr),
|
||||
.ready_i (core_if.ready),
|
||||
.valid_o (core_if.valid),
|
||||
.rdata_o (core_if.rdata),
|
||||
.addr_o (core_if.addr),
|
||||
.err_o (core_if.err),
|
||||
.err_plus2_o (core_if.err_plus2),
|
||||
.icache_enable_i (core_if.enable),
|
||||
.icache_inval_i (core_if.invalidate),
|
||||
.busy_o (core_if.busy)
|
||||
|
||||
// TODO: add remaining IOs and hook them
|
||||
);
|
||||
|
@ -32,7 +46,7 @@ module tb;
|
|||
// drive clk and rst_n from clk_if
|
||||
clk_rst_if.set_active();
|
||||
uvm_config_db#(virtual clk_rst_if)::set(null, "*.env", "clk_rst_vif", clk_rst_if);
|
||||
uvm_config_db#(virtual ibex_icache_if)::set(null, "*.env.m_ibex_icache_agent*", "vif", ibex_icache_if);
|
||||
uvm_config_db#(virtual ibex_icache_if)::set(null, "*.env.m_ibex_icache_agent*", "vif", core_if);
|
||||
uvm_config_db#(virtual ibex_mem_intf)::set(null, "*.env.m_ibex_mem_intf_slave_agent*", "vif", ibex_mem_intf);
|
||||
$timeformat(-12, 0, " ps", 12);
|
||||
run_test();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue