[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:
Greg Chadwick 2021-11-05 11:26:19 +00:00 committed by Greg Chadwick
parent 5e7c2cf00a
commit fa3df3b8ee
8 changed files with 143 additions and 20 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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