mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-06-27 17:00:57 -04:00
[Fix] fence.i fails to synchronize ICache/DCache flushes in write-back mode (#2971)
Fix fence.i synchronization issue with HPDCache write-back Data Add [halt_frontend_o] signal to stall instruction fetch during fence.i Introduce [fence_i_active] state to track ICache+DCache flush progress Gate ICache requests and NPC updates when fence.i is active Resolves stale instruction fetch in self-modifying code scenarios (riscv-tests/rv32ui/fence_i) Enables Linux boot with HPDCache write-back data by ensuring ICache/DCache coherency Note: This was not seen with [std_dcache] in standard [fence_i] tests, since it normally finishes write‑back before the ICache flush completes. However, for large self‑modifying code regions, the write‑back can lag behind the ICache flush, leading to stale instruction fetches even with [std_dcache]. Problem: When executing self-modifying code or booting Linux with a write-back D$, fence.i flushes the ICache but allows the frontend to fetch new instructions before the DCache flush completes. This causes the core to execute stale instructions from the ICache that were modified in the DCache (Dirty data may not be written back because the DCache is still flushing). Root Cause: The frontend resumes fetching immediately after ICache flush, even if the DCache is still flushing. NPC (Next PC) continues advancing after ICache flush, leading to incorrect instruction fetch. Solution: Introduce halt_frontend_o signal to freeze frontend during fence.i. Add fence_i_active state in controller to track combined ICache+DCache flush. Gate ICache requests (icache_dreq_o.req) and NPC updates (if_ready) during fence_i_active.
This commit is contained in:
parent
2700d14471
commit
6d08ed1389
3 changed files with 29 additions and 8 deletions
|
@ -53,6 +53,8 @@ module controller
|
|||
input logic halt_csr_i,
|
||||
// Halt request from accelerator dispatcher - ACC_DISPATCHER
|
||||
input logic halt_acc_i,
|
||||
// Halt frontend during fence.i to prevent fetching stale instructions
|
||||
output logic halt_frontend_o,
|
||||
// Halt signal to commit stage - COMMIT_STAGE
|
||||
output logic halt_o,
|
||||
// Return from exception - CSR_REGFILE
|
||||
|
@ -84,12 +86,15 @@ module controller
|
|||
// active fence - high if we are currently flushing the dcache
|
||||
logic fence_active_d, fence_active_q;
|
||||
logic flush_dcache;
|
||||
// Added fence_i_active state to track fence.i progress
|
||||
logic fence_i_active_d, fence_i_active_q;
|
||||
|
||||
// ------------
|
||||
// Flush CTRL
|
||||
// ------------
|
||||
always_comb begin : flush_ctrl
|
||||
fence_active_d = fence_active_q;
|
||||
fence_i_active_d = fence_i_active_q;
|
||||
set_pc_commit_o = 1'b0;
|
||||
flush_if_o = 1'b0;
|
||||
flush_unissued_instr_o = 1'b0;
|
||||
|
@ -142,16 +147,22 @@ module controller
|
|||
flush_icache_o = 1'b1;
|
||||
// this is not needed in the case since we
|
||||
// have a write-through cache in this case
|
||||
// When handling fence.i, flush both caches and activate fence_i state
|
||||
if (CVA6Cfg.DcacheFlushOnFence) begin
|
||||
flush_dcache = 1'b1;
|
||||
flush_dcache = 1'b1;
|
||||
fence_active_d = 1'b1;
|
||||
fence_i_active_d = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// this is not needed in the case since we
|
||||
// have a write-through cache in this case
|
||||
if (CVA6Cfg.DcacheFlushOnFence) begin
|
||||
// wait for the acknowledge here
|
||||
// Wait for the acknowledge here
|
||||
// Deassert fence_i state only after DCache flush completes
|
||||
if (flush_dcache_ack_i && fence_i_active_q) begin
|
||||
fence_i_active_d = 1'b0;
|
||||
end
|
||||
if (flush_dcache_ack_i && fence_active_q) begin
|
||||
fence_active_d = 1'b0;
|
||||
// keep the flush dcache signal high as long as we didn't get the acknowledge from the cache
|
||||
|
@ -243,6 +254,8 @@ module controller
|
|||
always_comb begin
|
||||
// halt the core if the fence is active
|
||||
halt_o = halt_csr_i || halt_acc_i || (CVA6Cfg.DcacheFlushOnFence && fence_active_q);
|
||||
// Halt frontend during fence.i to synchronize ICache/DCache flushes
|
||||
halt_frontend_o = fence_i_active_q;
|
||||
end
|
||||
|
||||
// ----------------------
|
||||
|
@ -250,12 +263,14 @@ module controller
|
|||
// ----------------------
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
fence_active_q <= 1'b0;
|
||||
flush_dcache_o <= 1'b0;
|
||||
fence_active_q <= 1'b0;
|
||||
fence_i_active_q <= 1'b0;
|
||||
flush_dcache_o <= 1'b0;
|
||||
end else begin
|
||||
fence_active_q <= fence_active_d;
|
||||
fence_active_q <= fence_active_d;
|
||||
fence_i_active_q <= fence_i_active_d;
|
||||
// register on the flush signal, this signal might be critical
|
||||
flush_dcache_o <= flush_dcache;
|
||||
flush_dcache_o <= flush_dcache;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
|
@ -622,6 +622,7 @@ module cva6
|
|||
logic hfence_vvma_commit_controller;
|
||||
logic hfence_gvma_commit_controller;
|
||||
logic halt_ctrl;
|
||||
logic halt_frontend;
|
||||
logic halt_csr_ctrl;
|
||||
logic dcache_flush_ctrl_cache;
|
||||
logic dcache_flush_ack_cache_ctrl;
|
||||
|
@ -677,6 +678,7 @@ module cva6
|
|||
.flush_bp_i (1'b0),
|
||||
.flush_i (flush_ctrl_if), // not entirely correct
|
||||
.halt_i (halt_ctrl),
|
||||
.halt_frontend_i (halt_frontend),
|
||||
.set_pc_commit_i (set_pc_ctrl_pcgen),
|
||||
.pc_commit_i (pc_commit),
|
||||
.ex_valid_i (ex_commit.valid),
|
||||
|
@ -1283,6 +1285,7 @@ module cva6
|
|||
.flush_tlb_gvma_o (flush_tlb_gvma_ctrl_ex),
|
||||
.halt_csr_i (halt_csr_ctrl),
|
||||
.halt_acc_i (halt_acc_ctrl),
|
||||
.halt_frontend_o (halt_frontend),
|
||||
.halt_o (halt_ctrl),
|
||||
// control ports
|
||||
.eret_i (eret),
|
||||
|
|
|
@ -36,6 +36,8 @@ module frontend
|
|||
input logic flush_i,
|
||||
// Halt requested by WFI and Accelerate port - CONTROLLER
|
||||
input logic halt_i,
|
||||
// Halt frontend - CONTROLLER (in the case of fence_i to avoid fetching an old instruction)
|
||||
input logic halt_frontend_i,
|
||||
// Set COMMIT PC as next PC requested by FENCE, CSR side-effect and Accelerate port - CONTROLLER
|
||||
input logic set_pc_commit_i,
|
||||
// COMMIT PC - COMMIT
|
||||
|
@ -306,8 +308,9 @@ module frontend
|
|||
assign is_mispredict = resolved_branch_i.valid & resolved_branch_i.is_mispredict;
|
||||
|
||||
// Cache interface
|
||||
assign icache_dreq_o.req = instr_queue_ready;
|
||||
assign if_ready = icache_dreq_i.ready & instr_queue_ready;
|
||||
// Gate ICache requests and NPC updates during fence.i
|
||||
assign icache_dreq_o.req = instr_queue_ready & ~halt_frontend_i;
|
||||
assign if_ready = icache_dreq_i.ready & instr_queue_ready & ~halt_frontend_i;
|
||||
// We need to flush the cache pipeline if:
|
||||
// 1. We mispredicted
|
||||
// 2. Want to flush the whole processor front-end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue