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:
Nils Wistoff 2020-09-11 20:22:22 +02:00 committed by Florian Zaruba
parent da363791d0
commit de5077332e
10 changed files with 203 additions and 472 deletions

View file

@ -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

View file

@ -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

View file

@ -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 ),

View file

@ -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

View 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

View file

@ -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,

View file

@ -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

View file

@ -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 ),

View file

@ -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 ),

View file

@ -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