Bug Report: With WritebackStage=1, FENCE.I refetches while an older store is still outstanding, so the next instructions can be stale

Đang mở
#2,574 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 2 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
55/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Lĩnh vực
operating-systems

Hướng nghiên cứu

Start with the ID/EX FSM in rtl/ibex_id_stage.sv, especially the jump_in_dec case, and the decoder entry in rtl/ibex_decoder.sv that treats FENCE.I as a jump. Reproduce with the attached bench at data latency 2, then check that FENCE.I delays its refetch until the older store responds while ordinary jumps retain early fetch. Done when the instruction after FENCE.I sees the updated encoding at all tested latencies.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Type:Bug

Hello I believe I've found a bug in this core. Please take a look.

Observed Behavior

With WritebackStage = 1, a store to instruction memory directly followed by FENCE.I can leave the next instruction stale. While the store waits in WB for its response, FENCE.I sits stalled in ID/EX, but its jump to pc+4 (flush and refetch) has already been issued. The refetch can reach memory before the store is performed, and the instruction after FENCE.I runs its old encoding.

data response latency expected CHERIoT On RV32E WritebackStage = 0 (RV32E)
1 cycle (+dlat=1) 7, 9 7, 9 7, 9 7, 9
2 cycles (+dlat=2) 7, 9 1, 2 1, 2 7, 9
3 cycles (+dlat=3) 7, 9 1, 2 1, 2 7, 9

BranchTargetALU = 1 (the opentitan / maxperf* setting) gives 1, 2 at latency 2, like the default build.

Cause: FENCE.I is implemented as a jump to pc+4, and with the writeback stage a jump raises its pc set while an older load/store is still outstanding in WB. The early fetch is by design for ordinary jumps (comment at ibex_id_stage.sv:803-809); for FENCE.I it lets the refetch overtake the store FENCE.I is meant to order.

what where
FENCE.I is decoded as a jump to the next pc rtl/ibex_decoder.sv:652
the jump is raised when the instruction executes speculatively rtl/ibex_id_stage.sv:885
jump_set_raw = jump_set_dec for every jump, FENCE.I included rtl/ibex_id_stage.sv:937
instr_executing_spec does not wait for an outstanding load/store in WB rtl/ibex_id_stage.sv:1045

Attached: simulation logs (evidence/shipped_*.log) and a formal counterexample (waveform/formal_cex.vcd, depth 10, data port answering 1 or 2 cycles after the grant): a store sh to 0x800000bc is granted in cycle 4 and answered in cycle 6; FENCE.I is stalled in ID/EX from cycle 5, but pc_set is 1 in cycle 5 and the refetch of 0x800000bc is granted then; in cycle 8 the instruction at 0x800000bc retires with its old encoding.

Expected Behavior

The instruction after FENCE.I runs its new encoding at every data latency (7, 9), as with WritebackStage = 0. RISC-V Unprivileged Specification, "Zifencei":

A FENCE.I instruction ensures that a subsequent instruction fetch on a RISC-V hart will see any previous data stores already visible to the same RISC-V hart.

A possible fix: do not raise FENCE.I's jump while an older memory access is outstanding; keep it in its first cycle until the access completes (it is stalled then anyway). In the jump_in_dec case of the ID/EX FSM, for example:

            jump_in_dec: begin
              if (icache_inval_o && (outstanding_load_wb_i | outstanding_store_wb_i) && ~lsu_resp_valid_i) begin
                id_fsm_d   = FIRST_CYCLE;   // FENCE.I: wait for the older load/store before refetching
                stall_jump = 1'b1;
              end else begin
                id_fsm_d     = BranchTargetALU ? FIRST_CYCLE : MULTI_CYCLE;
                stall_jump   = ~BranchTargetALU;
                jump_set_raw = jump_set_dec;
              end
            end

Ordinary jumps keep their early fetch.

Steps to reproduce the issue

Parameters: ibex_top with BaseIsa = ibex_pkg::BaseIsaRV32IorCHERIoT, RV32E = 1, WritebackStage = 1, other parameters at their defaults (no icache); cheriot_enable_i held On and, in a second run, Off (RV32E mode). Needed: WritebackStage = 1 and a data memory that answers a store more than one cycle after its grant (allowed by doc/03_reference/load_store_unit.rst). ibex_configs.yaml sets WritebackStage: 1 in opentitan, maxperf* and experimental-branch-predictor.

The upstream Simple System ties cheriot_enable_i off and answers data in one cycle, so the attached bench tb_ibex_cheriot.sv instantiates ibex_top directly (see README.txt). It answers a data request +dlat=N cycles after the grant and performs a store when it answers; fetches are answered one cycle after their grant. +define+AB_WB0 builds WritebackStage = 0, +define+AB_BTALU builds BranchTargetALU = 1.

fencei_smc.S (with crt.S, common.h, link.ld):

  sw    x12, 0(x11)      # x12 = 0x00700513 = addi x10, x0, 7; x11 -> target
  fence.i
target:
  addi  x10, x0, 1       # old encoding; report x10, expected 7

The second case stores addi x10, x0, 9 over addi x10, x0, 2.

  1. git clone https://github.com/lowRISC/ibex && cd ibex && git checkout 626034d7
  2. Run the attached script, which assembles the program, builds the bench with Verilator (+define+SYNTHESIS) and runs it at latency 1, 2 and 3 in both modes:
    sh run.sh <ibex checkout>
    # assembles with: riscv32-unknown-elf-gcc -march=rv32emc_zicsr_zifencei_zcb -mabi=ilp32e -nostdlib -nostartfiles -T link.ld crt.S fencei_smc.S
    
  3. Each SIG line is the value of x10 for one case. Output at latency 2 (latency 3 is the same; latency 1 prints 7, 9):
    CHERIoT mode, data latency 2:
    SIG 00000001
    SIG 00000002
    RV32E mode, data latency 2:
    SIG 00000001
    SIG 00000002
    

No changes to the Ibex source code.

My Environment

EDA tool and version:
Verilator 5.044 (simulation); riscv32-unknown-elf-gcc 16.1.0.

Operating system:
Red Hat Enterprise Linux 8.10

Version of the Ibex source code:
626034d7557ab85a6d989b13011b44296bad26fa (master, 2026-11-06), unmodified.

All of the relevant materials pertaining to the bug:

FENCEI_SPEC_bug.zip

Ngôn ngữ chính
SystemVerilog
Star
2.2k
Fork
836
Merge trung bình
5 ngày 8 giờ
Pull request đã merge (30 ngày)
22

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của lowRISC/ibex

Tất cả issue của lowRISC/ibex

Issue tương tự

Thêm issue về Operating Systems

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.