// Copyright 2019 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. // // Authors: // - Wolfgang Roenninger <wroennin@iis.ee.ethz.ch> // - Andreas Kurth <akurth@iis.ee.ethz.ch> // - Matheus Cavalcante <matheusd@iis.ee.ethz.ch> // AXI Error Slave: This module always responds with an AXI error for transactions that are sent to // it. This module optionally supports ATOPs if the `ATOPs` parameter is set. module axi_err_slv #( parameter int unsigned AxiIdWidth = 0, // AXI ID Width parameter type req_t = logic, // AXI 4 request struct, with atop field parameter type resp_t = logic, // AXI 4 response struct parameter axi_pkg::resp_t Resp = axi_pkg::RESP_DECERR, // Error generated by this slave. parameter int unsigned RespWidth = 32'd64, // Data response width, gets zero extended or truncated to r.data. parameter logic [RespWidth-1:0] RespData = 64'hCA11AB1EBADCAB1E, // Hexvalue for data return value parameter bit ATOPs = 1'b1, // Activate support for ATOPs. Set to 1 if this slave could ever get an atomic AXI transaction. parameter int unsigned MaxTrans = 1 // Maximum # of accepted transactions before stalling ) ( input logic clk_i, // Clock input logic rst_ni, // Asynchronous reset active low input logic test_i, // Testmode enable // slave port input req_t slv_req_i, output resp_t slv_resp_o ); typedef logic [AxiIdWidth-1:0] id_t; typedef struct packed { id_t id; axi_pkg::len_t len; } r_data_t; req_t err_req; resp_t err_resp; if (ATOPs) begin axi_atop_filter #( .AxiIdWidth ( AxiIdWidth ), .AxiMaxWriteTxns ( MaxTrans ), .req_t ( req_t ), .resp_t ( resp_t ) ) i_atop_filter ( .clk_i, .rst_ni, .slv_req_i ( slv_req_i ), .slv_resp_o ( slv_resp_o ), .mst_req_o ( err_req ), .mst_resp_i ( err_resp ) ); end else begin assign err_req = slv_req_i; assign slv_resp_o = err_resp; end // w fifo logic w_fifo_full, w_fifo_empty; logic w_fifo_push, w_fifo_pop; id_t w_fifo_data; // b fifo logic b_fifo_full, b_fifo_empty; logic b_fifo_push, b_fifo_pop; id_t b_fifo_data; // r fifo r_data_t r_fifo_inp; logic r_fifo_full, r_fifo_empty; logic r_fifo_push, r_fifo_pop; r_data_t r_fifo_data; // r counter logic r_cnt_clear, r_cnt_en, r_cnt_load; axi_pkg::len_t r_current_beat; // r status logic r_busy_d, r_busy_q, r_busy_load; //-------------------------------------- // Write Transactions //-------------------------------------- // push, when there is room in the fifo assign w_fifo_push = err_req.aw_valid & ~w_fifo_full; assign err_resp.aw_ready = ~w_fifo_full; fifo_v3 #( .FALL_THROUGH ( 1'b1 ), .DEPTH ( MaxTrans ), .dtype ( id_t ) ) i_w_fifo ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( 1'b0 ), .testmode_i ( test_i ), .full_o ( w_fifo_full ), .empty_o ( w_fifo_empty ), .usage_o ( ), .data_i ( err_req.aw.id ), .push_i ( w_fifo_push ), .data_o ( w_fifo_data ), .pop_i ( w_fifo_pop ) ); always_comb begin : proc_w_channel err_resp.w_ready = 1'b0; w_fifo_pop = 1'b0; b_fifo_push = 1'b0; if (!w_fifo_empty && !b_fifo_full) begin // eat the beats err_resp.w_ready = 1'b1; // on the last w transaction if (err_req.w_valid && err_req.w.last) begin w_fifo_pop = 1'b1; b_fifo_push = 1'b1; end end end fifo_v3 #( .FALL_THROUGH ( 1'b0 ), .DEPTH ( unsigned'(2) ), // two placed so that w can eat beats if b is not sent .dtype ( id_t ) ) i_b_fifo ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( 1'b0 ), .testmode_i ( test_i ), .full_o ( b_fifo_full ), .empty_o ( b_fifo_empty ), .usage_o ( ), .data_i ( w_fifo_data ), .push_i ( b_fifo_push ), .data_o ( b_fifo_data ), .pop_i ( b_fifo_pop ) ); always_comb begin : proc_b_channel b_fifo_pop = 1'b0; err_resp.b = '0; err_resp.b.id = b_fifo_data; err_resp.b.resp = Resp; err_resp.b_valid = 1'b0; if (!b_fifo_empty) begin err_resp.b_valid = 1'b1; // b transaction b_fifo_pop = err_req.b_ready; end end //-------------------------------------- // Read Transactions //-------------------------------------- // push if there is room in the fifo assign r_fifo_push = err_req.ar_valid & ~r_fifo_full; assign err_resp.ar_ready = ~r_fifo_full; // fifo data assignment assign r_fifo_inp.id = err_req.ar.id; assign r_fifo_inp.len = err_req.ar.len; fifo_v3 #( .FALL_THROUGH ( 1'b0 ), .DEPTH ( MaxTrans ), .dtype ( r_data_t ) ) i_r_fifo ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .flush_i ( 1'b0 ), .testmode_i( test_i ), .full_o ( r_fifo_full ), .empty_o ( r_fifo_empty ), .usage_o ( ), .data_i ( r_fifo_inp ), .push_i ( r_fifo_push ), .data_o ( r_fifo_data ), .pop_i ( r_fifo_pop ) ); always_comb begin : proc_r_channel // default assignments r_busy_d = r_busy_q; r_busy_load = 1'b0; // r fifo signals r_fifo_pop = 1'b0; // r counter signals r_cnt_clear = 1'b0; r_cnt_en = 1'b0; r_cnt_load = 1'b0; // r_channel err_resp.r = '0; err_resp.r.id = r_fifo_data.id; err_resp.r.data = RespData; err_resp.r.resp = Resp; err_resp.r_valid = 1'b0; // control if (r_busy_q) begin err_resp.r_valid = 1'b1; err_resp.r.last = (r_current_beat == '0); // r transaction if (err_req.r_ready) begin r_cnt_en = 1'b1; if (r_current_beat == '0) begin r_busy_d = 1'b0; r_busy_load = 1'b1; r_cnt_clear = 1'b1; r_fifo_pop = 1'b1; end end end else begin // when not busy and fifo not empty, start counter err gen if (!r_fifo_empty) begin r_busy_d = 1'b1; r_busy_load = 1'b1; r_cnt_load = 1'b1; end end end always_ff @(posedge clk_i, negedge rst_ni) begin if (!rst_ni) begin r_busy_q <= '0; end else if (r_busy_load) begin r_busy_q <= r_busy_d; end end counter #( .WIDTH ($bits(axi_pkg::len_t)) ) i_r_counter ( .clk_i ( clk_i ), .rst_ni ( rst_ni ), .clear_i ( r_cnt_clear ), .en_i ( r_cnt_en ), .load_i ( r_cnt_load ), .down_i ( 1'b1 ), .d_i ( r_fifo_data.len ), .q_o ( r_current_beat ), .overflow_o( ) ); // pragma translate_off `ifndef VERILATOR `ifndef XSIM initial begin assert (Resp == axi_pkg::RESP_DECERR || Resp == axi_pkg::RESP_SLVERR) else $fatal(1, "This module may only generate RESP_DECERR or RESP_SLVERR responses!"); end default disable iff (!rst_ni); if (!ATOPs) begin : gen_assert_atops_unsupported assume property( @(posedge clk_i) (slv_req_i.aw_valid |-> slv_req_i.aw.atop == '0)) else $fatal(1, "Got ATOP but not configured to support ATOPs!"); end `endif `endif // pragma translate_on endmodule