// Copyright (c) 2014-2020 ETH Zurich, 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. // // Authors: // - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch> // - Andreas Kurth <akurth@iis.ee.ethz.ch> // - Fabian Schuiki <fschuiki@iis.ee.ethz.ch> // - Florian Zaruba <zarubaf@iis.ee.ethz.ch> /// An AXI4+ATOP to AXI4-Lite converter with atomic transaction and burst support. module axi_to_axi_lite #( parameter int unsigned AxiAddrWidth = 32'd0, parameter int unsigned AxiDataWidth = 32'd0, parameter int unsigned AxiIdWidth = 32'd0, parameter int unsigned AxiUserWidth = 32'd0, parameter int unsigned AxiMaxWriteTxns = 32'd0, parameter int unsigned AxiMaxReadTxns = 32'd0, parameter bit FallThrough = 1'b1, // FIFOs in Fall through mode in ID reflect parameter type full_req_t = logic, parameter type full_resp_t = logic, parameter type lite_req_t = logic, parameter type lite_resp_t = logic ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input logic test_i, // Testmode enable // slave port full AXI4+ATOP input full_req_t slv_req_i, output full_resp_t slv_resp_o, // master port AXI4-Lite output lite_req_t mst_req_o, input lite_resp_t mst_resp_i ); // full bus declarations full_req_t filtered_req, splitted_req; full_resp_t filtered_resp, splitted_resp; // atomics adapter so that atomics can be resolved axi_atop_filter #( .AxiIdWidth ( AxiIdWidth ), .AxiMaxWriteTxns ( AxiMaxWriteTxns ), .req_t ( full_req_t ), .resp_t ( full_resp_t ) ) i_axi_atop_filter( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .slv_req_i ( slv_req_i ), .slv_resp_o ( slv_resp_o ), .mst_req_o ( filtered_req ), .mst_resp_i ( filtered_resp ) ); // burst splitter so that the id reflect module has no burst accessing it axi_burst_splitter #( .MaxReadTxns ( AxiMaxReadTxns ), .MaxWriteTxns ( AxiMaxWriteTxns ), .AddrWidth ( AxiAddrWidth ), .DataWidth ( AxiDataWidth ), .IdWidth ( AxiIdWidth ), .UserWidth ( AxiUserWidth ), .req_t ( full_req_t ), .resp_t ( full_resp_t ) ) i_axi_burst_splitter ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .slv_req_i ( filtered_req ), .slv_resp_o ( filtered_resp ), .mst_req_o ( splitted_req ), .mst_resp_i ( splitted_resp ) ); // ID reflect module handles the conversion from the full AXI to AXI lite on the wireing axi_to_axi_lite_id_reflect #( .AxiIdWidth ( AxiIdWidth ), .AxiMaxWriteTxns ( AxiMaxWriteTxns ), .AxiMaxReadTxns ( AxiMaxReadTxns ), .FallThrough ( FallThrough ), .full_req_t ( full_req_t ), .full_resp_t ( full_resp_t ), .lite_req_t ( lite_req_t ), .lite_resp_t ( lite_resp_t ) ) i_axi_to_axi_lite_id_reflect ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .test_i ( test_i ), .slv_req_i ( splitted_req ), .slv_resp_o ( splitted_resp ), .mst_req_o ( mst_req_o ), .mst_resp_i ( mst_resp_i ) ); // Assertions, check params // pragma translate_off `ifndef VERILATOR initial begin assume (AxiIdWidth > 0) else $fatal(1, "AXI ID width has to be > 0"); assume (AxiAddrWidth > 0) else $fatal(1, "AXI address width has to be > 0"); assume (AxiDataWidth > 0) else $fatal(1, "AXI data width has to be > 0"); end `endif // pragma translate_on endmodule // Description: This module does the translation of the full AXI4+ATOP to AXI4-Lite signals. // It reflects the ID of the incoming transaction and crops all signals not used // in AXI4-Lite. It requires that incoming AXI4+ATOP transactions have a // `axi_pkg::len_t` of `'0` and an `axi_pkg::atop_t` of `'0`. module axi_to_axi_lite_id_reflect #( parameter int unsigned AxiIdWidth = 32'd0, parameter int unsigned AxiMaxWriteTxns = 32'd0, parameter int unsigned AxiMaxReadTxns = 32'd0, parameter bit FallThrough = 1'b1, // FIFOs in fall through mode parameter type full_req_t = logic, parameter type full_resp_t = logic, parameter type lite_req_t = logic, parameter type lite_resp_t = logic ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input logic test_i, // Testmode enable // slave port full AXI input full_req_t slv_req_i, output full_resp_t slv_resp_o, // master port AXI LITE output lite_req_t mst_req_o, input lite_resp_t mst_resp_i ); typedef logic [AxiIdWidth-1:0] id_t; // FIFO status and control signals logic aw_full, aw_empty, aw_push, aw_pop, ar_full, ar_empty, ar_push, ar_pop; id_t aw_reflect_id, ar_reflect_id; assign slv_resp_o = '{ aw_ready: mst_resp_i.aw_ready & ~aw_full, w_ready: mst_resp_i.w_ready, b: '{ id: aw_reflect_id, resp: mst_resp_i.b.resp, default: '0 }, b_valid: mst_resp_i.b_valid & ~aw_empty, ar_ready: mst_resp_i.ar_ready & ~ar_full, r: '{ id: ar_reflect_id, data: mst_resp_i.r.data, resp: mst_resp_i.r.resp, last: 1'b1, default: '0 }, r_valid: mst_resp_i.r_valid & ~ar_empty, default: '0 }; // Write ID reflection assign aw_push = mst_req_o.aw_valid & slv_resp_o.aw_ready; assign aw_pop = slv_resp_o.b_valid & mst_req_o.b_ready; fifo_v3 #( .FALL_THROUGH ( FallThrough ), .DEPTH ( AxiMaxWriteTxns ), .dtype ( id_t ) ) i_aw_id_fifo ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( 1'b0 ), .testmode_i( test_i ), .full_o ( aw_full ), .empty_o ( aw_empty ), .usage_o ( /*not used*/ ), .data_i ( slv_req_i.aw.id ), .push_i ( aw_push ), .data_o ( aw_reflect_id ), .pop_i ( aw_pop ) ); // Read ID reflection assign ar_push = mst_req_o.ar_valid & slv_resp_o.ar_ready; assign ar_pop = slv_resp_o.r_valid & mst_req_o.r_ready; fifo_v3 #( .FALL_THROUGH ( FallThrough ), .DEPTH ( AxiMaxReadTxns ), .dtype ( id_t ) ) i_ar_id_fifo ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( 1'b0 ), .testmode_i( test_i ), .full_o ( ar_full ), .empty_o ( ar_empty ), .usage_o ( /*not used*/ ), .data_i ( slv_req_i.ar.id ), .push_i ( ar_push ), .data_o ( ar_reflect_id ), .pop_i ( ar_pop ) ); assign mst_req_o = '{ aw: '{ addr: slv_req_i.aw.addr, prot: slv_req_i.aw.prot }, aw_valid: slv_req_i.aw_valid & ~aw_full, w: '{ data: slv_req_i.w.data, strb: slv_req_i.w.strb }, w_valid: slv_req_i.w_valid, b_ready: slv_req_i.b_ready & ~aw_empty, ar: '{ addr: slv_req_i.ar.addr, prot: slv_req_i.ar.prot }, ar_valid: slv_req_i.ar_valid & ~ar_full, r_ready: slv_req_i.r_ready & ~ar_empty, default: '0 }; // Assertions // pragma translate_off `ifndef VERILATOR aw_atop: assume property( @(posedge clk_i) disable iff (~rst_ni) slv_req_i.aw_valid |-> (slv_req_i.aw.atop == '0)) else $fatal(1, "Module does not support atomics. Value observed: %0b", slv_req_i.aw.atop); aw_axi_len: assume property( @(posedge clk_i) disable iff (~rst_ni) slv_req_i.aw_valid |-> (slv_req_i.aw.len == '0)) else $fatal(1, "AW request length has to be zero. Value observed: %0b", slv_req_i.aw.len); w_axi_last: assume property( @(posedge clk_i) disable iff (~rst_ni) slv_req_i.w_valid |-> (slv_req_i.w.last == 1'b1)) else $fatal(1, "W last signal has to be one. Value observed: %0b", slv_req_i.w.last); ar_axi_len: assume property( @(posedge clk_i) disable iff (~rst_ni) slv_req_i.ar_valid |-> (slv_req_i.ar.len == '0)) else $fatal(1, "AR request length has to be zero. Value observed: %0b", slv_req_i.ar.len); `endif // pragma translate_on endmodule // interface wrapper `include "axi/assign.svh" `include "axi/typedef.svh" module axi_to_axi_lite_intf #( /// AXI bus parameters parameter int unsigned AXI_ADDR_WIDTH = 32'd0, parameter int unsigned AXI_DATA_WIDTH = 32'd0, parameter int unsigned AXI_ID_WIDTH = 32'd0, parameter int unsigned AXI_USER_WIDTH = 32'd0, /// Maximum number of outstanding writes. parameter int unsigned AXI_MAX_WRITE_TXNS = 32'd1, /// Maximum number of outstanding reads. parameter int unsigned AXI_MAX_READ_TXNS = 32'd1, parameter bit FALL_THROUGH = 1'b1 ) ( input logic clk_i, input logic rst_ni, input logic testmode_i, AXI_BUS.Slave slv, AXI_LITE.Master mst ); typedef logic [AXI_ADDR_WIDTH-1:0] addr_t; typedef logic [AXI_DATA_WIDTH-1:0] data_t; typedef logic [AXI_ID_WIDTH-1:0] id_t; typedef logic [AXI_DATA_WIDTH/8-1:0] strb_t; typedef logic [AXI_USER_WIDTH-1:0] user_t; // full channels typedefs `AXI_TYPEDEF_AW_CHAN_T(full_aw_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_W_CHAN_T(full_w_chan_t, data_t, strb_t, user_t) `AXI_TYPEDEF_B_CHAN_T(full_b_chan_t, id_t, user_t) `AXI_TYPEDEF_AR_CHAN_T(full_ar_chan_t, addr_t, id_t, user_t) `AXI_TYPEDEF_R_CHAN_T(full_r_chan_t, data_t, id_t, user_t) `AXI_TYPEDEF_REQ_T(full_req_t, full_aw_chan_t, full_w_chan_t, full_ar_chan_t) `AXI_TYPEDEF_RESP_T(full_resp_t, full_b_chan_t, full_r_chan_t) // LITE channels typedef `AXI_LITE_TYPEDEF_AW_CHAN_T(lite_aw_chan_t, addr_t) `AXI_LITE_TYPEDEF_W_CHAN_T(lite_w_chan_t, data_t, strb_t) `AXI_LITE_TYPEDEF_B_CHAN_T(lite_b_chan_t) `AXI_LITE_TYPEDEF_AR_CHAN_T(lite_ar_chan_t, addr_t) `AXI_LITE_TYPEDEF_R_CHAN_T (lite_r_chan_t, data_t) `AXI_LITE_TYPEDEF_REQ_T(lite_req_t, lite_aw_chan_t, lite_w_chan_t, lite_ar_chan_t) `AXI_LITE_TYPEDEF_RESP_T(lite_resp_t, lite_b_chan_t, lite_r_chan_t) full_req_t full_req; full_resp_t full_resp; lite_req_t lite_req; lite_resp_t lite_resp; `AXI_ASSIGN_TO_REQ(full_req, slv) `AXI_ASSIGN_FROM_RESP(slv, full_resp) `AXI_LITE_ASSIGN_FROM_REQ(mst, lite_req) `AXI_LITE_ASSIGN_TO_RESP(lite_resp, mst) axi_to_axi_lite #( .AxiAddrWidth ( AXI_ADDR_WIDTH ), .AxiDataWidth ( AXI_DATA_WIDTH ), .AxiIdWidth ( AXI_ID_WIDTH ), .AxiUserWidth ( AXI_USER_WIDTH ), .AxiMaxWriteTxns ( AXI_MAX_WRITE_TXNS ), .AxiMaxReadTxns ( AXI_MAX_READ_TXNS ), .FallThrough ( FALL_THROUGH ), // FIFOs in Fall through mode in ID reflect .full_req_t ( full_req_t ), .full_resp_t ( full_resp_t ), .lite_req_t ( lite_req_t ), .lite_resp_t ( lite_resp_t ) ) i_axi_to_axi_lite ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .test_i ( testmode_i ), // slave port full AXI4+ATOP .slv_req_i ( full_req ), .slv_resp_o ( full_resp ), // master port AXI4-Lite .mst_req_o ( lite_req ), .mst_resp_i ( lite_resp ) ); endmodule