mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-23 05:37:16 -04:00
cache_subsystem: merge icaches
- add wrapper module to connect wt_icache to AXI bus - replace std_icache by cva6_icache_axi_wrapper - rename wt_icache to cva6_icache Signed-off-by: Nils Wistoff <nwistoff@iis.ee.ethz.ch>
This commit is contained in:
parent
da363791d0
commit
de5077332e
10 changed files with 203 additions and 472 deletions
|
@ -94,11 +94,11 @@ sources:
|
|||
- src/cache_subsystem/wt_dcache_ctrl.sv
|
||||
- src/cache_subsystem/wt_cache_subsystem.sv
|
||||
- src/cache_subsystem/wt_dcache_missunit.sv
|
||||
- src/cache_subsystem/wt_icache.sv
|
||||
- src/cache_subsystem/std_icache.sv
|
||||
- src/cache_subsystem/cva6_icache.sv
|
||||
- src/cache_subsystem/wt_dcache_wbuffer.sv
|
||||
- src/cache_subsystem/wt_l15_adapter.sv
|
||||
- src/cache_subsystem/wt_dcache_mem.sv
|
||||
- src/cache_subsystem/cva6_icache_axi_wrapper.sv
|
||||
- src/cache_subsystem/std_cache_subsystem.sv
|
||||
- src/cache_subsystem/wt_dcache.sv
|
||||
- src/clint/axi_lite_interface.sv
|
||||
|
|
|
@ -118,7 +118,7 @@ src/cache_subsystem/wt_dcache_mem.sv
|
|||
src/cache_subsystem/wt_dcache_missunit.sv
|
||||
src/cache_subsystem/wt_dcache_wbuffer.sv
|
||||
src/cache_subsystem/wt_dcache.sv
|
||||
src/cache_subsystem/wt_icache.sv
|
||||
src/cache_subsystem/cva6_icache.sv
|
||||
src/cache_subsystem/wt_l15_adapter.sv
|
||||
src/cache_subsystem/wt_cache_subsystem.sv
|
||||
src/clint/clint.sv
|
||||
|
|
|
@ -653,7 +653,7 @@ module ariane import ariane_pkg::*; #(
|
|||
// note: this only works with one cacheable region
|
||||
// not as important since this cache subsystem is about to be
|
||||
// deprecated
|
||||
.CACHE_START_ADDR ( ArianeCfg.CachedRegionAddrBase )
|
||||
.ArianeCfg ( ArianeCfg )
|
||||
) i_cache_subsystem (
|
||||
// to D$
|
||||
.clk_i ( clk_i ),
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
//
|
||||
|
||||
|
||||
module wt_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
|
||||
module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
|
||||
parameter logic [CACHE_ID_WIDTH-1:0] RdTxId = 0, // ID to be used for read transactions
|
||||
parameter ariane_pkg::ariane_cfg_t ArianeCfg = ariane_pkg::ArianeDefaultConfig // contains cacheable regions
|
||||
) (
|
||||
|
@ -533,4 +533,4 @@ end else begin : gen_piton_offset
|
|||
`endif
|
||||
//pragma translate_on
|
||||
|
||||
endmodule // wt_icache
|
||||
endmodule // cva6_icache
|
188
src/cache_subsystem/cva6_icache_axi_wrapper.sv
Normal file
188
src/cache_subsystem/cva6_icache_axi_wrapper.sv
Normal file
|
@ -0,0 +1,188 @@
|
|||
// Copyright 2018 ETH Zurich and University of Bologna.
|
||||
// Copyright and related rights are licensed under the Solderpad Hardware
|
||||
// License, Version 0.51 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License at
|
||||
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
||||
// or agreed to in writing, software, hardware and materials distributed under
|
||||
// this 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.
|
||||
//
|
||||
// Author: Nils Wistoff <nwistoff@iis.ee.ethz.ch>, ETH Zurich
|
||||
// Date: 07.09.2020
|
||||
// Description: wrapper module to connect the L1I$ to a 64bit AXI bus.
|
||||
//
|
||||
|
||||
module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
|
||||
parameter ariane_cfg_t ArianeCfg = ArianeDefaultConfig // contains cacheable regions
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input riscv::priv_lvl_t priv_lvl_i,
|
||||
|
||||
input logic flush_i, // flush the icache, flush and kill have to be asserted together
|
||||
input logic en_i, // enable icache
|
||||
output logic miss_o, // to performance counter
|
||||
// address translation requests
|
||||
input icache_areq_i_t areq_i,
|
||||
output icache_areq_o_t areq_o,
|
||||
// data requests
|
||||
input icache_dreq_i_t dreq_i,
|
||||
output icache_dreq_o_t dreq_o,
|
||||
// AXI refill port
|
||||
output ariane_axi::req_t axi_req_o,
|
||||
input ariane_axi::resp_t axi_resp_i
|
||||
);
|
||||
|
||||
localparam AxiNumWords = (ICACHE_LINE_WIDTH/64) * (ICACHE_LINE_WIDTH > DCACHE_LINE_WIDTH) +
|
||||
(DCACHE_LINE_WIDTH/64) * (ICACHE_LINE_WIDTH <= DCACHE_LINE_WIDTH) ;
|
||||
|
||||
logic icache_mem_rtrn_vld;
|
||||
icache_rtrn_t icache_mem_rtrn;
|
||||
logic icache_mem_data_req;
|
||||
logic icache_mem_data_ack;
|
||||
icache_req_t icache_mem_data;
|
||||
|
||||
logic axi_rd_req;
|
||||
logic axi_rd_gnt;
|
||||
logic [63:0] axi_rd_addr;
|
||||
logic [$clog2(AxiNumWords)-1:0] axi_rd_blen;
|
||||
logic [1:0] axi_rd_size;
|
||||
logic [$size(axi_resp_i.r.id)-1:0] axi_rd_id_in;
|
||||
logic axi_rd_rdy;
|
||||
logic axi_rd_lock;
|
||||
logic axi_rd_last;
|
||||
logic axi_rd_valid;
|
||||
logic [63:0] axi_rd_data;
|
||||
logic [$size(axi_resp_i.r.id)-1:0] axi_rd_id_out;
|
||||
logic axi_rd_exokay;
|
||||
|
||||
logic req_valid_d, req_valid_q;
|
||||
icache_req_t req_data_d, req_data_q;
|
||||
logic first_d, first_q;
|
||||
logic [ICACHE_LINE_WIDTH/64-1:0][63:0] rd_shift_d, rd_shift_q;
|
||||
|
||||
// Keep read request asserted until we have an AXI grant. This is not guaranteed by icache (but
|
||||
// required by AXI).
|
||||
assign req_valid_d = ~axi_rd_gnt & (icache_mem_data_req | req_valid_q);
|
||||
|
||||
// Update read request information on a new request
|
||||
assign req_data_d = (icache_mem_data_req) ? icache_mem_data : req_data_q;
|
||||
|
||||
// We have a new or pending read request
|
||||
assign axi_rd_req = icache_mem_data_req | req_valid_q;
|
||||
assign axi_rd_addr = {{64-riscv::PLEN{1'b0}}, req_data_d.paddr};
|
||||
|
||||
// Fetch a full cache line on a cache miss, or a single word on a bypassed access
|
||||
assign axi_rd_blen = (req_data_d.nc) ? '0 : ariane_pkg::ICACHE_LINE_WIDTH/64-1;
|
||||
assign axi_rd_size = 2'b11;
|
||||
assign axi_rd_id_in = req_data_d.tid;
|
||||
assign axi_rd_rdy = 1'b1;
|
||||
assign axi_rd_lock = 1'b0;
|
||||
|
||||
// Immediately acknowledge read request. This is an implicit requirement for the icache.
|
||||
assign icache_mem_data_ack = icache_mem_data_req;
|
||||
|
||||
// Return data as soon as last word arrives
|
||||
assign icache_mem_rtrn_vld = axi_rd_valid & axi_rd_last;
|
||||
assign icache_mem_rtrn.data = rd_shift_d;
|
||||
assign icache_mem_rtrn.tid = req_data_q.tid;
|
||||
assign icache_mem_rtrn.rtype = wt_cache_pkg::ICACHE_IFILL_ACK;
|
||||
assign icache_mem_rtrn.inv = '0;
|
||||
|
||||
// -------
|
||||
// I-Cache
|
||||
// -------
|
||||
cva6_icache #(
|
||||
// use ID 0 for icache reads
|
||||
.RdTxId ( 0 ),
|
||||
.ArianeCfg ( ArianeCfg )
|
||||
) i_cva6_icache (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.flush_i ( flush_i ),
|
||||
.en_i ( en_i ),
|
||||
.miss_o ( miss_o ),
|
||||
.areq_i ( areq_i ),
|
||||
.areq_o ( areq_o ),
|
||||
.dreq_i ( dreq_i ),
|
||||
.dreq_o ( dreq_o ),
|
||||
.mem_rtrn_vld_i ( icache_mem_rtrn_vld ),
|
||||
.mem_rtrn_i ( icache_mem_rtrn ),
|
||||
.mem_data_req_o ( icache_mem_data_req ),
|
||||
.mem_data_ack_i ( icache_mem_data_ack ),
|
||||
.mem_data_o ( icache_mem_data )
|
||||
);
|
||||
|
||||
// --------
|
||||
// AXI shim
|
||||
// --------
|
||||
axi_shim #(
|
||||
.AxiNumWords ( AxiNumWords ),
|
||||
.AxiIdWidth ( $size(axi_resp_i.r.id) )
|
||||
) i_axi_shim (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.rd_req_i ( axi_rd_req ),
|
||||
.rd_gnt_o ( axi_rd_gnt ),
|
||||
.rd_addr_i ( axi_rd_addr ),
|
||||
.rd_blen_i ( axi_rd_blen ),
|
||||
.rd_size_i ( axi_rd_size ),
|
||||
.rd_id_i ( axi_rd_id_in ),
|
||||
.rd_rdy_i ( axi_rd_rdy ),
|
||||
.rd_lock_i ( axi_rd_lock ),
|
||||
.rd_last_o ( axi_rd_last ),
|
||||
.rd_valid_o ( axi_rd_valid ),
|
||||
.rd_data_o ( axi_rd_data ),
|
||||
.rd_id_o ( axi_rd_id_out ),
|
||||
.rd_exokay_o ( axi_rd_exokay ),
|
||||
.wr_req_i ( '0 ),
|
||||
.wr_gnt_o ( ),
|
||||
.wr_addr_i ( '0 ),
|
||||
.wr_data_i ( '0 ),
|
||||
.wr_be_i ( '0 ),
|
||||
.wr_blen_i ( '0 ),
|
||||
.wr_size_i ( '0 ),
|
||||
.wr_id_i ( '0 ),
|
||||
.wr_lock_i ( '0 ),
|
||||
.wr_atop_i ( '0 ),
|
||||
.wr_rdy_i ( '0 ),
|
||||
.wr_valid_o ( ),
|
||||
.wr_id_o ( ),
|
||||
.wr_exokay_o ( ),
|
||||
.axi_req_o ( axi_req_o ),
|
||||
.axi_resp_i ( axi_resp_i )
|
||||
);
|
||||
|
||||
// Buffer burst data in shift register
|
||||
always_comb begin : p_axi_rtrn_shift
|
||||
first_d = first_q;
|
||||
rd_shift_d = rd_shift_q;
|
||||
|
||||
if (axi_rd_valid) begin
|
||||
first_d = axi_rd_last;
|
||||
rd_shift_d = {axi_rd_data, rd_shift_q[ICACHE_LINE_WIDTH/64-1:1]};
|
||||
|
||||
// If this is a single word transaction, we need to make sure that word is placed at offset 0
|
||||
if (first_q) begin
|
||||
rd_shift_d[0] = axi_rd_data;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Registers
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin : p_rd_buf
|
||||
if (!rst_ni) begin
|
||||
req_valid_q <= 1'b0;
|
||||
req_data_q <= '0;
|
||||
first_q <= 1'b1;
|
||||
rd_shift_q <= '0;
|
||||
end else begin
|
||||
req_valid_q <= req_valid_d;
|
||||
req_data_q <= req_data_d;
|
||||
first_q <= first_d;
|
||||
rd_shift_q <= rd_shift_d;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // cva6_icache_axi_wrapper
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
|
||||
module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
|
||||
parameter logic [63:0] CACHE_START_ADDR = 64'h4000_0000
|
||||
parameter ariane_cfg_t ArianeCfg = ArianeDefaultConfig // contains cacheable regions
|
||||
) (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
|
@ -58,7 +58,9 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
|
|||
ariane_axi::req_t axi_req_data;
|
||||
ariane_axi::resp_t axi_resp_data;
|
||||
|
||||
std_icache i_icache (
|
||||
cva6_icache_axi_wrapper #(
|
||||
.ArianeCfg ( ArianeCfg )
|
||||
) i_cva6_icache_axi_wrapper (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.priv_lvl_i ( priv_lvl_i ),
|
||||
|
@ -78,7 +80,7 @@ module std_cache_subsystem import ariane_pkg::*; import std_cache_pkg::*; #(
|
|||
// Port 1: Load Unit
|
||||
// Port 2: Store Unit
|
||||
std_nbdcache #(
|
||||
.CACHE_START_ADDR ( CACHE_START_ADDR )
|
||||
.CACHE_START_ADDR ( ArianeCfg.CachedRegionAddrBase )
|
||||
) i_nbdcache (
|
||||
.clk_i,
|
||||
.rst_ni,
|
||||
|
|
|
@ -1,459 +0,0 @@
|
|||
// Copyright 2018 ETH Zurich and University of Bologna.
|
||||
// Copyright and related rights are licensed under the Solderpad Hardware
|
||||
// License, Version 0.51 (the "License"); you may not use this file except in
|
||||
// compliance with the License. You may obtain a copy of the License at
|
||||
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
||||
// or agreed to in writing, software, hardware and materials distributed under
|
||||
// this 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.
|
||||
//
|
||||
// Author: Florian Zaruba, ETH Zurich
|
||||
// Date: 12.02.2018
|
||||
// ------------------------------
|
||||
// Instruction Cache
|
||||
// ------------------------------
|
||||
|
||||
module std_icache import ariane_pkg::*; import std_cache_pkg::*; (
|
||||
input logic clk_i,
|
||||
input logic rst_ni,
|
||||
input riscv::priv_lvl_t priv_lvl_i,
|
||||
|
||||
input logic flush_i, // flush the icache, flush and kill have to be asserted together
|
||||
input logic en_i, // enable icache
|
||||
output logic miss_o, // to performance counter
|
||||
// address translation requests
|
||||
input icache_areq_i_t areq_i,
|
||||
output icache_areq_o_t areq_o,
|
||||
// data requests
|
||||
input icache_dreq_i_t dreq_i,
|
||||
output icache_dreq_o_t dreq_o,
|
||||
// AXI refill port
|
||||
output ariane_axi::req_t axi_req_o,
|
||||
input ariane_axi::resp_t axi_resp_i
|
||||
);
|
||||
|
||||
localparam int unsigned ICACHE_BYTE_OFFSET = $clog2(ICACHE_LINE_WIDTH/8); // 3
|
||||
localparam int unsigned ICACHE_NUM_WORD = 2**(ICACHE_INDEX_WIDTH - ICACHE_BYTE_OFFSET);
|
||||
localparam int unsigned NR_AXI_REFILLS = ($clog2(ICACHE_LINE_WIDTH/64) == 0) ? 1 : $clog2(ICACHE_LINE_WIDTH/64);
|
||||
// registers
|
||||
enum logic [3:0] { FLUSH, IDLE, TAG_CMP, WAIT_AXI_R_RESP, WAIT_KILLED_REFILL, WAIT_KILLED_AXI_R_RESP,
|
||||
REDO_REQ, TAG_CMP_SAVED, REFILL,
|
||||
WAIT_ADDRESS_TRANSLATION, WAIT_ADDRESS_TRANSLATION_KILLED
|
||||
} state_d, state_q;
|
||||
logic [$clog2(ICACHE_NUM_WORD)-1:0] cnt_d, cnt_q;
|
||||
logic [NR_AXI_REFILLS-1:0] burst_cnt_d, burst_cnt_q; // counter for AXI transfers
|
||||
logic [63:0] vaddr_d, vaddr_q;
|
||||
logic [ICACHE_TAG_WIDTH-1:0] tag_d, tag_q;
|
||||
logic [ICACHE_SET_ASSOC-1:0] evict_way_d, evict_way_q;
|
||||
logic flushing_d, flushing_q;
|
||||
|
||||
// signals
|
||||
logic [ICACHE_SET_ASSOC-1:0] req; // request to data memory
|
||||
logic [ICACHE_SET_ASSOC-1:0] vld_req; // request to valid/tag memory
|
||||
logic [(ICACHE_LINE_WIDTH+7)/8-1:0] data_be; // byte enable for data memory
|
||||
logic [(2**NR_AXI_REFILLS-1):0][7:0] be; // byte enable
|
||||
logic [$clog2(ICACHE_NUM_WORD)-1:0] addr; // this is a cache-line address, to memory array
|
||||
logic we; // write enable to memory array
|
||||
logic [ICACHE_SET_ASSOC-1:0] hit; // hit from tag compare
|
||||
logic [$clog2(ICACHE_NUM_WORD)-1:0] idx; // index in cache line
|
||||
logic update_lfsr; // shift the LFSR
|
||||
logic [ICACHE_SET_ASSOC-1:0] random_way; // random way select from LFSR
|
||||
logic [ICACHE_SET_ASSOC-1:0] way_valid; // bit string which contains the zapped valid bits
|
||||
logic [$clog2(ICACHE_SET_ASSOC)-1:0] repl_invalid; // first non-valid encountered
|
||||
logic repl_w_random; // we need to switch repl strategy since all are valid
|
||||
logic [ICACHE_TAG_WIDTH-1:0] tag; // tag to do comparison with
|
||||
|
||||
// tag + valid bit read/write data
|
||||
struct packed {
|
||||
logic valid;
|
||||
logic [ICACHE_TAG_WIDTH-1:0] tag;
|
||||
} tag_rdata [ICACHE_SET_ASSOC-1:0], tag_wdata;
|
||||
|
||||
logic [ICACHE_LINE_WIDTH-1:0] data_rdata [ICACHE_SET_ASSOC-1:0], data_wdata;
|
||||
logic [(2**NR_AXI_REFILLS-1):0][63:0] wdata;
|
||||
|
||||
for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : sram_block
|
||||
// ------------
|
||||
// Tag RAM
|
||||
// ------------
|
||||
sram #(
|
||||
// tag + valid bit
|
||||
.DATA_WIDTH ( ICACHE_TAG_WIDTH + 1 ),
|
||||
.NUM_WORDS ( ICACHE_NUM_WORD )
|
||||
) tag_sram (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.req_i ( vld_req[i] ),
|
||||
.we_i ( we ),
|
||||
.addr_i ( addr ),
|
||||
.wdata_i ( tag_wdata ),
|
||||
.be_i ( '1 ),
|
||||
.rdata_o ( tag_rdata[i] )
|
||||
);
|
||||
// ------------
|
||||
// Data RAM
|
||||
// ------------
|
||||
sram #(
|
||||
.DATA_WIDTH ( ICACHE_LINE_WIDTH ),
|
||||
.NUM_WORDS ( ICACHE_NUM_WORD )
|
||||
) data_sram (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.req_i ( req[i] ),
|
||||
.we_i ( we ),
|
||||
.addr_i ( addr ),
|
||||
.wdata_i ( data_wdata ),
|
||||
.be_i ( data_be ),
|
||||
.rdata_o ( data_rdata[i] )
|
||||
);
|
||||
end
|
||||
|
||||
// --------------------
|
||||
// Tag Comparison and way select
|
||||
// --------------------
|
||||
|
||||
// cacheline selected by hit
|
||||
logic [ICACHE_SET_ASSOC-1:0][FETCH_WIDTH-1:0] cl_sel;
|
||||
|
||||
assign idx = vaddr_q[ICACHE_BYTE_OFFSET-1:2];
|
||||
|
||||
generate
|
||||
for (genvar i = 0; i < ICACHE_SET_ASSOC; i++) begin : g_tag_cmpsel
|
||||
assign hit[i] = (tag_rdata[i].tag == tag) ? tag_rdata[i].valid : 1'b0;
|
||||
assign cl_sel[i] = (hit[i]) ? data_rdata[i][{idx, 5'b0} +: FETCH_WIDTH] : '0;
|
||||
assign way_valid[i] = tag_rdata[i].valid;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// OR reduction of selected cachelines
|
||||
always_comb begin : p_reduction
|
||||
dreq_o.data = cl_sel[0];
|
||||
for(int i = 1; i < ICACHE_SET_ASSOC; i++)
|
||||
dreq_o.data |= cl_sel[i];
|
||||
end
|
||||
|
||||
// ------------------
|
||||
// AXI Plumbing
|
||||
// ------------------
|
||||
// instruction cache is read-only
|
||||
assign axi_req_o.aw_valid = '0;
|
||||
assign axi_req_o.aw.addr = '0;
|
||||
assign axi_req_o.aw.prot = '0;
|
||||
assign axi_req_o.aw.region = '0;
|
||||
assign axi_req_o.aw.len = '0;
|
||||
assign axi_req_o.aw.size = 3'b000;
|
||||
assign axi_req_o.aw.burst = 2'b00;
|
||||
assign axi_req_o.aw.lock = '0;
|
||||
assign axi_req_o.aw.cache = '0;
|
||||
assign axi_req_o.aw.qos = '0;
|
||||
assign axi_req_o.aw.id = '0;
|
||||
assign axi_req_o.aw.atop = '0;
|
||||
assign axi_req_o.w_valid = '0;
|
||||
assign axi_req_o.w.data = '0;
|
||||
assign axi_req_o.w.strb = '0;
|
||||
assign axi_req_o.w.last = 1'b0;
|
||||
assign axi_req_o.b_ready = 1'b0;
|
||||
|
||||
// set protection flag, MSB -> instruction fetch, LSB -> privileged access or not
|
||||
assign axi_req_o.ar.prot = '0;
|
||||
assign axi_req_o.ar.region = '0;
|
||||
assign axi_req_o.ar.len = (2**NR_AXI_REFILLS) - 1;
|
||||
assign axi_req_o.ar.size = 3'b011;
|
||||
assign axi_req_o.ar.burst = 2'b01;
|
||||
assign axi_req_o.ar.lock = '0;
|
||||
assign axi_req_o.ar.cache = '0;
|
||||
assign axi_req_o.ar.qos = '0;
|
||||
assign axi_req_o.ar.id = '0;
|
||||
|
||||
assign axi_req_o.r_ready = 1'b1;
|
||||
|
||||
assign data_be = be;
|
||||
assign data_wdata = wdata;
|
||||
|
||||
assign dreq_o.ex = areq_i.fetch_exception;
|
||||
|
||||
assign addr = (state_q==FLUSH) ? cnt_q : vaddr_d[ICACHE_INDEX_WIDTH-1:ICACHE_BYTE_OFFSET];
|
||||
|
||||
// ------------------
|
||||
// Cache Ctrl
|
||||
// ------------------
|
||||
// for bypassing we use the existing infrastructure of the cache
|
||||
// but on every access we are re-fetching the cache-line
|
||||
always_comb begin : cache_ctrl
|
||||
// default assignments
|
||||
state_d = state_q;
|
||||
cnt_d = cnt_q;
|
||||
vaddr_d = vaddr_q;
|
||||
tag_d = tag_q;
|
||||
evict_way_d = evict_way_q;
|
||||
flushing_d = flushing_q;
|
||||
burst_cnt_d = burst_cnt_q;
|
||||
|
||||
dreq_o.vaddr = vaddr_q;
|
||||
|
||||
req = '0;
|
||||
vld_req = '0;
|
||||
we = 1'b0;
|
||||
be = '0;
|
||||
wdata = '0;
|
||||
tag_wdata = '0;
|
||||
dreq_o.ready = 1'b0;
|
||||
tag = areq_i.fetch_paddr[ICACHE_TAG_WIDTH+ICACHE_INDEX_WIDTH-1:ICACHE_INDEX_WIDTH];
|
||||
dreq_o.valid = 1'b0;
|
||||
update_lfsr = 1'b0;
|
||||
miss_o = 1'b0;
|
||||
|
||||
axi_req_o.ar_valid = 1'b0;
|
||||
axi_req_o.ar.addr = '0;
|
||||
|
||||
areq_o.fetch_req = 1'b0;
|
||||
areq_o.fetch_vaddr = vaddr_q;
|
||||
|
||||
case (state_q)
|
||||
// ~> we are ready to receive a new request
|
||||
IDLE: begin
|
||||
dreq_o.ready = 1'b1;
|
||||
vaddr_d = dreq_i.vaddr;
|
||||
|
||||
// we are getting a new request
|
||||
if (dreq_i.req) begin
|
||||
// request the content of all arrays
|
||||
req = '1;
|
||||
vld_req = '1;
|
||||
// save the virtual address
|
||||
state_d = TAG_CMP;
|
||||
end
|
||||
|
||||
// go to flushing state
|
||||
if (flush_i || flushing_q)
|
||||
state_d = FLUSH;
|
||||
|
||||
if (dreq_i.kill_s1)
|
||||
state_d = IDLE;
|
||||
end
|
||||
// ~> compare the tag
|
||||
TAG_CMP, TAG_CMP_SAVED: begin
|
||||
areq_o.fetch_req = 1'b1; // request address translation
|
||||
|
||||
// (speculatively) request the content of all arrays
|
||||
req = '1;
|
||||
vld_req = '1;
|
||||
|
||||
// use the saved tag
|
||||
if (state_q == TAG_CMP_SAVED)
|
||||
tag = tag_q;
|
||||
// -------
|
||||
// Hit
|
||||
// -------
|
||||
// disabling the icache just makes it fetch on every request
|
||||
if (|hit && areq_i.fetch_valid && (en_i || (state_q != TAG_CMP))) begin
|
||||
dreq_o.ready = 1'b1;
|
||||
dreq_o.valid = 1'b1;
|
||||
vaddr_d = dreq_i.vaddr;
|
||||
|
||||
// we've got another request
|
||||
if (dreq_i.req) begin
|
||||
// save the index and stay in compare mode
|
||||
state_d = TAG_CMP;
|
||||
// no new request -> go back to idle
|
||||
end else begin
|
||||
state_d = IDLE;
|
||||
end
|
||||
|
||||
if (dreq_i.kill_s1)
|
||||
state_d = IDLE;
|
||||
// -------
|
||||
// Miss
|
||||
// -------
|
||||
end else begin
|
||||
state_d = REFILL;
|
||||
// hit gonna be zero in most cases except for when the cache is disabled
|
||||
evict_way_d = hit;
|
||||
// save tag
|
||||
tag_d = areq_i.fetch_paddr[ICACHE_TAG_WIDTH+ICACHE_INDEX_WIDTH-1:ICACHE_INDEX_WIDTH];
|
||||
miss_o = en_i;
|
||||
// get way which to replace
|
||||
// only if there is no hit we should fall back to real replacement. If there was a hit then
|
||||
// it means we are in bypass mode (!en_i) and should update the cache-line with the most recent
|
||||
// value fetched from memory.
|
||||
if (!(|hit)) begin
|
||||
// all ways are currently full, randomly replace one of them
|
||||
if (repl_w_random) begin
|
||||
evict_way_d = random_way;
|
||||
// shift the lfsr
|
||||
update_lfsr = 1'b1;
|
||||
// there is still one cache-line which is not valid ~> replace that one
|
||||
end else begin
|
||||
evict_way_d[repl_invalid] = 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
// if we didn't hit on the TLB we need to wait until the request has been completed
|
||||
if (!areq_i.fetch_valid) begin
|
||||
state_d = WAIT_ADDRESS_TRANSLATION;
|
||||
end
|
||||
end
|
||||
// ~> wait here for a valid address translation, or on a translation even if the request has been killed
|
||||
WAIT_ADDRESS_TRANSLATION, WAIT_ADDRESS_TRANSLATION_KILLED: begin
|
||||
areq_o.fetch_req = 1'b1;
|
||||
// retry the request if no exception occurred
|
||||
if (areq_i.fetch_valid && (state_q == WAIT_ADDRESS_TRANSLATION)) begin
|
||||
if (areq_i.fetch_exception.valid) begin
|
||||
dreq_o.valid = 1'b1;
|
||||
state_d = IDLE;
|
||||
end else begin
|
||||
state_d = REDO_REQ;
|
||||
tag_d = areq_i.fetch_paddr[ICACHE_TAG_WIDTH+ICACHE_INDEX_WIDTH-1:ICACHE_INDEX_WIDTH];
|
||||
end
|
||||
end else if (areq_i.fetch_valid) begin
|
||||
state_d = IDLE;
|
||||
end
|
||||
|
||||
if (dreq_i.kill_s2)
|
||||
state_d = WAIT_ADDRESS_TRANSLATION_KILLED;
|
||||
end
|
||||
// ~> request a cache-line refill
|
||||
REFILL, WAIT_KILLED_REFILL: begin
|
||||
axi_req_o.ar_valid = 1'b1;
|
||||
axi_req_o.ar.addr[ICACHE_INDEX_WIDTH+ICACHE_TAG_WIDTH-1:0] = {tag_q, vaddr_q[ICACHE_INDEX_WIDTH-1:ICACHE_BYTE_OFFSET], {ICACHE_BYTE_OFFSET{1'b0}}};
|
||||
burst_cnt_d = '0;
|
||||
|
||||
if (dreq_i.kill_s2)
|
||||
state_d = WAIT_KILLED_REFILL;
|
||||
|
||||
// we need to finish this AXI transfer
|
||||
if (axi_resp_i.ar_ready)
|
||||
state_d = (dreq_i.kill_s2 || (state_q == WAIT_KILLED_REFILL)) ? WAIT_KILLED_AXI_R_RESP : WAIT_AXI_R_RESP;
|
||||
end
|
||||
// ~> wait for the read response
|
||||
WAIT_AXI_R_RESP, WAIT_KILLED_AXI_R_RESP: begin
|
||||
|
||||
req = evict_way_q;
|
||||
vld_req = evict_way_q;
|
||||
|
||||
if (axi_resp_i.r_valid) begin
|
||||
we = 1'b1;
|
||||
tag_wdata.tag = tag_q;
|
||||
tag_wdata.valid = 1'b1;
|
||||
wdata[burst_cnt_q] = axi_resp_i.r.data;
|
||||
// enable the right write path
|
||||
be[burst_cnt_q] = '1;
|
||||
// increase burst count
|
||||
burst_cnt_d = burst_cnt_q + 1;
|
||||
end
|
||||
|
||||
if (dreq_i.kill_s2)
|
||||
state_d = WAIT_KILLED_AXI_R_RESP;
|
||||
|
||||
if (axi_resp_i.r_valid && axi_resp_i.r.last) begin
|
||||
state_d = (dreq_i.kill_s2) ? IDLE : REDO_REQ;
|
||||
end
|
||||
|
||||
if ((state_q == WAIT_KILLED_AXI_R_RESP) && axi_resp_i.r.last && axi_resp_i.r_valid)
|
||||
state_d = IDLE;
|
||||
end
|
||||
// ~> redo the request,
|
||||
REDO_REQ: begin
|
||||
req = '1;
|
||||
vld_req = '1;
|
||||
tag = tag_q;
|
||||
state_d = TAG_CMP_SAVED; // do tag comparison on the saved tag
|
||||
end
|
||||
// ~> we are coming here after reset or when a flush was requested
|
||||
FLUSH: begin
|
||||
cnt_d = cnt_q + 1;
|
||||
vld_req = '1;
|
||||
we = 1;
|
||||
// we've finished flushing, go back to idle
|
||||
if (cnt_q == ICACHE_NUM_WORD - 1) begin
|
||||
state_d = IDLE;
|
||||
flushing_d = 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
default : state_d = IDLE;
|
||||
endcase
|
||||
|
||||
// those are the states where we need to wait a little longer until we can safely exit
|
||||
if (dreq_i.kill_s2 && !(state_q inside {
|
||||
REFILL,
|
||||
WAIT_AXI_R_RESP,
|
||||
WAIT_KILLED_AXI_R_RESP,
|
||||
WAIT_KILLED_REFILL,
|
||||
WAIT_ADDRESS_TRANSLATION,
|
||||
WAIT_ADDRESS_TRANSLATION_KILLED})
|
||||
&& !dreq_o.ready) begin
|
||||
state_d = IDLE;
|
||||
end
|
||||
|
||||
// if we are killing we can never give a valid response
|
||||
if (dreq_i.kill_s2)
|
||||
dreq_o.valid = 1'b0;
|
||||
|
||||
if (flush_i) begin
|
||||
flushing_d = 1'b1;
|
||||
dreq_o.ready = 1'b0; // we are not ready to accept a further request here
|
||||
end
|
||||
// if we are going to flush -> do not accept any new requests
|
||||
if (flushing_q)
|
||||
dreq_o.ready = 1'b0;
|
||||
end
|
||||
|
||||
lzc #(
|
||||
.WIDTH ( ICACHE_SET_ASSOC )
|
||||
) i_lzc (
|
||||
.in_i ( ~way_valid ),
|
||||
.cnt_o ( repl_invalid ),
|
||||
.empty_o ( repl_w_random )
|
||||
);
|
||||
|
||||
// -----------------
|
||||
// Replacement LFSR
|
||||
// -----------------
|
||||
lfsr_8bit #(.WIDTH (ICACHE_SET_ASSOC)) i_lfsr (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.en_i ( update_lfsr ),
|
||||
.refill_way_oh ( random_way ),
|
||||
.refill_way_bin ( ) // left open
|
||||
);
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
state_q <= FLUSH;
|
||||
cnt_q <= '0;
|
||||
vaddr_q <= '0;
|
||||
tag_q <= '0;
|
||||
evict_way_q <= '0;
|
||||
flushing_q <= 1'b0;
|
||||
burst_cnt_q <= '0;
|
||||
end else begin
|
||||
state_q <= state_d;
|
||||
cnt_q <= cnt_d;
|
||||
vaddr_q <= vaddr_d;
|
||||
tag_q <= tag_d;
|
||||
evict_way_q <= evict_way_d;
|
||||
flushing_q <= flushing_d;
|
||||
burst_cnt_q <= burst_cnt_d;
|
||||
end
|
||||
end
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// assertions
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
//pragma translate_off
|
||||
`ifndef VERILATOR
|
||||
initial begin
|
||||
assert ($bits(axi_req_o.aw.addr) == 64)
|
||||
else $fatal(1, "[icache] Ariane needs a 64-bit bus");
|
||||
end
|
||||
|
||||
// assert that cache only hits on one way
|
||||
onehot: assert property (
|
||||
@(posedge clk_i) disable iff (~rst_ni) $onehot0(hit))
|
||||
else $fatal(1, "[icache] Hit should be one-hot encoded");
|
||||
`endif
|
||||
//pragma translate_on
|
||||
endmodule
|
|
@ -70,11 +70,11 @@ module wt_cache_subsystem import ariane_pkg::*; import wt_cache_pkg::*; #(
|
|||
wt_cache_pkg::dcache_req_t dcache_adapter;
|
||||
wt_cache_pkg::dcache_rtrn_t adapter_dcache;
|
||||
|
||||
wt_icache #(
|
||||
cva6_icache #(
|
||||
// use ID 0 for icache reads
|
||||
.RdTxId ( 0 ),
|
||||
.ArianeCfg ( ArianeCfg )
|
||||
) i_wt_icache (
|
||||
) i_cva6_icache (
|
||||
.clk_i ( clk_i ),
|
||||
.rst_ni ( rst_ni ),
|
||||
.flush_i ( icache_flush_i ),
|
||||
|
|
|
@ -261,7 +261,7 @@ module tb import tb_pkg::*; import ariane_pkg::*; import wt_cache_pkg::*; #()();
|
|||
// MUT
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
wt_icache #(
|
||||
cva6_icache #(
|
||||
.ArianeCfg(Cfg)
|
||||
) dut (
|
||||
.clk_i ( clk_i ),
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
../../include/ariane_pkg.sv
|
||||
../../include/wt_cache_pkg.sv
|
||||
../../src/fpga-support/rtl/SyncSpRamBeNx64.sv
|
||||
../../src/cache_subsystem/wt_icache.sv
|
||||
../../src/cache_subsystem/cva6_icache.sv
|
||||
../../src/common_cells/src/lfsr_8bit.sv
|
||||
../../src/common_cells/src/fifo_v3.sv
|
||||
../../src/common_cells/src/lzc.sv
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue