// 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: 17/07/2017 // Description: AXI Lite compatible interface // module axi_lite_interface #( parameter int unsigned AXI_ADDR_WIDTH = 64, parameter int unsigned AXI_DATA_WIDTH = 64, parameter int unsigned AXI_ID_WIDTH = 10 )( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input ariane_axi::req_t axi_req_i, output ariane_axi::resp_t axi_resp_o, output logic [AXI_ADDR_WIDTH-1:0] address_o, output logic en_o, // transaction is valid output logic we_o, // write input logic [AXI_DATA_WIDTH-1:0] data_i, // data output logic [AXI_DATA_WIDTH-1:0] data_o ); // The RLAST signal is not required, and is considered asserted for every transfer on the read data channel. enum logic [1:0] { IDLE, READ, WRITE, WRITE_B} CS, NS; // save the trans id, we will need it for reflection otherwise we are not plug compatible to the AXI standard logic [AXI_ID_WIDTH-1:0] trans_id_n, trans_id_q; // address register logic [AXI_ADDR_WIDTH-1:0] address_n, address_q; // pass through read data on the read data channel assign axi_resp_o.r.data = data_i; // send back the transaction id we've latched assign axi_resp_o.r.id = trans_id_q; assign axi_resp_o.b.id = trans_id_q; // set r_last to one as defined by the AXI4 - Lite standard assign axi_resp_o.r.last = 1'b1; // we do not support any errors so set response flag to all zeros assign axi_resp_o.b.resp = 2'b0; assign axi_resp_o.r.resp = 2'b0; // output data which we want to write to the slave assign data_o = axi_req_i.w.data; // ------------------------ // AXI4-Lite State Machine // ------------------------ always_comb begin // default signal assignment NS = CS; address_n = address_q; trans_id_n = trans_id_q; // we'll answer a write request only if we got address and data axi_resp_o.aw_ready = 1'b0; axi_resp_o.w_ready = 1'b0; axi_resp_o.b_valid = 1'b0; axi_resp_o.ar_ready = 1'b1; axi_resp_o.r_valid = 1'b0; address_o = '0; we_o = 1'b0; en_o = 1'b0; case (CS) // we are ready to accept a new request IDLE: begin // we've git a valid write request, we also know that we have asserted the aw_ready if (axi_req_i.aw_valid) begin axi_resp_o.aw_ready = 1'b1; // this costs performance but the interconnect does not obey the AXI standard NS = WRITE; // save address address_n = axi_req_i.aw.addr; // save the transaction id for reflection trans_id_n = axi_req_i.aw.id; // we've got a valid read request, we also know that we have asserted the ar_ready end else if (axi_req_i.ar_valid) begin NS = READ; address_n = axi_req_i.ar.addr; // also request the word from the memory-like interface address_o = axi_req_i.ar.addr; // save the transaction id for reflection trans_id_n = axi_req_i.ar.id; end end // We've got a read request at least one cycle earlier // so data_i will already contain the data we'd like tor read READ: begin // enable the ram-like en_o = 1'b1; // we are not ready for another request here axi_resp_o.ar_ready = 1'b0; // further assert the correct address address_o = address_q; // the read is valid axi_resp_o.r_valid = 1'b1; // check if we got a valid r_ready and go back to IDLE if (axi_req_i.r_ready) NS = IDLE; end // We've got a write request at least one cycle earlier // wait here for the data WRITE: begin if (axi_req_i.w_valid) begin // we are not ready for another request here axi_resp_o.ar_ready = 1'b0; axi_resp_o.w_ready = 1'b1; // use the latched address address_o = address_q; en_o = 1'b1; we_o = 1'b1; // close this request NS = WRITE_B; end end WRITE_B: begin axi_resp_o.b_valid = 1'b1; // we've already performed the write here so wait for the ready signal if (axi_req_i.b_ready) NS = IDLE; end default:; endcase end // ------------------------ // Registers // ------------------------ always_ff @(posedge clk_i or negedge rst_ni) begin if (~rst_ni) begin CS <= IDLE; address_q <= '0; trans_id_q <= '0; end else begin CS <= NS; address_q <= address_n; trans_id_q <= trans_id_n; end end // ------------------------ // Assertions // ------------------------ // Listen for illegal transactions //pragma translate_off `ifndef VERILATOR // check that burst length is just one assert property (@(posedge clk_i) axi_req_i.ar_valid |-> ((axi_req_i.ar.len == 8'b0))) else begin $error("AXI Lite does not support bursts larger than 1 or byte length unequal to the native bus size"); $stop(); end // do the same for the write channel assert property (@(posedge clk_i) axi_req_i.aw_valid |-> ((axi_req_i.aw.len == 8'b0))) else begin $error("AXI Lite does not support bursts larger than 1 or byte length unequal to the native bus size"); $stop(); end `endif //pragma translate_on endmodule