Repository navigation
Description
Observed Behavior
On lowRISC Ibex f451552 built with +define+RVFI, every synchronous exception handler entry (ecall, ebreak, illegal instruction, load/store/fetch access fault) retires with rvfi_intr=0, while every interrupt or NMI handler entry retires with rvfi_intr=1: 37/37 exception handler entries have rvfi_intr=0 and 27/27 interrupt/NMI entries have rvfi_intr=1 across 72 archived traces, with no spurious entry in either direction. Severity P2: trace-only, no architectural state corruption.
rvfi_intr marks the first instruction of a trap handler, for synchronous exceptions and asynchronous interrupts alike. It is not the same signal as rvfi_trap: the faulting instruction retires with rvfi_trap=1, and the first handler instruction retires with rvfi_intr=1, on different rows.
In rtl/ibex_core.sv:2409-2421 the condition at :2417 accepts only EXC_PC_IRQ, so the trap-PC assignment it guards at :2419 never runs for EXC_PC_EXC:
// rtl/ibex_core.sv:2409-2421
// rvfi_intr must be set for first instruction that is part of a trap handler.
assign rvfi_intr_d = instr_first_cycle_id ? rvfi_set_trap_pc_q : rvfi_intr_q;
...
if (pc_set && pc_mux_id == PC_EXC && (exc_pc_mux_id == EXC_PC_IRQ)) begin
rvfi_set_trap_pc_d = 1'b1;EXC_PC_DBD and EXC_PC_DBG_EXC are debug entries, not trap handlers, and correctly stay out. PR #191 (merged 2019-07-26, "RVFI: re-add accidentally removed rvfi_intr signal") introduced the marker with both terms, so the exception case was implemented upstream and later dropped:
PR #191: if (pc_set && pc_mux_id == PC_EXC &&
(exc_pc_mux_id == EXC_PC_EXC || exc_pc_mux_id == EXC_PC_IRQ)) begin
now: if (pc_set && pc_mux_id == PC_EXC && (exc_pc_mux_id == EXC_PC_IRQ)) beginNo upstream issue covers the exception-entry case; #2231 is about rvfi_pc_wdata for mret/dret, a different field.
Expected Behavior
riscv-formal/docs/rvfi.md:35, the interface Ibex names in doc/03_reference/rvfi.rst, requires: "rvfi_intr must be set for the first instruction that is part of a trap handler, i.e. an instruction that has a rvfi_pc_rdata that does not match the rvfi_pc_wdata of the previous instruction."
A synchronous exception vector entry qualifies: mtvec is the trap-vector base for exceptions and interrupts alike, so the first instruction fetched from mtvec, or from mtvec + 4*cause for a vectored interrupt, should retire with rvfi_intr=1, while the faulting instruction should still retire with rvfi_trap=1.
The minimal fix adds the synchronous-exception term at rtl/ibex_core.sv:2417 and keeps the debug-entry exclusion:
if (pc_set && pc_mux_id == PC_EXC &&
(exc_pc_mux_id == EXC_PC_EXC || exc_pc_mux_id == EXC_PC_IRQ)) beginEXC_PC_DBD and EXC_PC_DBG_EXC remain excluded because they are debug entries, not trap handlers.
Steps to reproduce the issue
From the directory holding run.sh, tb_rvfi.sv, prog_ecall.hex and prog_timer.hex:
IBEX=/path/to/ibex ./run.shrun.sh builds ibex_core with Verilator twice (WB=0,BTALU=0 and WB=1,BTALU=1), runs both images in each configuration with +MAXCYC=900, and compares one RVFI dump row per program: ecall at 0x1080 vectors to 0x1000, and the timer IRQ asserted from reset (+IRQ0=2 +IRQ0LEN=60) is enabled by mie.MTIE/mstatus.MIE and vectors to 0x101c.
Expected output line, with exit 0:
VERDICT: BUG_CONFIRMED IBEX-RVFI-INTR-01
A build failure prints VERDICT: BUILD_FAILED and exits 2; a check failure prints VERDICT: CHECK_FAILED and exits 1.
My Environment
| EDA tool and version | Verilator 5.051 (rev vUNKNOWN, built 2026-08-06) |
| Operating system | Ubuntu 24.04.5 LTS, Linux 6.18.33.2-microsoft-standard-WSL2 x86_64 |
| Version of the Ibex source code | f451552, rtl/ibex_core.sv sha256 843a0d57aff3b43473d3fd2d2148d48b44f16b7b006da4e778def107a6dab0a5 |
Activity
[rtl] Set rvfi_intr on entry to synchronous exception handlers
h-filali commentedon Nov 6, 2026
Thanks @qinkejiu I created #2572 to address this issue.