mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-19 03:44:46 -04:00
310 lines
11 KiB
Systemverilog
310 lines
11 KiB
Systemverilog
/* 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_shim.sv
|
|
* Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>
|
|
* Florian Zaruba <zarubaf@iis.ee.ethz.ch>
|
|
* Date: 1.8.2018
|
|
*
|
|
* Description: Manages communication with the AXI Bus. Note that this unit does not
|
|
* buffer requests and register the signals.
|
|
*
|
|
*/
|
|
|
|
|
|
module axi_shim #(
|
|
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
|
|
parameter int unsigned AxiNumWords = 4, // data width in dwords, this is also the maximum burst length, must be >=2
|
|
parameter type axi_req_t = logic,
|
|
parameter type axi_rsp_t = logic
|
|
) (
|
|
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 [CVA6Cfg.AxiAddrWidth-1:0] rd_addr_i,
|
|
input logic [$clog2(AxiNumWords)-1:0] rd_blen_i, // axi convention: LEN-1
|
|
input logic [2:0] rd_size_i,
|
|
input logic [CVA6Cfg.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 [CVA6Cfg.AxiDataWidth-1:0] rd_data_o,
|
|
output logic [CVA6Cfg.AxiUserWidth-1:0] rd_user_o,
|
|
output logic [CVA6Cfg.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 [CVA6Cfg.AxiAddrWidth-1:0] wr_addr_i,
|
|
input logic [AxiNumWords-1:0][CVA6Cfg.AxiDataWidth-1:0] wr_data_i,
|
|
input logic [AxiNumWords-1:0][CVA6Cfg.AxiUserWidth-1:0] wr_user_i,
|
|
input logic [AxiNumWords-1:0][(CVA6Cfg.AxiDataWidth/8)-1:0] wr_be_i,
|
|
input logic [$clog2(AxiNumWords)-1:0] wr_blen_i, // axi convention: LEN-1
|
|
input logic [2:0] wr_size_i,
|
|
input logic [CVA6Cfg.AxiIdWidth-1:0] wr_id_i,
|
|
input logic wr_lock_i,
|
|
input logic [5:0] wr_atop_i,
|
|
// write response
|
|
input logic wr_rdy_i,
|
|
output logic wr_valid_o,
|
|
output logic [CVA6Cfg.AxiIdWidth-1:0] wr_id_o,
|
|
output logic wr_exokay_o, // indicates whether exclusive tx succeeded
|
|
// AXI port
|
|
output axi_req_t axi_req_o,
|
|
input axi_rsp_t axi_resp_i
|
|
);
|
|
localparam AddrIndex = ($clog2(AxiNumWords) > 0) ? $clog2(AxiNumWords) : 1;
|
|
|
|
///////////////////////////////////////////////////////
|
|
// write channel
|
|
///////////////////////////////////////////////////////
|
|
|
|
enum logic [3:0] {
|
|
IDLE,
|
|
WAIT_AW_READY,
|
|
WAIT_LAST_W_READY,
|
|
WAIT_LAST_W_READY_AW_READY,
|
|
WAIT_AW_READY_BURST
|
|
}
|
|
wr_state_q, wr_state_d;
|
|
|
|
// AXI tx counter
|
|
logic [AddrIndex-1:0] wr_cnt_d, wr_cnt_q;
|
|
logic wr_single_req, wr_cnt_done, wr_cnt_clr, wr_cnt_en;
|
|
|
|
assign wr_single_req = (wr_blen_i == 0);
|
|
|
|
// 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[CVA6Cfg.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;
|
|
assign axi_req_o.aw.prot = 3'b0;
|
|
assign axi_req_o.aw.region = 4'b0;
|
|
assign axi_req_o.aw.lock = wr_lock_i;
|
|
assign axi_req_o.aw.cache = axi_pkg::CACHE_MODIFIABLE;
|
|
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];
|
|
assign axi_req_o.w.strb = wr_be_i[wr_cnt_q];
|
|
assign axi_req_o.w.last = wr_cnt_done;
|
|
|
|
// write response
|
|
assign wr_exokay_o = (axi_resp_i.b.resp == axi_pkg::RESP_EXOKAY);
|
|
assign axi_req_o.b_ready = wr_rdy_i;
|
|
assign wr_valid_o = axi_resp_i.b_valid;
|
|
assign wr_id_o = axi_resp_i.b.id;
|
|
|
|
// tx counter
|
|
assign wr_cnt_done = (wr_cnt_q == wr_blen_i);
|
|
assign wr_cnt_d = (wr_cnt_clr) ? '0 : (wr_cnt_en && CVA6Cfg.AxiBurstWriteEn) ? wr_cnt_q + 1 : wr_cnt_q;
|
|
|
|
always_comb begin : p_axi_write_fsm
|
|
// default
|
|
wr_state_d = wr_state_q;
|
|
|
|
axi_req_o.aw_valid = 1'b0;
|
|
axi_req_o.w_valid = 1'b0;
|
|
wr_gnt_o = 1'b0;
|
|
|
|
wr_cnt_en = 1'b0;
|
|
wr_cnt_clr = 1'b0;
|
|
|
|
case (wr_state_q)
|
|
///////////////////////////////////
|
|
IDLE: begin
|
|
// we have an incoming request
|
|
if (wr_req_i) begin
|
|
// is this a read or write?
|
|
axi_req_o.aw_valid = 1'b1;
|
|
axi_req_o.w_valid = 1'b1;
|
|
|
|
if (CVA6Cfg.AxiBurstWriteEn && !wr_single_req) begin
|
|
wr_cnt_en = axi_resp_i.w_ready;
|
|
|
|
case ({
|
|
axi_resp_i.aw_ready, axi_resp_i.w_ready
|
|
})
|
|
2'b11: wr_state_d = WAIT_LAST_W_READY;
|
|
2'b01: wr_state_d = WAIT_LAST_W_READY_AW_READY;
|
|
2'b10: wr_state_d = WAIT_LAST_W_READY;
|
|
default: ;
|
|
endcase
|
|
end else if (wr_single_req) begin // its a single write
|
|
wr_cnt_clr = 1'b1;
|
|
// single req can be granted here
|
|
wr_gnt_o = axi_resp_i.aw_ready & axi_resp_i.w_ready;
|
|
case ({
|
|
axi_resp_i.aw_ready, axi_resp_i.w_ready
|
|
})
|
|
2'b01: wr_state_d = WAIT_AW_READY;
|
|
2'b10: wr_state_d = WAIT_LAST_W_READY;
|
|
default: wr_state_d = IDLE;
|
|
endcase
|
|
// its a request for the whole cache line
|
|
end
|
|
end
|
|
end
|
|
///////////////////////////////////
|
|
// ~> from single write
|
|
WAIT_AW_READY: begin
|
|
axi_req_o.aw_valid = 1'b1;
|
|
|
|
if (axi_resp_i.aw_ready) begin
|
|
wr_state_d = IDLE;
|
|
wr_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 (CVA6Cfg.AxiBurstWriteEn && axi_resp_i.w_ready && !wr_cnt_done) begin
|
|
wr_cnt_en = 1'b1;
|
|
end else if (wr_cnt_done) begin // this is the last write
|
|
if (axi_resp_i.w_ready) begin
|
|
wr_state_d = IDLE;
|
|
wr_cnt_clr = 1'b1;
|
|
wr_gnt_o = 1'b1;
|
|
end
|
|
end
|
|
end
|
|
///////////////////////////////////
|
|
default: begin
|
|
///////////////////////////////////
|
|
// ~> we need to wait for an aw_ready and there is at least one outstanding write
|
|
if (CVA6Cfg.AxiBurstWriteEn) begin
|
|
if (wr_state_q == WAIT_LAST_W_READY_AW_READY) begin
|
|
axi_req_o.w_valid = 1'b1;
|
|
axi_req_o.aw_valid = 1'b1;
|
|
// 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 (wr_cnt_done) begin
|
|
wr_state_d = WAIT_AW_READY_BURST;
|
|
wr_cnt_clr = 1'b1;
|
|
end else begin
|
|
// yes, so reduce the count and stay here
|
|
wr_cnt_en = 1'b1;
|
|
end
|
|
end
|
|
2'b10: wr_state_d = WAIT_LAST_W_READY;
|
|
2'b11: begin
|
|
// we are finished
|
|
if (wr_cnt_done) begin
|
|
wr_state_d = IDLE;
|
|
wr_gnt_o = 1'b1;
|
|
wr_cnt_clr = 1'b1;
|
|
// there are outstanding transactions
|
|
end else begin
|
|
wr_state_d = WAIT_LAST_W_READY;
|
|
wr_cnt_en = 1'b1;
|
|
end
|
|
end
|
|
default: ;
|
|
endcase
|
|
end ///////////////////////////////////
|
|
// ~> all data has already been sent, we are only waiting for the aw_ready
|
|
else if (wr_state_q == WAIT_AW_READY_BURST) begin
|
|
axi_req_o.aw_valid = 1'b1;
|
|
|
|
if (axi_resp_i.aw_ready) begin
|
|
wr_state_d = IDLE;
|
|
wr_gnt_o = 1'b1;
|
|
end
|
|
end
|
|
end else begin
|
|
wr_state_d = IDLE;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
|
|
|
|
///////////////////////////////////////////////////////
|
|
// read channel
|
|
///////////////////////////////////////////////////////
|
|
|
|
// address
|
|
// 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[CVA6Cfg.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;
|
|
assign axi_req_o.ar.prot = 3'b0;
|
|
assign axi_req_o.ar.region = 4'b0;
|
|
assign axi_req_o.ar.lock = rd_lock_i;
|
|
assign axi_req_o.ar.cache = axi_pkg::CACHE_MODIFIABLE;
|
|
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;
|
|
assign rd_gnt_o = rd_req_i & axi_resp_i.ar_ready;
|
|
|
|
// return path
|
|
assign axi_req_o.r_ready = rd_rdy_i;
|
|
assign rd_data_o = axi_resp_i.r.data;
|
|
if (CVA6Cfg.AXI_USER_EN) begin
|
|
assign rd_user_o = axi_resp_i.r.user;
|
|
end else begin
|
|
assign rd_user_o = '0;
|
|
end
|
|
assign rd_last_o = axi_resp_i.r.last;
|
|
assign rd_valid_o = axi_resp_i.r_valid;
|
|
assign rd_id_o = axi_resp_i.r.id;
|
|
assign rd_exokay_o = (axi_resp_i.r.resp == axi_pkg::RESP_EXOKAY);
|
|
|
|
|
|
// ----------------
|
|
// Registers
|
|
// ----------------
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (~rst_ni) begin
|
|
// start in flushing state and initialize the memory
|
|
wr_state_q <= IDLE;
|
|
wr_cnt_q <= '0;
|
|
end else begin
|
|
wr_state_q <= wr_state_d;
|
|
wr_cnt_q <= wr_cnt_d;
|
|
end
|
|
end
|
|
|
|
// ----------------
|
|
// Assertions
|
|
// ----------------
|
|
|
|
//pragma translate_off
|
|
initial begin
|
|
assert (AxiNumWords >= 1)
|
|
else $fatal(1, "[axi adapter] AxiNumWords must be >= 1");
|
|
assert (CVA6Cfg.AxiIdWidth >= 2)
|
|
else $fatal(1, "[axi adapter] AXI id width must be at least 2 bit wide");
|
|
end
|
|
//pragma translate_on
|
|
|
|
endmodule // axi_adapter2
|