Skip to content

Commit fd6d2eb

Browse files
committed
Drain outstanding I$ traffic before completing a flush
A flush could invalidate the L0 while pre-flush refill or prefetch responses were still in flight. Those old responses could then be accepted after the flush and repopulate the cache with stale pre-flush state. Fix this by turning the shared I$ flush into a two-phase operation. Mark each L0 as flush-pending immediately so it stops accepting hits and issuing new refill or prefetch requests, then wait for the shared handler and all L0-side state to drain before firing the actual invalidate and completing the flush handshake. Relax the stalled-fetch assertions so a pending flush may legally withdraw a blocked fetch request.
1 parent 8264f6e commit fd6d2eb

5 files changed

Lines changed: 95 additions & 29 deletions

File tree

src/snitch_icache.sv

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,16 @@ module snitch_icache
228228
logic [NR_FETCH_PORTS-1:0] in_cache_ready, in_bypass_ready;
229229
logic [NR_FETCH_PORTS-1:0][FETCH_DW-1:0] in_cache_data, in_bypass_data;
230230
logic [NR_FETCH_PORTS-1:0] in_cache_error, in_bypass_error;
231+
logic [NR_FETCH_PORTS-1:0] l0_flush_ready;
232+
logic handler_idle;
233+
234+
logic flush_req;
235+
logic flush_busy_d, flush_busy_q;
236+
logic flush_shared_done_d, flush_shared_done_q;
237+
logic flush_wait_drop_d, flush_wait_drop_q;
238+
logic flush_l0_fire, flush_l0_pending;
239+
logic flush_req_valid_lookup, flush_req_ready_lookup, flush_fire_lookup;
240+
logic l0_flush_ready_all, shared_cache_idle;
231241

