From fc654f067ae9c034ce81dcc791bfef0a977067a5 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Fri, 5 Jun 2026 11:09:12 +0200 Subject: [PATCH 1/2] [rtl] Fix speculative increment of `minstret` The `wb_count_q` signal can become stale. It tracks whether the current or previous retired instruction increments the `instret` count. If there is a bubble before an `minstret` read, the counter will be updated, but `wb_count_q` and therefore `perf_instr_ret_wb_spec_o` will incorrectly suggest speculatively incrementing the counter further. This led to the `instret` counter being one instruction ahead every time we read it after a stall cycle. We fix this by validating `wb_count_q` and only incrementing speculatively if we actually have to. That is, when we are executing instructions back-to-back and the counter hasn't updated yet. --- rtl/ibex_wb_stage.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl/ibex_wb_stage.sv b/rtl/ibex_wb_stage.sv index 38ce421ca0..5c8075de4e 100644 --- a/rtl/ibex_wb_stage.sv +++ b/rtl/ibex_wb_stage.sv @@ -155,7 +155,7 @@ module ibex_wb_stage #( // Speculative versions of the signals do not factor in exceptions and whether the instruction // is done yet. These are used to get correct values for instructions reading the relevant // performance counters in the ID stage. - assign perf_instr_ret_wb_spec_o = wb_count_q; + assign perf_instr_ret_wb_spec_o = wb_count_q & wb_valid_q; assign perf_instr_ret_compressed_wb_spec_o = perf_instr_ret_wb_spec_o & wb_compressed_q; assign perf_instr_ret_wb_o = instr_done_wb_o & wb_count_q & ~(lsu_resp_valid_i & lsu_resp_err_i); From 05bb791cf1103f052521a3744b0f5a3827d62a76 Mon Sep 17 00:00:00 2001 From: Samuel Riedel Date: Fri, 5 Jun 2026 16:04:56 +0200 Subject: [PATCH 2/2] [rtl] Fix incrementing `minstret` after setting it According to the Specification Section 6.1 CSR Instructions: Some CSRs, such as the instructions-retired counter, instret, may be modified as side effects of instruction execution. In these cases, if a CSR access instruction reads a CSR, it reads the value prior to the execution of the instruction. If a CSR access instruction writes such a CSR, the explicit write is done instead of the update from the side effect. In particular, a value written to instret by one instruction will be the value read by the following instruction. --- rtl/ibex_id_stage.sv | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/rtl/ibex_id_stage.sv b/rtl/ibex_id_stage.sv index 81fe19253f..a7c27f7683 100644 --- a/rtl/ibex_id_stage.sv +++ b/rtl/ibex_id_stage.sv @@ -1076,9 +1076,15 @@ module ibex_id_stage #( end // Signal which instructions to count as retired in minstret, all traps along with ebrk and - // ecall instructions are not counted. + // ecall instructions are not counted. Writes to minstret/minstreth themselves are also excluded + // to avoid incrementing right after a write. + logic minstret_write; + assign minstret_write = csr_access_o & + (csr_op_o inside {CSR_OP_WRITE, CSR_OP_SET, CSR_OP_CLEAR}) & + (csr_addr_o inside {CSR_MINSTRET, CSR_MINSTRETH}); + assign instr_perf_count_id_o = ~ebrk_insn & ~ecall_insn_dec & ~illegal_insn_dec & - ~illegal_csr_insn_i & ~instr_fetch_err_i; + ~illegal_csr_insn_i & ~instr_fetch_err_i & ~minstret_write; // An instruction is ready to move to the writeback stage (or retire if there is no writeback // stage)