Remove redundant axi_adapter.sv (#1174)

This module was moved to `core/cache_subsystem/axi_adapter.sv`
in #1127.
This commit is contained in:
Nils Wistoff 2023-04-09 08:46:35 +02:00 committed by GitHub
parent e51eec2904
commit 09ed2de0df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 1 additions and 456 deletions

View file

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

View file

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