From a81ed71eec1c863bff636046feabf69542a55fc2 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Wed, 21 Feb 2024 12:03:36 +0100 Subject: [PATCH 1/3] Add PLRU eviction to L1 Current addition is directly with FFs in the handler, not re-using tag storage. --- src/snitch_icache.sv | 1 + src/snitch_icache_handler.sv | 65 ++++++++++++++++++++++++++++------- src/snitch_icache_pkg.sv | 1 + src/snitch_read_only_cache.sv | 1 + test/snitch_icache_l0_tb.sv | 1 + 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/snitch_icache.sv b/src/snitch_icache.sv index f09966e..9764c99 100644 --- a/src/snitch_icache.sv +++ b/src/snitch_icache.sv @@ -99,6 +99,7 @@ module snitch_icache import snitch_icache_pkg::*; #( EARLY_LATCH: EARLY_LATCH, BUFFER_LOOKUP: 0, GUARANTEE_ORDERING: 0, + L1_PLRU: 1, FETCH_ALIGN: $clog2(FETCH_DW/8), FILL_ALIGN: $clog2(FILL_DW/8), diff --git a/src/snitch_icache_handler.sv b/src/snitch_icache_handler.sv index 243558b..c609f19 100644 --- a/src/snitch_icache_handler.sv +++ b/src/snitch_icache_handler.sv @@ -242,21 +242,62 @@ module snitch_icache_handler #( end end - // The cache line eviction LFSR is responsible for picking a cache line for - // replacement at random. Note that we assume that the entire cache is full, - // so no empty cache lines are available. This is the common case since we - // do not support flushing of the cache. logic [CFG.WAY_ALIGN-1:0] evict_index; logic evict_enable; - snitch_icache_lfsr #( - .N ( CFG.WAY_ALIGN) - ) i_evict_lfsr ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .value_o ( evict_index ), - .enable_i ( evict_enable ) - ); + if ( CFG.L1_PLRU ) begin : gen_plru + + logic [CFG.LINE_COUNT-1:0][CFG.WAY_COUNT-1:0] used_masks; + logic [CFG.LINE_COUNT-1:0][CFG.WAY_COUNT-1:0] evict_masks; + + always_comb begin + used_masks = '0; + if (in_req_valid_i && in_req_hit_i) begin + // hit update + used_masks[in_req_addr_i >> CFG.LINE_ALIGN][in_req_way_i] = 1'b1; + end else if (write_valid_o) begin + // refill update + used_masks[write_addr_o][write_way_o] = 1'b1; + end + end + + for (genvar i = 0; i < CFG.LINE_COUNT; i++) begin : gen_plru_tree + + plru_tree #( + .ENTRIES ( CFG.WAY_COUNT ) + ) i_plru_tree ( + .clk_i, + .rst_ni, + + .used_i ( used_masks [i] ), + .plru_o ( evict_masks[i] ) + ); + + end + + onehot_to_bin #( + .ONEHOT_WIDTH ( CFG.WAY_COUNT ) + ) i_evict_mask_to_index ( + .onehot ( evict_masks[write_addr_o] ), + .bin ( evict_index ) + ); + + end else begin : gen_lfsr + + // The cache line eviction LFSR is responsible for picking a cache line for + // replacement at random. Note that we assume that the entire cache is full, + // so no empty cache lines are available. This is the common case since we + // do not support flushing of the cache. + snitch_icache_lfsr #( + .N (CFG.WAY_ALIGN) + ) i_evict_lfsr ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .value_o ( evict_index ), + .enable_i ( evict_enable ) + ); + + end // The response handler deals with incoming refill responses. It queries and // clears the corresponding entry in the pending table, stores the data in diff --git a/src/snitch_icache_pkg.sv b/src/snitch_icache_pkg.sv index a69e093..cb5bc10 100644 --- a/src/snitch_icache_pkg.sv +++ b/src/snitch_icache_pkg.sv @@ -37,6 +37,7 @@ package snitch_icache_pkg; bit EARLY_LATCH; bit BUFFER_LOOKUP; bit GUARANTEE_ORDERING; + bit L1_PLRU; // Derived values. int unsigned FETCH_ALIGN; diff --git a/src/snitch_read_only_cache.sv b/src/snitch_read_only_cache.sv index 5fef14c..13d1f75 100644 --- a/src/snitch_read_only_cache.sv +++ b/src/snitch_read_only_cache.sv @@ -205,6 +205,7 @@ module snitch_read_only_cache import snitch_icache_pkg::*; #( EARLY_LATCH: 0, // Unused here BUFFER_LOOKUP: 1, // Mandatory here GUARANTEE_ORDERING: 1, // Mandatory here + L1_PLRU: 1, FETCH_ALIGN: $clog2(AxiDataWidth/8), FILL_ALIGN: $clog2(AxiDataWidth/8), diff --git a/test/snitch_icache_l0_tb.sv b/test/snitch_icache_l0_tb.sv index 9ca35e3..920d44d 100644 --- a/test/snitch_icache_l0_tb.sv +++ b/test/snitch_icache_l0_tb.sv @@ -91,6 +91,7 @@ module snitch_icache_l0_tb #( EARLY_LATCH: EARLY_LATCH, BUFFER_LOOKUP: BUFFER_LOOKUP, GUARANTEE_ORDERING: GUARANTEE_ORDERING, + L1_PLRU: 1'b1, FETCH_ALIGN: $clog2(FETCH_DW/8), FILL_ALIGN: $clog2(FILL_DW/8), From c18d25f7a0fec56e6964a0e712a3e7905e664bdd Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 20 Feb 2024 19:48:55 +0100 Subject: [PATCH 2/3] Add plru tree to L0 --- src/snitch_icache.sv | 1 + src/snitch_icache_l0.sv | 53 +++++++++++++++++++++++++---------- src/snitch_icache_pkg.sv | 1 + src/snitch_read_only_cache.sv | 1 + test/snitch_icache_l0_tb.sv | 1 + 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/snitch_icache.sv b/src/snitch_icache.sv index 9764c99..ae4f2be 100644 --- a/src/snitch_icache.sv +++ b/src/snitch_icache.sv @@ -99,6 +99,7 @@ module snitch_icache import snitch_icache_pkg::*; #( EARLY_LATCH: EARLY_LATCH, BUFFER_LOOKUP: 0, GUARANTEE_ORDERING: 0, + L0_PLRU: 1, L1_PLRU: 1, FETCH_ALIGN: $clog2(FETCH_DW/8), diff --git a/src/snitch_icache_l0.sv b/src/snitch_icache_l0.sv index 201df56..e9a20a6 100644 --- a/src/snitch_icache_l0.sv +++ b/src/snitch_icache_l0.sv @@ -201,21 +201,46 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // ------- // Evictor // ------- - logic [$clog2(CFG.L0_LINE_COUNT)-1:0] cnt_d, cnt_q; - - always_comb begin : evictor - evict_strb = '0; - cnt_d = cnt_q; - - // Round-Robin - if (evict_req) begin - evict_strb = 1 << cnt_q; - cnt_d = cnt_q + 1; - if (evict_strb == hit_early) begin - evict_strb = 1 << cnt_d; - cnt_d = cnt_q + 2; + + if (CFG.L0_PLRU) begin : gen_plru + + logic [CFG.L0_LINE_COUNT-1:0] hit_plru; + logic [CFG.L0_LINE_COUNT-1:0] evict_plru; + + // Update plru on hit and on miss eviction, prefetch only once fetch hits + assign hit_plru = hit | (evict_because_miss ? evict_strb : '0); + assign evict_strb = evict_req ? evict_plru : '0; + + plru_tree #( + .ENTRIES(CFG.L0_LINE_COUNT) + ) i_plru_tree ( + .clk_i, + .rst_ni, + .used_i ( hit_plru ), + .plru_o ( evict_plru ) + ); + + end else begin : gen_round_robin + + logic [$clog2(CFG.L0_LINE_COUNT)-1:0] cnt_d, cnt_q; + + always_comb begin : evictor + evict_strb = '0; + cnt_d = cnt_q; + + // Round-Robin + if (evict_req) begin + evict_strb = 1 << cnt_q; + cnt_d = cnt_q + 1; + if (evict_strb == hit_early) begin + evict_strb = 1 << cnt_d; + cnt_d = cnt_q + 2; + end end end + + `FF(cnt_q, cnt_d, '0) + end always_comb begin : flush @@ -230,8 +255,6 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( if (flush_valid_i) flush_strb = '1; end - `FF(cnt_q, cnt_d, '0) - // ------------- // Miss Handling // ------------- diff --git a/src/snitch_icache_pkg.sv b/src/snitch_icache_pkg.sv index cb5bc10..ec931e2 100644 --- a/src/snitch_icache_pkg.sv +++ b/src/snitch_icache_pkg.sv @@ -37,6 +37,7 @@ package snitch_icache_pkg; bit EARLY_LATCH; bit BUFFER_LOOKUP; bit GUARANTEE_ORDERING; + bit L0_PLRU; bit L1_PLRU; // Derived values. diff --git a/src/snitch_read_only_cache.sv b/src/snitch_read_only_cache.sv index 13d1f75..fb87aa1 100644 --- a/src/snitch_read_only_cache.sv +++ b/src/snitch_read_only_cache.sv @@ -205,6 +205,7 @@ module snitch_read_only_cache import snitch_icache_pkg::*; #( EARLY_LATCH: 0, // Unused here BUFFER_LOOKUP: 1, // Mandatory here GUARANTEE_ORDERING: 1, // Mandatory here + L0_PLRU: 0, // Unused here L1_PLRU: 1, FETCH_ALIGN: $clog2(AxiDataWidth/8), diff --git a/test/snitch_icache_l0_tb.sv b/test/snitch_icache_l0_tb.sv index 920d44d..6962a4c 100644 --- a/test/snitch_icache_l0_tb.sv +++ b/test/snitch_icache_l0_tb.sv @@ -91,6 +91,7 @@ module snitch_icache_l0_tb #( EARLY_LATCH: EARLY_LATCH, BUFFER_LOOKUP: BUFFER_LOOKUP, GUARANTEE_ORDERING: GUARANTEE_ORDERING, + L0_PLRU: 1'b1, L1_PLRU: 1'b1, FETCH_ALIGN: $clog2(FETCH_DW/8), From e05767469852035b8b967fa0713766c273101b1c Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Tue, 30 Jul 2024 18:33:58 +0200 Subject: [PATCH 3/3] Ensure L0 prefetch doesn't evict currently used line --- src/snitch_icache_l0.sv | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/snitch_icache_l0.sv b/src/snitch_icache_l0.sv index e9a20a6..7c938c3 100644 --- a/src/snitch_icache_l0.sv +++ b/src/snitch_icache_l0.sv @@ -58,6 +58,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( logic [CFG.L0_LINE_COUNT-1:0] evict_strb; logic [CFG.L0_LINE_COUNT-1:0] flush_strb; logic [CFG.L0_LINE_COUNT-1:0] validate_strb; + logic [CFG.L0_LINE_COUNT-1:0] next_evict; typedef struct packed { logic vld; @@ -210,6 +211,7 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // Update plru on hit and on miss eviction, prefetch only once fetch hits assign hit_plru = hit | (evict_because_miss ? evict_strb : '0); assign evict_strb = evict_req ? evict_plru : '0; + assign next_evict = evict_plru; plru_tree #( .ENTRIES(CFG.L0_LINE_COUNT) @@ -222,6 +224,8 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( end else begin : gen_round_robin + assign next_evict = '0; + logic [$clog2(CFG.L0_LINE_COUNT)-1:0] cnt_d, cnt_q; always_comb begin : evictor @@ -310,10 +314,12 @@ module snitch_icache_l0 import snitch_icache_pkg::*; #( // Pre-fetching // ------------- // Generate a prefetch request if the cache hits and we haven't - // pre-fetched the line yet and there is no other refill in progress. + // pre-fetched the line yet and there is no other refill in progress + // and the current hit won't be evicted. assign prefetcher_out.vld = enable_prefetching_i & hit_any & ~hit_prefetch_any & - hit_early_is_onehot & ~pending_refill_q; + hit_early_is_onehot & ~pending_refill_q & + ~|(next_evict & hit_early); localparam int unsigned FetchPkts = CFG.LINE_WIDTH/32; logic [FetchPkts-1:0] is_branch_taken;