mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-22 13:17:41 -04:00
Remove redundant axi_adapter.sv
(#1174)
This module was moved to `core/cache_subsystem/axi_adapter.sv` in #1127.
This commit is contained in:
parent
e51eec2904
commit
09ed2de0df
2 changed files with 1 additions and 456 deletions
3
Makefile
3
Makefile
|
@ -139,8 +139,7 @@ endif
|
|||
|
||||
|
||||
# this list contains the standalone components
|
||||
src := corev_apu/tb/axi_adapter.sv \
|
||||
corev_apu/tb/ariane.sv \
|
||||
src := corev_apu/tb/ariane.sv \
|
||||
$(wildcard corev_apu/bootrom/*.sv) \
|
||||
$(wildcard corev_apu/clint/*.sv) \
|
||||
$(wildcard corev_apu/fpga/src/axi2apb/src/*.sv) \
|
||||
|
|
|
@ -1,454 +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.
|
||||
*
|
||||
* File: axi_adapter.sv
|
||||
* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
|
||||
* Date: 1.8.2018
|
||||
*
|
||||
* Description: Manages communication with the AXI Bus
|
||||
*/
|
||||
//import std_cache_pkg::*;
|
||||
|
||||
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 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
|
||||
|
||||
input logic req_i,
|
||||
input ariane_axi::ad_req_t type_i,
|
||||
input ariane_pkg::amo_t amo_i,
|
||||
output logic gnt_o,
|
||||
input logic [riscv::XLEN-1:0] addr_i,
|
||||
input logic we_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/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 [AXI_DATA_WIDTH-1:0] critical_word_o,
|
||||
output logic critical_word_valid_o,
|
||||
// AXI port
|
||||
output axi_req_t axi_req_o,
|
||||
input axi_rsp_t axi_resp_i
|
||||
);
|
||||
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,
|
||||
WAIT_R_VALID, WAIT_R_VALID_MULTIPLE, COMPLETE_READ, WAIT_AMO_R_VALID
|
||||
} state_q, state_d;
|
||||
|
||||
// counter for AXI transfers
|
||||
logic [ADDR_INDEX-1:0] cnt_d, cnt_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/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
|
||||
ariane_pkg::amo_t amo_d, amo_q;
|
||||
logic [1:0] size_d, size_q;
|
||||
|
||||
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;
|
||||
axi_req_o.aw.len = 8'b0;
|
||||
axi_req_o.aw.size = {1'b0, size_i}; // 1, 2, 4 or 8 bytes
|
||||
axi_req_o.aw.burst = axi_pkg::BURST_INCR; // Use BURST_INCR for AXI regular transaction
|
||||
axi_req_o.aw.lock = 1'b0;
|
||||
axi_req_o.aw.cache = axi_pkg::CACHE_MODIFIABLE;
|
||||
axi_req_o.aw.qos = 4'b0;
|
||||
axi_req_o.aw.id = id_i;
|
||||
axi_req_o.aw.atop = atop_from_amo(amo_i);
|
||||
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
|
||||
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;
|
||||
axi_req_o.ar.size = {1'b0, size_i}; // 1, 2, 4 or 8 bytes
|
||||
axi_req_o.ar.burst = (CRITICAL_WORD_FIRST ? axi_pkg::BURST_WRAP : axi_pkg::BURST_INCR); // wrapping transfer in case of a critical word first strategy
|
||||
axi_req_o.ar.lock = 1'b0;
|
||||
axi_req_o.ar.cache = axi_pkg::CACHE_MODIFIABLE;
|
||||
axi_req_o.ar.qos = 4'b0;
|
||||
axi_req_o.ar.id = id_i;
|
||||
axi_req_o.ar.user = '0;
|
||||
|
||||
axi_req_o.w_valid = 1'b0;
|
||||
axi_req_o.w.data = wdata_i[0];
|
||||
axi_req_o.w.strb = be_i[0];
|
||||
axi_req_o.w.last = 1'b0;
|
||||
axi_req_o.w.user = '0;
|
||||
|
||||
axi_req_o.b_ready = 1'b0;
|
||||
axi_req_o.r_ready = 1'b0;
|
||||
|
||||
gnt_o = 1'b0;
|
||||
valid_o = 1'b0;
|
||||
id_o = axi_resp_i.r.id;
|
||||
|
||||
critical_word_o = axi_resp_i.r.data;
|
||||
critical_word_valid_o = 1'b0;
|
||||
rdata_o = cache_line_q;
|
||||
|
||||
state_d = state_q;
|
||||
cnt_d = cnt_q;
|
||||
cache_line_d = cache_line_q;
|
||||
addr_offset_d = addr_offset_q;
|
||||
id_d = id_q;
|
||||
amo_d = amo_q;
|
||||
size_d = size_q;
|
||||
index = '0;
|
||||
|
||||
case (state_q)
|
||||
|
||||
IDLE: begin
|
||||
cnt_d = '0;
|
||||
// we have an incoming request
|
||||
if (req_i) begin
|
||||
// is this a read or write?
|
||||
// write
|
||||
if (we_i) begin
|
||||
// the data is valid
|
||||
axi_req_o.aw_valid = 1'b1;
|
||||
axi_req_o.w_valid = 1'b1;
|
||||
// store-conditional requires exclusive access
|
||||
axi_req_o.aw.lock = amo_i == ariane_pkg::AMO_SC;
|
||||
// its a single write
|
||||
if (type_i == ariane_axi::SINGLE_REQ) begin
|
||||
// only a single write so the data is already the last one
|
||||
axi_req_o.w.last = 1'b1;
|
||||
// single req can be granted here
|
||||
gnt_o = axi_resp_i.aw_ready & axi_resp_i.w_ready;
|
||||
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
|
||||
2'b11: state_d = WAIT_B_VALID;
|
||||
2'b01: state_d = WAIT_AW_READY;
|
||||
2'b10: state_d = WAIT_LAST_W_READY;
|
||||
default: state_d = IDLE;
|
||||
endcase
|
||||
|
||||
if (axi_resp_i.aw_ready) begin
|
||||
amo_d = amo_i;
|
||||
size_d = size_i;
|
||||
end
|
||||
|
||||
// its a request for the whole cache line
|
||||
end else begin
|
||||
// bursts of AMOs unsupported
|
||||
assert (amo_i == ariane_pkg::AMO_NONE)
|
||||
else $fatal("Bursts of atomic operations are not supported");
|
||||
|
||||
axi_req_o.aw.len = BURST_SIZE; // number of bursts to do
|
||||
axi_req_o.w.data = wdata_i[0];
|
||||
axi_req_o.w.strb = be_i[0];
|
||||
|
||||
if (axi_resp_i.w_ready)
|
||||
cnt_d = BURST_SIZE - 1;
|
||||
else
|
||||
cnt_d = BURST_SIZE;
|
||||
|
||||
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
|
||||
2'b11: state_d = WAIT_LAST_W_READY;
|
||||
2'b01: state_d = WAIT_LAST_W_READY_AW_READY;
|
||||
2'b10: state_d = WAIT_LAST_W_READY;
|
||||
default:;
|
||||
endcase
|
||||
end
|
||||
// read
|
||||
end else begin
|
||||
|
||||
axi_req_o.ar_valid = 1'b1;
|
||||
// load-reserved requires exclusive access
|
||||
axi_req_o.ar.lock = amo_i == ariane_pkg::AMO_LR;
|
||||
|
||||
gnt_o = axi_resp_i.ar_ready;
|
||||
if (type_i != ariane_axi::SINGLE_REQ) begin
|
||||
assert (amo_i == ariane_pkg::AMO_NONE)
|
||||
else $fatal("Bursts of atomic operations are not supported");
|
||||
|
||||
axi_req_o.ar.len = BURST_SIZE;
|
||||
cnt_d = BURST_SIZE;
|
||||
end
|
||||
|
||||
if (axi_resp_i.ar_ready) begin
|
||||
state_d = (type_i == ariane_axi::SINGLE_REQ) ? WAIT_R_VALID : WAIT_R_VALID_MULTIPLE;
|
||||
addr_offset_d = addr_i[ADDR_INDEX-1+3:3];
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ~> from single write
|
||||
WAIT_AW_READY: begin
|
||||
axi_req_o.aw_valid = 1'b1;
|
||||
|
||||
if (axi_resp_i.aw_ready) begin
|
||||
gnt_o = 1'b1;
|
||||
state_d = WAIT_B_VALID;
|
||||
amo_d = amo_i;
|
||||
size_d = size_i;
|
||||
end
|
||||
end
|
||||
|
||||
// ~> we need to wait for an aw_ready and there is at least one outstanding write
|
||||
WAIT_LAST_W_READY_AW_READY: begin
|
||||
axi_req_o.w_valid = 1'b1;
|
||||
axi_req_o.w.last = (cnt_q == '0);
|
||||
if (type_i == ariane_axi::SINGLE_REQ) begin
|
||||
axi_req_o.w.data = wdata_i[0];
|
||||
axi_req_o.w.strb = be_i[0];
|
||||
end else begin
|
||||
axi_req_o.w.data = wdata_i[BURST_SIZE-cnt_q];
|
||||
axi_req_o.w.strb = be_i[BURST_SIZE-cnt_q];
|
||||
end
|
||||
axi_req_o.aw_valid = 1'b1;
|
||||
// we are here because we want to write a cache line
|
||||
axi_req_o.aw.len = BURST_SIZE;
|
||||
// we got an aw_ready
|
||||
case ({axi_resp_i.aw_ready, axi_resp_i.w_ready})
|
||||
// we got an aw ready
|
||||
2'b01: begin
|
||||
// are there any outstanding transactions?
|
||||
if (cnt_q == 0)
|
||||
state_d = WAIT_AW_READY_BURST;
|
||||
else // yes, so reduce the count and stay here
|
||||
cnt_d = cnt_q - 1;
|
||||
end
|
||||
2'b10: state_d = WAIT_LAST_W_READY;
|
||||
2'b11: begin
|
||||
// we are finished
|
||||
if (cnt_q == 0) begin
|
||||
state_d = WAIT_B_VALID;
|
||||
gnt_o = 1'b1;
|
||||
// there are outstanding transactions
|
||||
end else begin
|
||||
state_d = WAIT_LAST_W_READY;
|
||||
cnt_d = cnt_q - 1;
|
||||
end
|
||||
end
|
||||
default:;
|
||||
endcase
|
||||
|
||||
end
|
||||
|
||||
// ~> all data has already been sent, we are only waiting for the aw_ready
|
||||
WAIT_AW_READY_BURST: begin
|
||||
axi_req_o.aw_valid = 1'b1;
|
||||
axi_req_o.aw.len = BURST_SIZE;
|
||||
|
||||
if (axi_resp_i.aw_ready) begin
|
||||
state_d = WAIT_B_VALID;
|
||||
gnt_o = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// ~> from write, there is an outstanding write
|
||||
WAIT_LAST_W_READY: begin
|
||||
axi_req_o.w_valid = 1'b1;
|
||||
|
||||
if (type_i != ariane_axi::SINGLE_REQ) begin
|
||||
axi_req_o.w.data = wdata_i[BURST_SIZE-cnt_q];
|
||||
axi_req_o.w.strb = be_i[BURST_SIZE-cnt_q];
|
||||
end
|
||||
|
||||
// this is the last write
|
||||
if (cnt_q == '0) begin
|
||||
axi_req_o.w.last = 1'b1;
|
||||
if (axi_resp_i.w_ready) begin
|
||||
state_d = WAIT_B_VALID;
|
||||
gnt_o = 1'b1;
|
||||
end
|
||||
end else if (axi_resp_i.w_ready) begin
|
||||
cnt_d = cnt_q - 1;
|
||||
end
|
||||
end
|
||||
|
||||
// ~> finish write transaction
|
||||
WAIT_B_VALID: begin
|
||||
id_o = axi_resp_i.b.id;
|
||||
|
||||
// Write is valid
|
||||
if (axi_resp_i.b_valid) begin
|
||||
axi_req_o.b_ready = 1'b1;
|
||||
|
||||
// some atomics must wait for read data
|
||||
// we only accept it after accepting bvalid
|
||||
if (amo_returns_data(amo_q)) begin
|
||||
if (axi_resp_i.r_valid) begin
|
||||
// return read data if valid
|
||||
valid_o = 1'b1;
|
||||
axi_req_o.r_ready = 1'b1;
|
||||
state_d = IDLE;
|
||||
rdata_o = axi_resp_i.r.data;
|
||||
end else begin
|
||||
// wait otherwise
|
||||
state_d = WAIT_AMO_R_VALID;
|
||||
end
|
||||
end else begin
|
||||
valid_o = 1'b1;
|
||||
state_d = IDLE;
|
||||
|
||||
// store-conditional response
|
||||
if (amo_q == ariane_pkg::AMO_SC) begin
|
||||
if (axi_resp_i.b.resp == axi_pkg::RESP_EXOKAY) begin
|
||||
// success -> return 0
|
||||
rdata_o = 1'b0;
|
||||
end else begin
|
||||
// failure -> when request is 64-bit, return 1;
|
||||
// when request is 32-bit place a 1 in both upper
|
||||
// and lower half words. The right word will be
|
||||
// realigned/masked externally
|
||||
rdata_o = size_q == 2'b10 ? (1'b1 << 32) | 64'b1 : 64'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ~> some atomics wait for read data
|
||||
WAIT_AMO_R_VALID: begin
|
||||
// acknowledge data and terminate atomic
|
||||
if (axi_resp_i.r_valid) begin
|
||||
axi_req_o.r_ready = 1'b1;
|
||||
state_d = IDLE;
|
||||
valid_o = 1'b1;
|
||||
rdata_o = axi_resp_i.r.data;
|
||||
end
|
||||
end
|
||||
|
||||
// ~> cacheline read, single read
|
||||
WAIT_R_VALID_MULTIPLE, WAIT_R_VALID: begin
|
||||
if (CRITICAL_WORD_FIRST)
|
||||
index = addr_offset_q + (BURST_SIZE-cnt_q);
|
||||
else
|
||||
index = BURST_SIZE-cnt_q;
|
||||
|
||||
// reads are always wrapping here
|
||||
axi_req_o.r_ready = 1'b1;
|
||||
// this is the first read a.k.a the critical word
|
||||
if (axi_resp_i.r_valid) begin
|
||||
if (CRITICAL_WORD_FIRST) begin
|
||||
// this is the first word of a cacheline read, e.g.: the word which was causing the miss
|
||||
if (state_q == WAIT_R_VALID_MULTIPLE && cnt_q == BURST_SIZE) begin
|
||||
critical_word_valid_o = 1'b1;
|
||||
critical_word_o = axi_resp_i.r.data;
|
||||
end
|
||||
end else begin
|
||||
// check if the address offset matches - then we are getting the critical word
|
||||
if (index == addr_offset_q) begin
|
||||
critical_word_valid_o = 1'b1;
|
||||
critical_word_o = axi_resp_i.r.data;
|
||||
end
|
||||
end
|
||||
|
||||
// this is the last read
|
||||
if (axi_resp_i.r.last) begin
|
||||
id_d = axi_resp_i.r.id;
|
||||
state_d = COMPLETE_READ;
|
||||
end
|
||||
|
||||
// save the word
|
||||
if (state_q == WAIT_R_VALID_MULTIPLE) begin
|
||||
cache_line_d[index] = axi_resp_i.r.data;
|
||||
|
||||
end else
|
||||
cache_line_d[0] = axi_resp_i.r.data;
|
||||
|
||||
// Decrease the counter
|
||||
cnt_d = cnt_q - 1;
|
||||
end
|
||||
end
|
||||
// ~> read is complete
|
||||
COMPLETE_READ: begin
|
||||
valid_o = 1'b1;
|
||||
state_d = IDLE;
|
||||
id_o = id_q;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
// ----------------
|
||||
// Registers
|
||||
// ----------------
|
||||
always_ff @(posedge clk_i or negedge rst_ni) begin
|
||||
if (~rst_ni) begin
|
||||
// start in flushing state and initialize the memory
|
||||
state_q <= IDLE;
|
||||
cnt_q <= '0;
|
||||
cache_line_q <= '0;
|
||||
addr_offset_q <= '0;
|
||||
id_q <= '0;
|
||||
amo_q <= ariane_pkg::AMO_NONE;
|
||||
size_q <= '0;
|
||||
end else begin
|
||||
state_q <= state_d;
|
||||
cnt_q <= cnt_d;
|
||||
cache_line_q <= cache_line_d;
|
||||
addr_offset_q <= addr_offset_d;
|
||||
id_q <= id_d;
|
||||
amo_q <= amo_d;
|
||||
size_q <= size_d;
|
||||
end
|
||||
end
|
||||
|
||||
function automatic axi_pkg::atop_t atop_from_amo(ariane_pkg::amo_t amo);
|
||||
axi_pkg::atop_t result = 6'b000000;
|
||||
|
||||
unique case(amo)
|
||||
ariane_pkg::AMO_NONE: result = {axi_pkg::ATOP_NONE, 4'b0000};
|
||||
ariane_pkg::AMO_SWAP: result = {axi_pkg::ATOP_ATOMICSWAP};
|
||||
ariane_pkg::AMO_ADD : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD};
|
||||
ariane_pkg::AMO_AND : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR};
|
||||
ariane_pkg::AMO_OR : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET};
|
||||
ariane_pkg::AMO_XOR : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR};
|
||||
ariane_pkg::AMO_MAX : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX};
|
||||
ariane_pkg::AMO_MAXU: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX};
|
||||
ariane_pkg::AMO_MIN : result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN};
|
||||
ariane_pkg::AMO_MINU: result = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN};
|
||||
ariane_pkg::AMO_CAS1: result = {axi_pkg::ATOP_NONE, 4'b0000}; // Unsupported
|
||||
ariane_pkg::AMO_CAS2: result = {axi_pkg::ATOP_NONE, 4'b0000}; // Unsupported
|
||||
default: result = 6'b000000;
|
||||
endcase
|
||||
|
||||
return result;
|
||||
endfunction
|
||||
|
||||
function automatic logic amo_returns_data(ariane_pkg::amo_t amo);
|
||||
axi_pkg::atop_t atop = atop_from_amo(amo);
|
||||
logic is_load = atop[5:4] == axi_pkg::ATOP_ATOMICLOAD;
|
||||
logic is_swap_or_cmp = atop[5:4] == axi_pkg::ATOP_ATOMICSWAP[5:4];
|
||||
return is_load || is_swap_or_cmp;
|
||||
endfunction
|
||||
|
||||
endmodule
|
Loading…
Add table
Add a link
Reference in a new issue