diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/core_ibex_ifetch_pmp_if.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/core_ibex_ifetch_pmp_if.sv new file mode 100644 index 00000000..9555102f --- /dev/null +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/core_ibex_ifetch_pmp_if.sv @@ -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 diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent.sv index 94b01ec4..99c38d9f 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent.sv @@ -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 diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent_pkg.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent_pkg.sv index 8091eae3..537ee1f7 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent_pkg.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_agent_pkg.sv @@ -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 diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv index c25a7937..42b584bc 100644 --- a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_cosim_scoreboard.sv @@ -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; diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_ifetch_pmp_monitor.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_ifetch_pmp_monitor.sv new file mode 100644 index 00000000..a5b232e3 --- /dev/null +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_ifetch_pmp_monitor.sv @@ -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 diff --git a/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_ifetch_pmp_seq_item.sv b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_ifetch_pmp_seq_item.sv new file mode 100644 index 00000000..d981d737 --- /dev/null +++ b/dv/uvm/core_ibex/common/ibex_cosim_agent/ibex_ifetch_pmp_seq_item.sv @@ -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 diff --git a/dv/uvm/core_ibex/ibex_dv.f b/dv/uvm/core_ibex/ibex_dv.f index 31ed8519..c8efc8fb 100644 --- a/dv/uvm/core_ibex/ibex_dv.f +++ b/dv/uvm/core_ibex/ibex_dv.f @@ -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 diff --git a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv index 5273ec1f..7d5998a9 100644 --- a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv +++ b/dv/uvm/core_ibex/tb/core_ibex_tb_top.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