mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-22 13:07:46 -04:00
[dv] Fix PMP error handling for icache
The icache uses a single bit to signify an error. This could either be a PMP error or a fetch error. Add extra probing so the testbench can differentiate between the two cases.
This commit is contained in:
parent
5e7c2cf00a
commit
fa3df3b8ee
8 changed files with 143 additions and 20 deletions
|
@ -0,0 +1,22 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
interface core_ibex_ifetch_pmp_if(input logic clk);
|
||||
logic reset;
|
||||
|
||||
logic fetch_valid;
|
||||
logic [31:0] fetch_addr;
|
||||
logic fetch_pmp_err;
|
||||
|
||||
clocking monitor_cb @(posedge clk);
|
||||
input reset;
|
||||
input fetch_valid;
|
||||
input fetch_addr;
|
||||
input fetch_pmp_err;
|
||||
endclocking
|
||||
|
||||
task automatic wait_clks(input int num);
|
||||
repeat (num) @(posedge clk);
|
||||
endtask
|
||||
endinterface : core_ibex_ifetch_pmp_if
|
|
@ -7,6 +7,7 @@
|
|||
class ibex_cosim_agent extends uvm_agent;
|
||||
ibex_rvfi_monitor rvfi_monitor;
|
||||
ibex_ifetch_monitor ifetch_monitor;
|
||||
ibex_ifetch_pmp_monitor ifetch_pmp_monitor;
|
||||
ibex_cosim_scoreboard scoreboard;
|
||||
|
||||
uvm_analysis_export#(ibex_mem_intf_seq_item) dmem_port;
|
||||
|
@ -24,9 +25,10 @@ class ibex_cosim_agent extends uvm_agent;
|
|||
virtual function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
|
||||
rvfi_monitor = ibex_rvfi_monitor::type_id::create("rvfi_monitor", this);
|
||||
scoreboard = ibex_cosim_scoreboard::type_id::create("scoreboard", this);
|
||||
ifetch_monitor = ibex_ifetch_monitor::type_id::create("ifetch_monitor", this);
|
||||
rvfi_monitor = ibex_rvfi_monitor::type_id::create("rvfi_monitor", this);
|
||||
scoreboard = ibex_cosim_scoreboard::type_id::create("scoreboard", this);
|
||||
ifetch_monitor = ibex_ifetch_monitor::type_id::create("ifetch_monitor", this);
|
||||
ifetch_pmp_monitor = ibex_ifetch_pmp_monitor::type_id::create("ifetch_pmp_monitor", this);
|
||||
endfunction: build_phase
|
||||
|
||||
virtual function void connect_phase(uvm_phase phase);
|
||||
|
@ -34,6 +36,7 @@ class ibex_cosim_agent extends uvm_agent;
|
|||
|
||||
rvfi_monitor.item_collected_port.connect(scoreboard.rvfi_port.analysis_export);
|
||||
ifetch_monitor.item_collected_port.connect(scoreboard.ifetch_port.analysis_export);
|
||||
ifetch_pmp_monitor.item_collected_port.connect(scoreboard.ifetch_pmp_port.analysis_export);
|
||||
dmem_port.connect(scoreboard.dmem_port.analysis_export);
|
||||
imem_port.connect(scoreboard.imem_port.analysis_export);
|
||||
endfunction: connect_phase
|
||||
|
|
|
@ -14,6 +14,8 @@ package ibex_cosim_agent_pkg;
|
|||
`include "ibex_rvfi_monitor.sv"
|
||||
`include "ibex_ifetch_seq_item.sv"
|
||||
`include "ibex_ifetch_monitor.sv"
|
||||
`include "ibex_ifetch_pmp_seq_item.sv"
|
||||
`include "ibex_ifetch_pmp_monitor.sv"
|
||||
`include "ibex_cosim_scoreboard.sv"
|
||||
`include "ibex_cosim_agent.sv"
|
||||
`endif
|
||||
|
|
|
@ -10,14 +10,16 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
|
||||
core_ibex_cosim_cfg cfg;
|
||||
|
||||
uvm_tlm_analysis_fifo #(ibex_rvfi_seq_item) rvfi_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) dmem_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) imem_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_ifetch_seq_item) ifetch_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_rvfi_seq_item) rvfi_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) dmem_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_mem_intf_seq_item) imem_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_ifetch_seq_item) ifetch_port;
|
||||
uvm_tlm_analysis_fifo #(ibex_ifetch_pmp_seq_item) ifetch_pmp_port;
|
||||
|
||||
virtual core_ibex_instr_monitor_if instr_vif;
|
||||
|
||||
bit failed_iside_accesses [bit[31:0]];
|
||||
bit iside_pmp_failure [bit[31:0]];
|
||||
|
||||
typedef struct {
|
||||
bit [63:0] order;
|
||||
|
@ -31,11 +33,12 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
function new(string name="", uvm_component parent=null);
|
||||
super.new(name, parent);
|
||||
|
||||
rvfi_port = new("rvfi_port", this);
|
||||
dmem_port = new("dmem_port", this);
|
||||
imem_port = new("imem_port", this);
|
||||
ifetch_port = new("ifetch_port", this);
|
||||
cosim_handle = null;
|
||||
rvfi_port = new("rvfi_port", this);
|
||||
dmem_port = new("dmem_port", this);
|
||||
imem_port = new("imem_port", this);
|
||||
ifetch_port = new("ifetch_port", this);
|
||||
ifetch_pmp_port = new("ifetch_pmp_port", this);
|
||||
cosim_handle = null;
|
||||
endfunction
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
|
@ -70,8 +73,12 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
if (cfg.probe_imem_for_errs) begin
|
||||
run_cosim_imem();
|
||||
end else begin
|
||||
run_cosim_ifetch();
|
||||
fork
|
||||
run_cosim_ifetch();
|
||||
run_cosim_ifetch_pmp();
|
||||
join_any
|
||||
end
|
||||
|
||||
wait (instr_vif.instr_cb.reset === 1'b1);
|
||||
join_any
|
||||
disable fork;
|
||||
|
@ -182,29 +189,51 @@ class ibex_cosim_scoreboard extends uvm_scoreboard;
|
|||
end
|
||||
endtask: run_cosim_ifetch
|
||||
|
||||
task run_cosim_ifetch_pmp();
|
||||
ibex_ifetch_pmp_seq_item ifetch_pmp;
|
||||
|
||||
// Keep track of which addresses have seen PMP failures.
|
||||
forever begin
|
||||
ifetch_pmp_port.get(ifetch_pmp);
|
||||
|
||||
if (ifetch_pmp.fetch_pmp_err) begin
|
||||
iside_pmp_failure[ifetch_pmp.fetch_addr] = 1'b1;
|
||||
end else begin
|
||||
if (iside_pmp_failure.exists(ifetch_pmp.fetch_addr)) begin
|
||||
iside_pmp_failure.delete(ifetch_pmp.fetch_addr);
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task run_cosim_imem_errors();
|
||||
bit [63:0] latest_order = 64'hffffffff_ffffffff;
|
||||
bit [31:0] aligned_addr;
|
||||
bit [31:0] aligned_next_addr;
|
||||
forever begin
|
||||
// Wait for new instruction to appear in ID stage
|
||||
wait (instr_vif.instr_cb.valid_id &&
|
||||
instr_vif.instr_cb.instr_new_id &&
|
||||
latest_order != instr_vif.instr_cb.rvfi_order_id);
|
||||
// Determine if instruction comes from an address that has seen an error. If an error was seen
|
||||
// add the instruction order ID and address to iside_error_queue
|
||||
aligned_addr = instr_vif.instr_cb.pc_id & 32'hfffffffc;
|
||||
// Determine if the instruction comes from an address that has seen an error that wasn't a PMP
|
||||
// error (the icache records both PMP errors and fetch errors with the same error bits). If a
|
||||
// fetch error was seen add the instruction order ID and address to iside_error_queue.
|
||||
aligned_addr = instr_vif.instr_cb.pc_id & 32'hfffffffc;
|
||||
aligned_next_addr = aligned_addr + 32'd4;
|
||||
|
||||
if (failed_iside_accesses.exists(instr_vif.instr_cb.pc_id & 32'hfffffffc)) begin
|
||||
if (failed_iside_accesses.exists(aligned_addr) && !iside_pmp_failure.exists(aligned_addr))
|
||||
begin
|
||||
iside_error_queue.push_back('{order : instr_vif.instr_cb.rvfi_order_id,
|
||||
addr : instr_vif.instr_cb.pc_id & 32'hfffffffc});
|
||||
addr : aligned_addr});
|
||||
end else if (!instr_vif.instr_cb.is_compressed_id &&
|
||||
(instr_vif.instr_cb.pc_id & 32'h3) != 0 &&
|
||||
failed_iside_accesses.exists((instr_vif.instr_cb.pc_id + 32'd4) & 32'hfffffffc))
|
||||
failed_iside_accesses.exists(aligned_next_addr) &&
|
||||
!iside_pmp_failure.exists(aligned_next_addr))
|
||||
begin
|
||||
// Where an instruction crosses a 32-bit boundary, check if an error was seen on the other
|
||||
// side of the boundary
|
||||
iside_error_queue.push_back('{order : instr_vif.instr_cb.rvfi_order_id,
|
||||
addr : (instr_vif.instr_cb.pc_id + 32'd4) & 32'hfffffffc});
|
||||
addr : aligned_next_addr});
|
||||
end
|
||||
|
||||
latest_order = instr_vif.instr_cb.rvfi_order_id;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class ibex_ifetch_pmp_monitor extends uvm_monitor;
|
||||
protected virtual core_ibex_ifetch_pmp_if vif;
|
||||
|
||||
uvm_analysis_port#(ibex_ifetch_pmp_seq_item) item_collected_port;
|
||||
|
||||
`uvm_component_utils(ibex_ifetch_pmp_monitor)
|
||||
`uvm_component_new
|
||||
|
||||
function void build_phase(uvm_phase phase);
|
||||
super.build_phase(phase);
|
||||
|
||||
item_collected_port = new("item_collected_port", this);
|
||||
|
||||
if(!uvm_config_db#(virtual core_ibex_ifetch_pmp_if)::get(this, "", "ifetch_pmp_if", vif)) begin
|
||||
`uvm_fatal("NOVIF", {"virtual interface must be set for: ", get_full_name(), ".vif"});
|
||||
end
|
||||
endfunction
|
||||
|
||||
virtual task run_phase(uvm_phase phase);
|
||||
ibex_ifetch_pmp_seq_item trans_collected;
|
||||
|
||||
wait (vif.monitor_cb.reset === 1'b0);
|
||||
|
||||
forever begin
|
||||
while(!vif.monitor_cb.fetch_valid) vif.wait_clks(1);
|
||||
|
||||
trans_collected = ibex_ifetch_pmp_seq_item::type_id::create("trans_collected");
|
||||
trans_collected.fetch_addr = vif.monitor_cb.fetch_addr;
|
||||
trans_collected.fetch_pmp_err = vif.monitor_cb.fetch_pmp_err;
|
||||
|
||||
`uvm_info(`gfn, $sformatf("Seen ifetch:\n%s", trans_collected.sprint()),
|
||||
UVM_HIGH)
|
||||
|
||||
item_collected_port.write(trans_collected);
|
||||
|
||||
vif.wait_clks(1);
|
||||
end
|
||||
endtask: run_phase
|
||||
endclass : ibex_ifetch_pmp_monitor
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright lowRISC contributors.
|
||||
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
class ibex_ifetch_pmp_seq_item extends uvm_sequence_item;
|
||||
bit [31:0] fetch_addr;
|
||||
bit fetch_pmp_err;
|
||||
|
||||
`uvm_object_utils_begin(ibex_ifetch_pmp_seq_item)
|
||||
`uvm_field_int (fetch_addr, UVM_DEFAULT)
|
||||
`uvm_field_int (fetch_pmp_err, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
`uvm_object_new
|
||||
endclass : ibex_ifetch_pmp_seq_item
|
|
@ -95,6 +95,7 @@ ${PRJ_DIR}/dv/uvm/core_ibex/common/irq_agent/irq_if.sv
|
|||
${PRJ_DIR}/dv/uvm/core_ibex/common/irq_agent/irq_agent_pkg.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_rvfi_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_cosim_agent/core_ibex_ifetch_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_cosim_agent/core_ibex_ifetch_pmp_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent_pkg.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_instr_monitor_if.sv
|
||||
${PRJ_DIR}/dv/uvm/core_ibex/env/core_ibex_dut_probe_if.sv
|
||||
|
|
|
@ -32,6 +32,8 @@ module core_ibex_tb_top;
|
|||
|
||||
core_ibex_ifetch_if ifetch_if(.clk(clk));
|
||||
|
||||
core_ibex_ifetch_pmp_if ifetch_pmp_if(.clk(clk));
|
||||
|
||||
// VCS does not support overriding enum and string parameters via command line. Instead, a
|
||||
// `define is used that can be set from the command line. If no value has been specified, this
|
||||
// gives a default. Other simulators don't take the detour via `define and can override the
|
||||
|
@ -209,6 +211,11 @@ module core_ibex_tb_top;
|
|||
assign ifetch_if.fetch_err = dut.u_ibex_top.u_ibex_core.if_stage_i.fetch_err;
|
||||
assign ifetch_if.fetch_err_plus2 = dut.u_ibex_top.u_ibex_core.if_stage_i.fetch_err_plus2;
|
||||
|
||||
assign ifetch_pmp_if.reset = ~dut.u_ibex_top.u_ibex_core.if_stage_i.rst_ni;
|
||||
assign ifetch_pmp_if.fetch_valid = dut.u_ibex_top.u_ibex_core.instr_req_out;
|
||||
assign ifetch_pmp_if.fetch_addr = dut.u_ibex_top.u_ibex_core.instr_addr_o;
|
||||
assign ifetch_pmp_if.fetch_pmp_err = dut.u_ibex_top.u_ibex_core.pmp_req_err[ibex_pkg::PMP_I];
|
||||
|
||||
assign data_mem_vif.misaligned_first =
|
||||
dut.u_ibex_top.u_ibex_core.load_store_unit_i.handle_misaligned_d |
|
||||
((dut.u_ibex_top.u_ibex_core.load_store_unit_i.lsu_type_i == 2'b01) &
|
||||
|
@ -237,6 +244,7 @@ module core_ibex_tb_top;
|
|||
uvm_config_db#(virtual ibex_mem_intf)::set(null, "*instr_if_response*", "vif", instr_mem_vif);
|
||||
uvm_config_db#(virtual irq_if)::set(null, "*", "vif", irq_vif);
|
||||
uvm_config_db#(virtual core_ibex_ifetch_if)::set(null, "*", "ifetch_if", ifetch_if);
|
||||
uvm_config_db#(virtual core_ibex_ifetch_pmp_if)::set(null, "*", "ifetch_pmp_if", ifetch_pmp_if);
|
||||
run_test();
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue