Skip to content

Commit

Permalink
cache_subsystem: Parametrise AXI interface (#982)
Browse files Browse the repository at this point in the history
Parametrise the AXI interface of CVA6. With this PR, both cache subsystems support variable AXI address widths. The write-through cache furthermore supports variable AXI data widths. Moreover, this PR includes a modular AXI testbench for the WT cache to test the introduced changes. The following configurations of the WT cache have been verified:

XLEN   Cacheline Width   AXI data width   AXI address width
64                  128                     64                       64
64                  128                   128                       52
64                  512                   128                       64
32                  512                   256                       48
32                    64                     32                       48
  • Loading branch information
niwis authored Oct 26, 2022
1 parent f346d66 commit 17743bc
Show file tree
Hide file tree
Showing 35 changed files with 1,787 additions and 185 deletions.
35 changes: 22 additions & 13 deletions core/axi_adapter.sv
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
module axi_adapter #(
parameter int unsigned DATA_WIDTH = 256,
parameter logic CRITICAL_WORD_FIRST = 0, // the AXI subsystem needs to support wrapping reads for this feature
parameter int unsigned AXI_ID_WIDTH = 10,
parameter int unsigned CACHELINE_BYTE_OFFSET = 8
parameter int unsigned CACHELINE_BYTE_OFFSET = 8,
parameter int unsigned AXI_ADDR_WIDTH = 0,
parameter int unsigned AXI_DATA_WIDTH = 0,
parameter int unsigned AXI_ID_WIDTH = 0,
parameter type axi_req_t = ariane_axi::req_t,
parameter type axi_rsp_t = ariane_axi::resp_t
)(
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
Expand All @@ -31,23 +35,23 @@ module axi_adapter #(
output logic gnt_o,
input logic [riscv::XLEN-1:0] addr_i,
input logic we_i,
input logic [(DATA_WIDTH/riscv::XLEN)-1:0][riscv::XLEN-1:0] wdata_i,
input logic [(DATA_WIDTH/riscv::XLEN)-1:0][(riscv::XLEN/8)-1:0] be_i,
input logic [(DATA_WIDTH/AXI_DATA_WIDTH)-1:0][AXI_DATA_WIDTH-1:0] wdata_i,
input logic [(DATA_WIDTH/AXI_DATA_WIDTH)-1:0][(AXI_DATA_WIDTH/8)-1:0] be_i,
input logic [1:0] size_i,
input logic [AXI_ID_WIDTH-1:0] id_i,
// read port
output logic valid_o,
output logic [(DATA_WIDTH/riscv::XLEN)-1:0][riscv::XLEN-1:0] rdata_o,
output logic [(DATA_WIDTH/AXI_DATA_WIDTH)-1:0][AXI_DATA_WIDTH-1:0] rdata_o,
output logic [AXI_ID_WIDTH-1:0] id_o,
// critical word - read port
output logic [riscv::XLEN-1:0] critical_word_o,
output logic [AXI_DATA_WIDTH-1:0] critical_word_o,
output logic critical_word_valid_o,
// AXI port
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i
output axi_req_t axi_req_o,
input axi_rsp_t axi_resp_i
);
localparam BURST_SIZE = DATA_WIDTH/riscv::XLEN-1;
localparam ADDR_INDEX = ($clog2(DATA_WIDTH/riscv::XLEN) > 0) ? $clog2(DATA_WIDTH/riscv::XLEN) : 1;
localparam BURST_SIZE = (DATA_WIDTH/AXI_DATA_WIDTH)-1;
localparam ADDR_INDEX = ($clog2(DATA_WIDTH/AXI_DATA_WIDTH) > 0) ? $clog2(DATA_WIDTH/AXI_DATA_WIDTH) : 1;