232242
for (genvar i = 0; i < NR_FETCH_PORTS; i++) begin : gen_prefetcher
233243
prefetch_req_t local_prefetch_req;
@@ -258,7 +268,9 @@ module snitch_icache
258268
) i_snitch_icache_l0 (
259269
.clk_i (clk_d2_i),
260270
.rst_ni,
261-
.flush_valid_i (flush_valid_i[i]),
271+
.flush_pending_i(flush_l0_pending),
272+
.flush_valid_i (flush_l0_fire),
273+
.flush_ready_o (l0_flush_ready[i]),
262274
.enable_prefetching_i,
263275
.enable_branch_pred_i,
264276
.icache_events_o(icache_l0_events_o[i]),
@@ -516,30 +528,62 @@ module snitch_icache
516528
logic write_valid;
517529
logic write_ready;
518530

519-
logic flush_valid, flush_ready;
520-
logic flush_valid_lookup, flush_ready_lookup;
531+
logic flush_ready_lookup_unused;
532+
533+
assign flush_req = |flush_valid_i;
534+
assign l0_flush_ready_all = &l0_flush_ready;
535+
// Hold the L0s in a "flush pending" state immediately so they stop creating new work while
536+
// the shared cache and any already-issued responses drain.
537+
assign flush_l0_pending = flush_busy_q | (flush_req & ~flush_wait_drop_q);
538+
// Only invalidate the L0s after the shared cache has flushed and every port has drained its
539+
// old responses, so no pre-flush traffic can repopulate an L0 afterwards.
540+
assign flush_l0_fire = flush_busy_q & flush_shared_done_q & l0_flush_ready_all;
541+
assign flush_ready_o = {CFG.NR_FETCH_PORTS{flush_l0_fire}};
542+
assign shared_cache_idle = ~(|prefetch_req_valid) & ~prefetch_lookup_req_valid &
543+
~lookup_valid & ~handler_req_valid & handler_idle;
544+
assign flush_fire_lookup = flush_req_valid_lookup & shared_cache_idle;
545+
546+
always_comb begin
547+
flush_busy_d = flush_busy_q;
548+
flush_shared_done_d = flush_shared_done_q;
549+
flush_wait_drop_d = flush_wait_drop_q;
550+
551+
if (flush_wait_drop_q) begin
552+
if (!flush_req) flush_wait_drop_d = 1'b0;
553+
end else if (!flush_busy_q) begin
554+
if (flush_req) begin
555+
flush_busy_d = 1'b1;
556+
flush_shared_done_d = 1'b0;
557+
end
558+
end else begin
559+
if (!flush_shared_done_q && flush_req_ready_lookup) flush_shared_done_d = 1'b1;
560+
if (flush_l0_fire) begin
561+
flush_busy_d = 1'b0;
562+
flush_wait_drop_d = 1'b1;
563+
end
564+
end
565+
end
521566

522-
assign flush_ready_o = {CFG.NR_FETCH_PORTS{flush_ready}};
523-
assign flush_valid = |flush_valid_i;
567+
`FF(flush_busy_q, flush_busy_d, '0, clk_d2_i, rst_ni)
568+
`FF(flush_shared_done_q, flush_shared_done_d, '0, clk_d2_i, rst_ni)
569+
`FF(flush_wait_drop_q, flush_wait_drop_d, '0, clk_d2_i, rst_ni)
524570

525-
// We need to propagate the handshake into the other
571+
// We need to propagate the shared-cache flush request into the other
526572
// clock domain in case we operate w/ different clocks.
527573
if (ISO_CROSSING) begin : gen_flush_crossing
528-
isochronous_spill_register i_isochronous_4phase_handshake (
574+
isochronous_4phase_handshake i_isochronous_4phase_handshake (
529575
.src_clk_i (clk_d2_i),
530576
.src_rst_ni (rst_ni),
531-
.src_valid_i(flush_valid),
532-
.src_ready_o(flush_ready),
533-
.src_data_i ('0),
577+
.src_valid_i(flush_busy_q & ~flush_shared_done_q),
578+
.src_ready_o(flush_req_ready_lookup),
534579
.dst_clk_i (clk_i),
535580
.dst_rst_ni (rst_ni),
536-
.dst_valid_o(flush_valid_lookup),
537-
.dst_ready_i(flush_ready_lookup),
538-
.dst_data_o ( /* Unused */)
581+
.dst_valid_o(flush_req_valid_lookup),
582+
.dst_ready_i(shared_cache_idle)
539583
);
540584
end else begin : gen_no_flush_crossing
541-
assign flush_valid_lookup = flush_valid;
542-
assign flush_ready = flush_ready_lookup;
585+
assign flush_req_valid_lookup = flush_busy_q & ~flush_shared_done_q;
586+
assign flush_req_ready_lookup = shared_cache_idle;
543587
end
544588

545589
if (SERIAL_LOOKUP) begin : gen_serial_lookup
@@ -553,8 +597,8 @@ module snitch_icache
553597
.clk_i,
554598
.rst_ni,
555599

556-
.flush_valid_i (flush_valid_lookup),
557-
.flush_ready_o (flush_ready_lookup),
600+
.flush_valid_i (flush_fire_lookup),
601+
.flush_ready_o (flush_ready_lookup_unused),
558602
.icache_events_o(icache_l1_events_o),
559603

560604
.in_addr_i (prefetch_lookup_req.addr),
@@ -596,8 +640,8 @@ module snitch_icache
596640
.clk_i,
597641
.rst_ni,
598642

599-
.flush_valid_i (flush_valid_lookup),
600-
.flush_ready_o (flush_ready_lookup),
643+
.flush_valid_i (flush_fire_lookup),
644+
.flush_ready_o (flush_ready_lookup_unused),
601645
.icache_events_o(icache_l1_events_o),
602646

603647
.in_addr_i (prefetch_lookup_req.addr),
@@ -651,6 +695,7 @@ module snitch_icache
651695
.in_rsp_id_o (prefetch_lookup_rsp.id),
652696
.in_rsp_valid_o(prefetch_lookup_rsp_valid),
653697
.in_rsp_ready_i(prefetch_lookup_rsp_ready),
698+
.idle_o (handler_idle),
654699

655700
.write_addr_o (write_addr),
656701
.write_way_o (write_way),

src/snitch_icache_handler.sv

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ module snitch_icache_handler
2929
output logic in_rsp_valid_o,
3030
input logic in_rsp_ready_i,
3131

32+
output logic idle_o,
33+
3234
output logic [CFG.COUNT_ALIGN-1:0] write_addr_o,
3335
output logic [ CFG.WAY_ALIGN-1:0] write_way_o,
3436
output logic [ CFG.LINE_WIDTH-1:0] write_data_o,
@@ -350,4 +352,11 @@ module snitch_icache_handler
350352
end
351353
end
352354

355+
always_comb begin
356+
idle_o = ~arb_q.lock & ~write_served_q & ~in_rsp_served_q;
357+
for (int i = 0; i < CFG.PENDING_COUNT; i++) begin
358+
idle_o &= ~pending_q[i].valid;
359+
end
360+
end
361+
353362
endmodule

src/snitch_icache_l0.sv

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ module snitch_icache_l0
1717
) (
1818
input logic clk_i,
1919
input logic rst_ni,
20+
input logic flush_pending_i,
2021
input logic flush_valid_i,
22+
output logic flush_ready_o,
2123

2224
input logic enable_prefetching_i,
2325
input logic enable_branch_pred_i,
@@ -104,6 +106,9 @@ module snitch_icache_l0
104106
logic refill_ready, prefetch_ready;
105107
req_t out_req;
106108

109+
// The local flush can fire once no queued request or in-flight L0 response remains.
110+
assign flush_ready_o = ~pending_refill_q & ~pending_prefetch_q & ~prefetch_req_vld_q;
111+
107112
assign evict_because_miss = miss & ~last_cycle_was_miss_q;
108113
assign evict_because_prefetch = latch_prefetch & ~last_cycle_was_prefetch_q;
109114
assign incoming_rsp_is_prefetch = (out_rsp_id_i == (1'b1 << {L0_ID, 1'b1}));
@@ -191,7 +196,7 @@ module snitch_icache_l0
191196
// HIT
192197
// ----
193198
// we hit in the cache and there was a unique hit.
194-
assign in_ready_o = hit_any;
199+
assign in_ready_o = hit_any & ~flush_pending_i;
195200

196201
logic [minwidth(CFG.LINE_WIDTH,32)-1:0] ins_data;
197202
always_comb begin : data_muxer
@@ -258,7 +263,7 @@ module snitch_icache_l0
258263
// -------------
259264
assign refill.addr = addr_tag << CFG.LINE_ALIGN;
260265
assign refill.is_prefetch = 1'b0;
261-
assign refill_valid = miss & ~pending_prefetch_q;
266+
assign refill_valid = miss & ~pending_prefetch_q & ~flush_pending_i;
262267

263268
`FFL(pending_line_refill_q, evict_strb, evict_because_miss, '0, clk_i, rst_ni)
264269
`FFL(pending_line_prefetch_q, evict_strb, evict_because_prefetch, '0, clk_i, rst_ni)
@@ -324,7 +329,7 @@ module snitch_icache_l0
324329
// -------------
325330
// Generate a prefetch request if the cache hits and we haven't
326331
// pre-fetched the line yet and there is no other refill in progress.
327-
assign prefetcher_out.vld = enable_prefetching_i &
332+
assign prefetcher_out.vld = enable_prefetching_i & ~flush_pending_i &
328333
hit_any & ~hit_prefetch_any &
329334
hit_early_is_onehot & ~pending_prefetch_q;
330335

@@ -431,18 +436,22 @@ module snitch_icache_l0
431436
prefetch_req_vld_d = prefetch_req_vld_q;
432437
prefetch_req_addr_d = prefetch_req_addr_q;
433438

434-
if (prefetch_ready) prefetch_req_vld_d = 1'b0;
439+
if (flush_pending_i) begin
440+
prefetch_req_vld_d = 1'b0;
441+
end else begin
442+
if (prefetch_ready) prefetch_req_vld_d = 1'b0;
435443

436-
if (latch_prefetch) begin
437-
prefetch_req_vld_d = 1'b1;
438-
prefetch_req_addr_d = prefetcher_out.addr;
444+
if (latch_prefetch) begin
445+
prefetch_req_vld_d = 1'b1;
446+
prefetch_req_addr_d = prefetcher_out.addr;
447+
end
439448
end
440449
end
441450

442451
assign addr_tag_prefetch_req = CFG.L0_TAG_WIDTH'(prefetch_req_addr_q >> CFG.LINE_ALIGN);
443452
assign prefetch.is_prefetch = 1'b1;
444453
assign prefetch.addr = prefetch_req_addr_q;
445-
assign prefetch_valid = prefetch_req_vld_q;
454+
assign prefetch_valid = prefetch_req_vld_q & ~flush_pending_i;
446455

447456
`FF(prefetch_req_vld_q, prefetch_req_vld_d, '0)
448457
`FF(prefetch_req_addr_q, prefetch_req_addr_d, '0)
@@ -466,8 +475,8 @@ module snitch_icache_l0
466475
// make sure only one signal is high and the conditions are mutual exclusive
467476
`ASSERT(ExclusiveEvict, $onehot0({evict_because_miss, evict_because_prefetch}))
468477
// request must be stable
469-
`ASSERT(InstReqStable, in_valid_i && !in_ready_o |=> in_valid_i)
470-
`ASSERT(InstReqDataStable, in_valid_i && !in_ready_o |=> $stable(in_addr_i))
478+
`ASSERT(InstReqStable, in_valid_i && !in_ready_o |=> (in_valid_i || flush_pending_i))
479+
`ASSERT(InstReqDataStable, in_valid_i && !in_ready_o |=> ($stable(in_addr_i) || flush_pending_i))
471480

472481
`ASSERT(RefillReqStable, out_req_valid_o && !out_req_ready_i |=> out_req_valid_o)
473482
`ASSERT(RefillReqDataStable, out_req_valid_o && !out_req_ready_i |=> $stable(out_req_addr_o)

src/snitch_read_only_cache.sv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ module snitch_read_only_cache
393393
.in_rsp_id_o (in_rsp_id),
394394
.in_rsp_valid_o(in_rsp_valid),
395395
.in_rsp_ready_i(in_rsp_ready),
396+
.idle_o (),
396397

397398
.write_addr_o (write_addr),
398399
.write_way_o (write_way),

test/snitch_icache_l0_tb.sv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ module snitch_icache_l0_tb #(
151151
) dut (
152152
.clk_i (clk),
153153
.rst_ni (~rst),
154+
.flush_pending_i (dut_flush_valid),
155+
.flush_ready_o (),
154156
.enable_prefetching_i(1'b1),
155157
.enable_branch_pred_i(1'b1),
156158
.icache_events_o (),

0 commit comments

Comments
 (0)