From ce9f63adb739e759a18be8690c7d835381f10f8e Mon Sep 17 00:00:00 2001 From: Yinrong Li Date: Wed, 1 Jul 2026 17:30:38 +0200 Subject: [PATCH 1/3] Fix serial-lookup data corruption on coincident refill write When a lookup request reaches the data stage in the same cycle a refill writes its line, the single-port data SRAM prioritises the write and the request's read is preempted, so `data_rdata` holds stale bytes from an earlier read. The output select `refill_hit_q && !data_req_q.hit` trusted that stale read whenever the tag also hit, so the request was served another line's data. Under heavy, rapidly-interleaved fetch traffic (many cores thrashing the same hot lines through a shared read-only cache) this returned wrong instructions and could hang the system. The tag-hit-plus-coincident-refill case looks contradictory (why refill a line that is already valid?) but is reachable: the miss handler decides hit/miss from the lookup result, which is two cycles latent. A request that missed a line before it was cached can still be in flight to the handler when the earlier refill completes and frees its pending/MSHR entry, so the coalescing check misses it and a second, redundant refill is issued for the now-valid line. That redundant write is what collides with a later request that already tag-hits the line. - Serve the in-flight refill data on any refill_hit_q (drop the !data_req_q.hit qualifier on proper_rdata/out_way_o/out_error_o). - Qualify the refill-payload capture (refill_wdata_q/write_way_q/ write_error_q) with the tag handshake so a later request's refill cannot overwrite it while the owning request is stalled in the data stage. The data output is already shielded by the data_rsp_q fall-through buffer; this protects the un-buffered way/error outputs. --- src/snitch_icache_lookup_serial.sv | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/snitch_icache_lookup_serial.sv b/src/snitch_icache_lookup_serial.sv index 22748c8..fbdf7e6 100644 --- a/src/snitch_icache_lookup_serial.sv +++ b/src/snitch_icache_lookup_serial.sv @@ -324,23 +324,25 @@ module snitch_icache_lookup_serial // Fall-through buffer the read data: Store the read data if the SRAM bank accepted a request in // the previous cycle and if we actually have to buffer them because the receiver is not ready `FFL(data_rsp_q, proper_rdata, tag_handshake && !data_ready, '0, clk_i, rst_ni) - assign proper_rdata = refill_hit_q && !data_req_q.hit ? refill_wdata_q : data_rdata; + // Coincident refill write preempts the data read; serve refill data even on a tag hit. + assign proper_rdata = refill_hit_q ? refill_wdata_q : data_rdata; assign out_data_o = tag_handshake ? proper_rdata : data_rsp_q; // Check immediate refill for possible match assign refill_hit_d = write_valid_i && write_tag_i == required_tag && write_addr_i == data_req_d.addr[CFG.LINE_ALIGN+:CFG.COUNT_ALIGN]; + // Bind payload capture to the handshake so a later refill can't overwrite it during a stall. `FFL(refill_hit_q, refill_hit_d, tag_valid && tag_ready, '0, clk_i, rst_ni) - `FFL(refill_wdata_q, write_data_i, refill_hit_d, '0, clk_i, rst_ni) - `FFL(write_way_q, write_way_i, refill_hit_d, '0, clk_i, rst_ni) - `FFL(write_error_q, write_error_i, refill_hit_d, '0, clk_i, rst_ni) + `FFL(refill_wdata_q, write_data_i, refill_hit_d && tag_valid && tag_ready, '0, clk_i, rst_ni) + `FFL(write_way_q, write_way_i, refill_hit_d && tag_valid && tag_ready, '0, clk_i, rst_ni) + `FFL(write_error_q, write_error_i, refill_hit_d && tag_valid && tag_ready, '0, clk_i, rst_ni) // Generate the remaining output signals. assign out_addr_o = data_req_q.addr; assign out_id_o = data_req_q.id; - assign out_way_o = refill_hit_q && !data_req_q.hit ? write_way_q : data_req_q.cway; + assign out_way_o = refill_hit_q ? write_way_q : data_req_q.cway; assign out_hit_o = refill_hit_q || data_req_q.hit; - assign out_error_o = refill_hit_q && !data_req_q.hit ? write_error_q : data_req_q.error; + assign out_error_o = refill_hit_q ? write_error_q : data_req_q.error; assign out_valid_o = data_valid; assign data_ready = out_ready_i; From a9d0cb506a22be5be0b3092ce2c65b7d293e47ca Mon Sep 17 00:00:00 2001 From: Yinrong Li Date: Wed, 1 Jul 2026 17:41:55 +0200 Subject: [PATCH 2/3] Fix serial SCM tag config tie-off --- src/snitch_icache_lookup_serial.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/snitch_icache_lookup_serial.sv b/src/snitch_icache_lookup_serial.sv index fbdf7e6..4020730 100644 --- a/src/snitch_icache_lookup_serial.sv +++ b/src/snitch_icache_lookup_serial.sv @@ -163,9 +163,9 @@ module snitch_icache_lookup_serial .WriteAddr (tag_addr), .WriteData (tag_wdata) ); - assign sram_cfg_out_tag_o = '0; end + assign sram_cfg_out_tag_o = '0; end else begin : gen_sram logic [CFG.WAY_COUNT*(CFG.TAG_WIDTH+2)-1:0] tag_rdata_flat; for (genvar i = 0; i < CFG.WAY_COUNT; i++) begin : g_ways_rdata From 85d68d4dccedc49d405058eeeeed8afc5b6f77ad Mon Sep 17 00:00:00 2001 From: Yinrong Li Date: Wed, 1 Jul 2026 18:04:44 +0200 Subject: [PATCH 3/3] Fix unexpected fetch merge under address mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the MERGE_FETCHES path, prefetch_req_ready[i] also OR'd in the arbiter grant prefetch_req_ready_tmp[i], so a fetch port was acknowledged even when a different-address request won the shared lookup mux — its prefetch was consumed without ever being looked up. Gate the ready strictly on an address match with the accepted lookup request, so a port is acknowledged only when its own address is the one being served. --- src/snitch_icache.sv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/snitch_icache.sv b/src/snitch_icache.sv index c38d82c..4ff6b46 100644 --- a/src/snitch_icache.sv +++ b/src/snitch_icache.sv @@ -469,8 +469,8 @@ module snitch_icache if (MERGE_FETCHES) begin : gen_merge_fetches for (genvar i = 0; i < NR_FETCH_PORTS; i++) begin : gen_prefetch_req_ready - assign prefetch_req_ready[i] = prefetch_req_ready_tmp[i] | - (prefetch_lookup_req_ready & prefetch_req[i].addr == prefetch_lookup_req.addr); + assign prefetch_req_ready[i] = prefetch_lookup_req_ready & + (prefetch_req[i].addr == prefetch_lookup_req.addr); end always_comb begin