enum logic [3:0] {
IDLE, WAIT_B_VALID, WAIT_AW_READY, WAIT_LAST_W_READY, WAIT_LAST_W_READY_AW_READY, WAIT_AW_READY_BURST,
Expand All @@ -56,9 +60,9 @@ module axi_adapter #(

// counter for AXI transfers
logic [ADDR_INDEX-1:0] cnt_d, cnt_q;
logic [(DATA_WIDTH/riscv::XLEN)-1:0][riscv::XLEN-1:0] cache_line_d, cache_line_q;
logic [(DATA_WIDTH/AXI_DATA_WIDTH)-1:0][AXI_DATA_WIDTH-1:0] cache_line_d, cache_line_q;
// save the address for a read, as we allow for non-cacheline aligned accesses
logic [(DATA_WIDTH/riscv::XLEN)-1:0] addr_offset_d, addr_offset_q;
logic [(DATA_WIDTH/AXI_DATA_WIDTH)-1:0] addr_offset_d, addr_offset_q;
logic [AXI_ID_WIDTH-1:0] id_d, id_q;
logic [ADDR_INDEX-1:0] index;
// save the atomic operation and size
Expand All @@ -68,6 +72,7 @@ module axi_adapter #(
always_comb begin : axi_fsm
// Default assignments
axi_req_o.aw_valid = 1'b0;
// Cast to AXI address width
axi_req_o.aw.addr = addr_i;
axi_req_o.aw.prot = 3'b0;
axi_req_o.aw.region = 4'b0;
Expand All @@ -82,9 +87,13 @@ module axi_adapter #(
axi_req_o.aw.user = '0;

axi_req_o.ar_valid = 1'b0;
// Cast to AXI address width
axi_req_o.ar.addr = addr_i;
// in case of a single request or wrapping transfer we can simply begin at the address, if we want to request a cache-line
// with an incremental transfer we need to output the corresponding base address of the cache line
axi_req_o.ar.addr = (CRITICAL_WORD_FIRST || type_i == ariane_axi::SINGLE_REQ) ? addr_i : { addr_i[(riscv::XLEN-1):CACHELINE_BYTE_OFFSET], {{CACHELINE_BYTE_OFFSET}{1'b0}}};
if (!CRITICAL_WORD_FIRST && type_i != ariane_axi::SINGLE_REQ) begin
axi_req_o.ar.addr[CACHELINE_BYTE_OFFSET-1:0] = '0;
end
axi_req_o.ar.prot = 3'b0;
axi_req_o.ar.region = 4'b0;
axi_req_o.ar.len = 8'b0;
Expand Down
37 changes: 23 additions & 14 deletions core/axi_shim.sv
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,40 @@
module axi_shim #(
parameter int unsigned AxiUserWidth = 64, // data width in dwords, this is also the maximum burst length, must be >=2
parameter int unsigned AxiNumWords = 4, // data width in dwords, this is also the maximum burst length, must be >=2
parameter int unsigned AxiIdWidth = 4 // stick to the spec
parameter int unsigned AxiAddrWidth = 0,
parameter int unsigned AxiDataWidth = 0,
parameter int unsigned AxiIdWidth = 0,
parameter type axi_req_t = ariane_axi::req_t,
parameter type axi_rsp_t = ariane_axi::resp_t
) (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
// read channel
// request
input logic rd_req_i,
output logic rd_gnt_o,
input logic [63:0] rd_addr_i,
input logic [AxiAddrWidth-1:0] rd_addr_i,
input logic [$clog2(AxiNumWords)-1:0] rd_blen_i, // axi convention: LEN-1
input logic [1:0] rd_size_i,
input logic [2:0] rd_size_i,
input logic [AxiIdWidth-1:0] rd_id_i, // use same ID for reads, or make sure you only have one outstanding read tx
input logic rd_lock_i,
// read response (we have to unconditionally sink the response)
input logic rd_rdy_i,
output logic rd_last_o,
output logic rd_valid_o,
output logic [63:0] rd_data_o,
output logic [AxiDataWidth-1:0] rd_data_o,
output logic [AxiUserWidth-1:0] rd_user_o,
output logic [AxiIdWidth-1:0] rd_id_o,
output logic rd_exokay_o, // indicates whether exclusive tx succeeded
// write channel
input logic wr_req_i,
output logic wr_gnt_o,
input logic [63:0] wr_addr_i,
input logic [AxiNumWords-1:0][63:0] wr_data_i,
input logic [AxiNumWords-1:0][AxiUserWidth-1:0] wr_user_i,
input logic [AxiNumWords-1:0][7:0] wr_be_i,
input logic [AxiAddrWidth-1:0] wr_addr_i,
input logic [AxiNumWords-1:0][AxiDataWidth-1:0] wr_data_i,
input logic [AxiNumWords-1:0][AxiUserWidth-1:0] wr_user_i,
input logic [AxiNumWords-1:0][(AxiDataWidth/8)-1:0] wr_be_i,
input logic [$clog2(AxiNumWords)-1:0] wr_blen_i, // axi convention: LEN-1
input logic [1:0] wr_size_i,
input logic [2:0] wr_size_i,
input logic [AxiIdWidth-1:0] wr_id_i,
input logic wr_lock_i,
input logic [5:0] wr_atop_i,
Expand All @@ -61,8 +65,8 @@ module axi_shim #(
output logic [AxiIdWidth-1:0] wr_id_o,
output logic wr_exokay_o, // indicates whether exclusive tx succeeded
// AXI port
output ariane_axi::req_t axi_req_o,
input ariane_axi::resp_t axi_resp_i
output axi_req_t axi_req_o,
input axi_rsp_t axi_resp_i
);
localparam AddrIndex = ($clog2(AxiNumWords) > 0) ? $clog2(AxiNumWords) : 1;

Expand All @@ -82,7 +86,7 @@ module axi_shim #(

// address
assign axi_req_o.aw.burst = axi_pkg::BURST_INCR; // Use BURST_INCR for AXI regular transaction
assign axi_req_o.aw.addr = wr_addr_i;
assign axi_req_o.aw.addr = wr_addr_i[AxiAddrWidth-1:0];
assign axi_req_o.aw.size = wr_size_i;
assign axi_req_o.aw.len = wr_blen_i;
assign axi_req_o.aw.id = wr_id_i;
Expand All @@ -92,6 +96,8 @@ module axi_shim #(
assign axi_req_o.aw.cache = 4'b0;
assign axi_req_o.aw.qos = 4'b0;
assign axi_req_o.aw.atop = wr_atop_i;
assign axi_req_o.aw.user = '0;

// data
assign axi_req_o.w.data = wr_data_i[wr_cnt_q];
assign axi_req_o.w.user = wr_user_i[wr_cnt_q];
Expand Down Expand Up @@ -239,7 +245,7 @@ module axi_shim #(
// in case of a wrapping transfer we can simply begin at the address, if we want to request a cache-line
// with an incremental transfer we need to output the corresponding base address of the cache line
assign axi_req_o.ar.burst = axi_pkg::BURST_INCR; // Use BURST_INCR for AXI regular transaction
assign axi_req_o.ar.addr = rd_addr_i;
assign axi_req_o.ar.addr = rd_addr_i[AxiAddrWidth-1:0];
assign axi_req_o.ar.size = rd_size_i;
assign axi_req_o.ar.len = rd_blen_i;
assign axi_req_o.ar.id = rd_id_i;
Expand All @@ -248,6 +254,7 @@ module axi_shim #(
assign axi_req_o.ar.lock = rd_lock_i;
assign axi_req_o.ar.cache = 4'b0;
assign axi_req_o.ar.qos = 4'b0;
assign axi_req_o.ar.user = '0;

// make the read request
assign axi_req_o.ar_valid = rd_req_i;
Expand Down Expand Up @@ -285,7 +292,9 @@ module axi_shim #(
`ifndef VERILATOR
initial begin
assert (AxiNumWords >= 1) else
$fatal(1,"[axi adapter] AxiNumWords must be >= 1");
$fatal(1, "[axi adapter] AxiNumWords must be >= 1");
assert (AxiIdWidth >= 2) else
$fatal(1, "[axi adapter] AXI id width must be at least 2 bit wide");
end
`endif
//pragma translate_on
Expand Down
6 changes: 1 addition & 5 deletions core/cache_subsystem/cache_ctrl.sv
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,7 @@ module cache_ctrl import ariane_pkg::*; import std_cache_pkg::*; #(
end

// this is timing critical
// req_port_o.data_rdata = cl_i[cl_offset +: 64];
case (mem_req_q.index[3])
1'b0: req_port_o.data_rdata = cl_i[63:0];
1'b1: req_port_o.data_rdata = cl_i[127:64];
endcase
req_port_o.data_rdata = cl_i[cl_offset +: 64];

// report data for a read
if (!mem_req_q.we) begin
Expand Down
2 changes: 1 addition & 1 deletion core/cache_subsystem/cva6_icache.sv
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ module cva6_icache import ariane_pkg::*; import wt_cache_pkg::*; #(
assign cl_index = vaddr_d[ICACHE_INDEX_WIDTH-1:ICACHE_OFFSET_WIDTH];


if (ArianeCfg.Axi64BitCompliant) begin : gen_axi_offset
if (ArianeCfg.AxiCompliant) begin : gen_axi_offset
// if we generate a noncacheable access, the word will be at offset 0 or 4 in the cl coming from memory
assign cl_offset_d = ( dreq_o.ready & dreq_i.req) ? {dreq_i.vaddr>>2, 2'b0} :
( paddr_is_nc & mem_data_req_o ) ? cl_offset_q[2]<<2 : // needed since we transfer 32bit over a 64bit AXI bus in this case
Expand Down
39 changes: 26 additions & 13 deletions core/cache_subsystem/cva6_icache_axi_wrapper.sv
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
//

module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
parameter ariane_cfg_t ArianeCfg = ArianeDefaultConfig // contains cacheable regions
parameter ariane_cfg_t ArianeCfg = ArianeDefaultConfig, // contains cacheable regions
parameter int unsigned AxiAddrWidth = 0,
parameter int unsigned AxiDataWidth = 0,
parameter int unsigned AxiIdWidth = 0,
parameter type axi_req_t = ariane_axi::req_t,
parameter type axi_rsp_t = ariane_axi::resp_t
) (
input logic clk_i,
input logic rst_ni,
Expand All @@ -30,12 +35,12 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
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
output axi_req_t axi_req_o,
input axi_rsp_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) ;
localparam AxiNumWords = (ICACHE_LINE_WIDTH/AxiDataWidth) * (ICACHE_LINE_WIDTH > DCACHE_LINE_WIDTH) +
(DCACHE_LINE_WIDTH/AxiDataWidth) * (ICACHE_LINE_WIDTH <= DCACHE_LINE_WIDTH) ;

logic icache_mem_rtrn_vld;
icache_rtrn_t icache_mem_rtrn;
Expand All @@ -47,20 +52,20 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
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 [2: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 [AxiDataWidth-1: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;
logic [ICACHE_LINE_WIDTH/AxiDataWidth-1:0][AxiDataWidth-1: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).
Expand All @@ -75,7 +80,7 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(

// 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_size = $clog2(AxiDataWidth/8); // Maximum
assign axi_rd_id_in = req_data_d.tid;
assign axi_rd_rdy = 1'b1;
assign axi_rd_lock = 1'b0;
Expand Down Expand Up @@ -118,9 +123,13 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(
// AXI shim
// --------
axi_shim #(
.AxiUserWidth ( AXI_USER_WIDTH ),
.AxiNumWords ( AxiNumWords ),
.AxiIdWidth ( $size(axi_resp_i.r.id) )
.AxiNumWords ( AxiNumWords ),
.AxiAddrWidth ( AxiAddrWidth ),
.AxiDataWidth ( AxiDataWidth ),
.AxiIdWidth ( AxiIdWidth ),
.AxiUserWidth ( AXI_USER_WIDTH ),
.axi_req_t ( axi_req_t ),
.axi_rsp_t ( axi_rsp_t )
) i_axi_shim (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
Expand Down Expand Up @@ -164,7 +173,11 @@ module cva6_icache_axi_wrapper import ariane_pkg::*; import wt_cache_pkg::*; #(

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 (ICACHE_LINE_WIDTH == AxiDataWidth) begin
rd_shift_d = axi_rd_data;
end else begin
rd_shift_d = {axi_rd_data, rd_shift_q[ICACHE_LINE_WIDTH/AxiDataWidth-1:1]};
end

// If this is a single word transaction, we need to make sure that word is placed at offset 0
if (first_q) begin
Expand Down
Loading

0 comments on commit 17743bc

Please sign in to comment.