// Copyright © 2019-2023 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. `include "VX_cache_define.vh" // this is an implementation of a pipelined multi-banked cache // we allocate a free slot from the MSHR before processing a core request // and release the slot when we get a cache hit. // during a memory fill response we initiate the replay sequence // and dequeue all associated pending entries. // Warning: This MSHR implementation is strongly coupled with the bank pipeline // and as such changes to either module requires careful evaluation. // This implementation makes the following assumptions: // (1) two-cycle pipeline: st0 and st1. // (2) core request flow: st0: allocate / lookup, st1: finalize. // (3) the first dequeue after the fill should happen in st0, when the fill is in st1 // this is enforced inside the bank by "rdw_hazard_st0". module VX_cache_mshr #( parameter `STRING INSTANCE_ID= "", parameter BANK_ID = 0, // Size of line inside a bank in bytes parameter LINE_SIZE = 16, // Number of banks parameter NUM_BANKS = 1, // Miss Reserv Queue Knob parameter MSHR_SIZE = 4, // Request debug identifier parameter UUID_WIDTH = 0, // MSHR parameters parameter DATA_WIDTH = 1, parameter MSHR_ADDR_WIDTH = `LOG2UP(MSHR_SIZE) ) ( input wire clk, input wire reset, `IGNORE_UNUSED_BEGIN input wire[`UP(UUID_WIDTH)-1:0] deq_req_uuid, input wire[`UP(UUID_WIDTH)-1:0] lkp_req_uuid, input wire[`UP(UUID_WIDTH)-1:0] fin_req_uuid, `IGNORE_UNUSED_END // allocate input wire allocate_valid, input wire [`CS_LINE_ADDR_WIDTH-1:0] allocate_addr, input wire allocate_rw, input wire [DATA_WIDTH-1:0] allocate_data, output wire [MSHR_ADDR_WIDTH-1:0] allocate_id, output wire [MSHR_ADDR_WIDTH-1:0] allocate_tail, output wire allocate_ready, // lookup input wire lookup_valid, input wire [`CS_LINE_ADDR_WIDTH-1:0] lookup_addr, output wire [MSHR_SIZE-1:0] lookup_matches, // memory fill input wire fill_valid, input wire [MSHR_ADDR_WIDTH-1:0] fill_id, output wire [`CS_LINE_ADDR_WIDTH-1:0] fill_addr, // dequeue output wire dequeue_valid, output wire [`CS_LINE_ADDR_WIDTH-1:0] dequeue_addr, output wire dequeue_rw, output wire [DATA_WIDTH-1:0] dequeue_data, output wire [MSHR_ADDR_WIDTH-1:0] dequeue_id, input wire dequeue_ready, // finalize input wire finalize_valid, input wire finalize_release, input wire finalize_pending, input wire [MSHR_ADDR_WIDTH-1:0] finalize_id, input wire [MSHR_ADDR_WIDTH-1:0] finalize_tail ); `UNUSED_PARAM (BANK_ID) reg [`CS_LINE_ADDR_WIDTH-1:0] addr_table [MSHR_SIZE-1:0]; reg [MSHR_ADDR_WIDTH-1:0] next_index [MSHR_SIZE-1:0]; reg [MSHR_SIZE-1:0] valid_table, valid_table_n; reg [MSHR_SIZE-1:0] next_table, next_table_x, next_table_n; reg [MSHR_SIZE-1:0] write_table; reg allocate_rdy, allocate_rdy_n; reg [MSHR_ADDR_WIDTH-1:0] allocate_id_r, allocate_id_n; reg dequeue_val, dequeue_val_n; reg [MSHR_ADDR_WIDTH-1:0] dequeue_id_r, dequeue_id_n; wire [MSHR_ADDR_WIDTH-1:0] tail_idx; wire allocate_fire = allocate_valid && allocate_ready; wire dequeue_fire = dequeue_valid && dequeue_ready; wire [MSHR_SIZE-1:0] addr_matches; for (genvar i = 0; i < MSHR_SIZE; ++i) begin assign addr_matches[i] = valid_table[i] && (addr_table[i] == lookup_addr); end VX_lzc #( .N (MSHR_SIZE), .REVERSE (1) ) allocate_sel ( .data_in (~valid_table_n), .data_out (allocate_id_n), .valid_out (allocate_rdy_n) ); VX_onehot_encoder #( .N (MSHR_SIZE) ) tail_sel ( .data_in (addr_matches & ~next_table_x), .data_out (tail_idx), `UNUSED_PIN (valid_out) ); always @(*) begin valid_table_n = valid_table; next_table_x = next_table; dequeue_val_n = dequeue_val; dequeue_id_n = dequeue_id; if (fill_valid) begin dequeue_val_n = 1; dequeue_id_n = fill_id; end if (dequeue_fire) begin valid_table_n[dequeue_id] = 0; if (next_table[dequeue_id]) begin dequeue_id_n = next_index[dequeue_id]; end else begin dequeue_val_n = 0; end end if (finalize_valid) begin if (finalize_release) begin valid_table_n[finalize_id] = 0; end if (finalize_pending) begin next_table_x[finalize_tail] = 1; end end next_table_n = next_table_x; if (allocate_fire) begin valid_table_n[allocate_id] = 1; next_table_n[allocate_id] = 0; end end always @(posedge clk) begin if (reset) begin valid_table <= '0; allocate_rdy <= 0; dequeue_val <= 0; end else begin valid_table <= valid_table_n; allocate_rdy <= allocate_rdy_n; dequeue_val <= dequeue_val_n; end if (allocate_fire) begin addr_table[allocate_id] <= allocate_addr; write_table[allocate_id] <= allocate_rw; end if (finalize_valid && finalize_pending) begin next_index[finalize_tail] <= finalize_id; end dequeue_id_r <= dequeue_id_n; allocate_id_r <= allocate_id_n; next_table <= next_table_n; end `RUNTIME_ASSERT((~allocate_fire || ~valid_table[allocate_id_r]), ("%t: *** %s-bank%0d inuse allocation: addr=0x%0h, id=%0d (#%0d)", $time, INSTANCE_ID, BANK_ID, `CS_LINE_TO_FULL_ADDR(allocate_addr, BANK_ID), allocate_id_r, lkp_req_uuid)) `RUNTIME_ASSERT((~finalize_valid || valid_table[finalize_id]), ("%t: *** %s-bank%0d invalid release: addr=0x%0h, id=%0d (#%0d)", $time, INSTANCE_ID, BANK_ID, `CS_LINE_TO_FULL_ADDR(addr_table[finalize_id], BANK_ID), finalize_id, fin_req_uuid)) `RUNTIME_ASSERT((~fill_valid || valid_table[fill_id]), ("%t: *** %s-bank%0d invalid fill: addr=0x%0h, id=%0d", $time, INSTANCE_ID, BANK_ID, `CS_LINE_TO_FULL_ADDR(addr_table[fill_id], BANK_ID), fill_id)) VX_dp_ram #( .DATAW (DATA_WIDTH), .SIZE (MSHR_SIZE), .LUTRAM (1) ) entries ( .clk (clk), .read (1'b1), .write (allocate_valid), `UNUSED_PIN (wren), .waddr (allocate_id_r), .wdata (allocate_data), .raddr (dequeue_id_r), .rdata (dequeue_data) ); assign fill_addr = addr_table[fill_id]; assign allocate_ready = allocate_rdy; assign allocate_id = allocate_id_r; assign allocate_tail = tail_idx; assign dequeue_valid = dequeue_val; assign dequeue_addr = addr_table[dequeue_id_r]; assign dequeue_rw = write_table[dequeue_id_r]; assign dequeue_id = dequeue_id_r; assign lookup_matches = addr_matches & ~write_table; `UNUSED_VAR (lookup_valid) `ifdef DBG_TRACE_CACHE_MSHR reg show_table; always @(posedge clk) begin if (reset) begin show_table <= 0; end else begin show_table <= allocate_fire || lookup_valid || finalize_valid || fill_valid || dequeue_fire; end if (allocate_fire) `TRACE(3, ("%d: %s-bank%0d mshr-allocate: addr=0x%0h, tail=%0d, id=%0d (#%0d)\n", $time, INSTANCE_ID, BANK_ID, `CS_LINE_TO_FULL_ADDR(allocate_addr, BANK_ID), allocate_tail, allocate_id, lkp_req_uuid)); if (lookup_valid) `TRACE(3, ("%d: %s-bank%0d mshr-lookup: addr=0x%0h, matches=%b (#%0d)\n", $time, INSTANCE_ID, BANK_ID, `CS_LINE_TO_FULL_ADDR(lookup_addr, BANK_ID), lookup_matches, lkp_req_uuid)); if (finalize_valid) `TRACE(3, ("%d: %s-bank%0d mshr-finalize release=%b, pending=%b, tail=%0d, id=%0d (#%0d)\n", $time, INSTANCE_ID, BANK_ID, finalize_release, finalize_pending, finalize_tail, finalize_id, fin_req_uuid)); if (fill_valid) `TRACE(3, ("%d: %s-bank%0d mshr-fill: addr=0x%0h, addr=0x%0h, id=%0d\n", $time, INSTANCE_ID, BANK_ID, `CS_LINE_TO_FULL_ADDR(addr_table[fill_id], BANK_ID), `CS_LINE_TO_FULL_ADDR(fill_addr, BANK_ID), fill_id)); if (dequeue_fire) `TRACE(3, ("%d: %s-bank%0d mshr-dequeue: addr=0x%0h, id=%0d (#%0d)\n", $time, INSTANCE_ID, BANK_ID, `CS_LINE_TO_FULL_ADDR(dequeue_addr, BANK_ID), dequeue_id_r, deq_req_uuid)); if (show_table) begin `TRACE(3, ("%d: %s-bank%0d mshr-table", $time, INSTANCE_ID, BANK_ID)); for (integer i = 0; i < MSHR_SIZE; ++i) begin if (valid_table[i]) begin `TRACE(3, (" %0d=0x%0h", i, `CS_LINE_TO_FULL_ADDR(addr_table[i], BANK_ID))); if (write_table[i]) `TRACE(3, ("(w)")); else `TRACE(3, ("(r)")); if (next_table[i]) `TRACE(3, ("->%0d", next_index[i])); end end `TRACE(3, ("\n")); end end `endif endmodule