//////////////////////////////////////////////////////////////////////////////// // // Filename: axilsafety.v // {{{ // Project: WB2AXIPSP: bus bridges and other odds and ends // // Purpose: A AXI-Lite bus fault isolator. This core will isolate any // downstream AXI-liite slave faults from the upstream channel. // It sits as a bump in the wire between upstream and downstream channels, // and so it will consume two clocks--slowing down the slave, but // potentially allowing developer to recover in case of a fault. // // This core is configured by a couple parameters, which are key to its // functionality. // // OPT_TIMEOUT Set this to a number to be roughly the longest time // period you expect the slave to stall the bus, or likewise // the longest time period you expect it to wait for a response. // If the slave takes longer for either task, a fault will be // detected and reported. // // OPT_SELF_RESET If set, this will send a reset signal to the downstream // core so that you can attempt to restart it without reloading // the FPGA. If set, the o_reset signal will be used to reset // the downstream core. // // A second key feature of this core are the outgoing fault indicators, // o_write_fault and o_read_fault. If either signal is ever raised, the // slave has (somehow) violated protocol on either the write or the // read channels respectively. Such a violation may (or may not) return an // error upstream. For example, if the slave returns a response // following no requests from the master, then no error will be returned // up stream (doing so would be a protocol violation), but a fault will // still be detected. Use this line to trigger any internal logic // analyzers. // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // }}} // Copyright (C) 2020-2024, Gisselquist Technology, LLC // {{{ // This file is part of the WB2AXIP project. // // The WB2AXIP project contains free software and gateware, licensed under the // Apache License, Version 2.0 (the "License"). You may not use this project, // or this file, except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the 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. // //////////////////////////////////////////////////////////////////////////////// // // `default_nettype none // }}} module axilsafety #( // {{{ parameter C_AXI_ADDR_WIDTH = 28, parameter C_AXI_DATA_WIDTH = 32, parameter OPT_TIMEOUT = 12, parameter MAX_DEPTH = (OPT_TIMEOUT), localparam AW = C_AXI_ADDR_WIDTH, localparam DW = C_AXI_DATA_WIDTH, localparam LGTIMEOUT = $clog2(OPT_TIMEOUT+1), localparam LGDEPTH = $clog2(MAX_DEPTH+1), parameter [0:0] OPT_SELF_RESET = 1'b1, parameter OPT_MIN_RESET = 16 `ifdef FORMAL , parameter [0:0] F_OPT_WRITES = 1'b1, parameter [0:0] F_OPT_READS = 1'b1, parameter [0:0] F_OPT_FAULTLESS = 1'b1 `endif // }}} ) ( // {{{ output reg o_write_fault, output reg o_read_fault, // input wire S_AXI_ACLK, input wire S_AXI_ARESETN, output reg M_AXI_ARESETN, // input wire S_AXI_AWVALID, output reg S_AXI_AWREADY, input wire [AW-1:0] S_AXI_AWADDR, input wire [2:0] S_AXI_AWPROT, // input wire S_AXI_WVALID, output reg S_AXI_WREADY, input wire [DW-1:0] S_AXI_WDATA, input wire [DW/8-1:0] S_AXI_WSTRB, // output reg S_AXI_BVALID, input wire S_AXI_BREADY, output reg [1:0] S_AXI_BRESP, // input wire S_AXI_ARVALID, output reg S_AXI_ARREADY, input wire [AW-1:0] S_AXI_ARADDR, input wire [2:0] S_AXI_ARPROT, // output reg S_AXI_RVALID, input wire S_AXI_RREADY, output reg [DW-1:0] S_AXI_RDATA, output reg [1:0] S_AXI_RRESP, // // // output reg M_AXI_AWVALID, input wire M_AXI_AWREADY, output reg [AW-1:0] M_AXI_AWADDR, output reg [2:0] M_AXI_AWPROT, // output reg M_AXI_WVALID, input wire M_AXI_WREADY, output reg [DW-1:0] M_AXI_WDATA, output reg [DW/8-1:0] M_AXI_WSTRB, // input wire M_AXI_BVALID, output wire M_AXI_BREADY, input wire [1:0] M_AXI_BRESP, // output reg M_AXI_ARVALID, input wire M_AXI_ARREADY, output reg [AW-1:0] M_AXI_ARADDR, output reg [2:0] M_AXI_ARPROT, // input wire M_AXI_RVALID, output wire M_AXI_RREADY, input wire [DW-1:0] M_AXI_RDATA, input wire [1:0] M_AXI_RRESP // }}} ); // localparam, wire, and register declarations // {{{ localparam OPT_LOWPOWER = 1'b0; localparam OKAY = 2'b00, EXOKAY = 2'b01, SLVERR = 2'b10; reg [LGDEPTH-1:0] aw_count, w_count, r_count; reg aw_zero, w_zero, r_zero, aw_full, w_full, r_full, aw_w_greater, w_aw_greater; reg [LGDEPTH-1:0] downstream_aw_count, downstream_w_count, downstream_r_count; reg downstream_aw_zero, downstream_w_zero, downstream_r_zero; // downstream_aw_w_greater, downstream_w_aw_greater; wire awskd_valid; wire [2:0] awskd_prot; wire [AW-1:0] awskd_addr; reg awskd_ready; wire wskd_valid; wire [DW-1:0] wskd_data; wire [DW/8-1:0] wskd_strb; reg wskd_ready; wire bskd_valid; wire [1:0] bskd_resp; reg bskd_ready; reg last_bvalid; reg [1:0] last_bdata; reg last_bchanged; wire arskd_valid; wire [2:0] arskd_prot; wire [AW-1:0] arskd_addr; reg arskd_ready; reg last_rvalid; reg [DW+1:0] last_rdata; reg last_rchanged; wire rskd_valid; wire [1:0] rskd_resp; wire [DW-1:0] rskd_data; reg rskd_ready; reg [LGTIMEOUT-1:0] aw_stall_counter, w_stall_counter, r_stall_counter, w_ack_timer, r_ack_timer; reg aw_stall_limit, w_stall_limit, r_stall_limit, w_ack_limit, r_ack_limit; // }}} //////////////////////////////////////////////////////////////////////// // // Write signaling // {{{ //////////////////////////////////////////////////////////////////////// // // // // Write address channel // {{{ skidbuffer #(.DW(AW+3) // {{{ `ifdef FORMAL , .OPT_PASSTHROUGH(1'b1) `endif // }}} ) awskd(S_AXI_ACLK, !S_AXI_ARESETN, // {{{ S_AXI_AWVALID, S_AXI_AWREADY, { S_AXI_AWPROT, S_AXI_AWADDR }, awskd_valid, awskd_ready, { awskd_prot, awskd_addr}); // }}} // awskd_ready // {{{ // awskd_ready is the critical piece here, since it determines when // we accept a packet from the skid buffer. // always @(*) if (!M_AXI_ARESETN || o_write_fault) // On any fault, we'll always accept a request (and return an // error). We always accept a value if there are already // more writes than write addresses accepted. awskd_ready = (w_aw_greater) ||((aw_zero)&&(!S_AXI_BVALID || S_AXI_BREADY)); else // Otherwise, we accept if ever our counters aren't about to // overflow, and there's a place downstream to accept it awskd_ready = (!M_AXI_AWVALID || M_AXI_AWREADY)&& (!aw_full); // }}} // M_AXI_AWVALID // {{{ initial M_AXI_AWVALID = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN) M_AXI_AWVALID <= 1'b0; else if (!M_AXI_AWVALID || M_AXI_AWREADY) M_AXI_AWVALID <= awskd_valid && awskd_ready && !o_write_fault; // }}} // M_AXI_AWADDR, M_AXI_AWPROT // {{{ always @(posedge S_AXI_ACLK) if (OPT_LOWPOWER && (!M_AXI_ARESETN || o_write_fault)) begin M_AXI_AWADDR <= 0; M_AXI_AWPROT <= 0; end else if (!M_AXI_AWVALID || M_AXI_AWREADY) begin M_AXI_AWADDR <= awskd_addr; M_AXI_AWPROT <= awskd_prot; end // }}} // }}} // // Write data channel // {{{ skidbuffer #(.DW(DW+DW/8) // {{{ `ifdef FORMAL , .OPT_PASSTHROUGH(1'b1) `endif // }}} ) wskd(S_AXI_ACLK, !S_AXI_ARESETN, // {{{ S_AXI_WVALID, S_AXI_WREADY, { S_AXI_WDATA, S_AXI_WSTRB }, wskd_valid, wskd_ready, { wskd_data, wskd_strb}); // }}} // wskd_ready // {{{ // As with awskd_ready, this logic is the critical key. // always @(*) if (!M_AXI_ARESETN || o_write_fault) wskd_ready = (aw_w_greater) || ((w_zero)&&(!S_AXI_BVALID || S_AXI_BREADY)); else wskd_ready = (!M_AXI_WVALID || M_AXI_WREADY) && (!w_full); // }}} // M_AXI_WVALID // {{{ initial M_AXI_WVALID = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN) M_AXI_WVALID <= 1'b0; else if (!M_AXI_WVALID || M_AXI_WREADY) M_AXI_WVALID <= wskd_valid && wskd_ready && !o_write_fault; // }}} // M_AXI_WDATA, M_AXI_WSTRB // {{{ always @(posedge S_AXI_ACLK) if (OPT_LOWPOWER && (!M_AXI_ARESETN || o_write_fault)) begin M_AXI_WDATA <= 0; M_AXI_WSTRB <= 0; end else if (!M_AXI_WVALID || M_AXI_WREADY) begin M_AXI_WDATA <= wskd_data; M_AXI_WSTRB <= (o_write_fault) ? 0 : wskd_strb; end // }}} // }}} // // Write return channel // {{{ // bskd_valid, M_AXI_BREADY, bskd_resp // {{{ `ifdef FORMAL assign bskd_valid = M_AXI_BVALID; assign M_AXI_BREADY= bskd_ready; assign bskd_resp = M_AXI_BRESP; `else skidbuffer #(.DW(2) ) bskd(S_AXI_ACLK, !S_AXI_ARESETN || !M_AXI_ARESETN, M_AXI_BVALID, M_AXI_BREADY, M_AXI_BRESP, bskd_valid, bskd_ready, bskd_resp); `endif // }}} // bskd_ready // {{{ always @(*) if (o_write_fault) bskd_ready = 1'b1; else bskd_ready = (!S_AXI_BVALID || S_AXI_BREADY); // }}} // S_AXI_BVALID // {{{ initial S_AXI_BVALID = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) S_AXI_BVALID <= 1'b0; else if (!S_AXI_BVALID || S_AXI_BREADY) begin if (o_write_fault || !M_AXI_ARESETN) S_AXI_BVALID <= (!S_AXI_BVALID&&(!aw_zero)&&(!w_zero)); else S_AXI_BVALID <= (!downstream_aw_zero) &&(!downstream_w_zero)&&(bskd_valid); end // }}} // last_bvalid // {{{ initial last_bvalid = 1'b0; always @(posedge S_AXI_ACLK) if (!M_AXI_ARESETN || o_write_fault) last_bvalid <= 1'b0; else last_bvalid <= (M_AXI_BVALID && !M_AXI_BREADY); // }}} // last_bdata // {{{ always @(posedge S_AXI_ACLK) if (M_AXI_BVALID) last_bdata <= M_AXI_BRESP; // }}} // last_bchanged // {{{ always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault) last_bchanged <= 1'b0; else last_bchanged <= (last_bvalid && (!M_AXI_BVALID || last_bdata != M_AXI_BRESP)); // }}} // S_AXI_BRESP // {{{ initial S_AXI_BRESP = OKAY; always @(posedge S_AXI_ACLK) if (!S_AXI_BVALID || S_AXI_BREADY) begin if (o_write_fault) S_AXI_BRESP <= SLVERR; else if (bskd_resp == EXOKAY) S_AXI_BRESP <= SLVERR; else S_AXI_BRESP <= bskd_resp; end // }}} // }}} // }}} //////////////////////////////////////////////////////////////////////// // // Read signaling // {{{ //////////////////////////////////////////////////////////////////////// // // // // Read address channel // {{{ skidbuffer #(.DW(AW+3) // {{{ `ifdef FORMAL , .OPT_PASSTHROUGH(1'b1) `endif // }}} ) arskd(S_AXI_ACLK, !S_AXI_ARESETN, // {{{ S_AXI_ARVALID, S_AXI_ARREADY, { S_AXI_ARPROT, S_AXI_ARADDR }, arskd_valid, arskd_ready, { arskd_prot, arskd_addr }); // }}} // arskd_ready // {{{ always @(*) if (!M_AXI_ARESETN || o_read_fault) arskd_ready =((r_zero)&&(!S_AXI_RVALID || S_AXI_RREADY)); else arskd_ready = (!M_AXI_ARVALID || M_AXI_ARREADY) && (!r_full); // }}} // M_AXI_ARVALID // {{{ initial M_AXI_ARVALID = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN) M_AXI_ARVALID <= 1'b0; else if (!M_AXI_ARVALID || M_AXI_ARREADY) M_AXI_ARVALID <= arskd_valid && arskd_ready && !o_read_fault; // }}} // M_AXI_ARADDR, M_AXI_ARPROT // {{{ always @(posedge S_AXI_ACLK) if (OPT_LOWPOWER && (!M_AXI_ARESETN || o_read_fault)) begin M_AXI_ARADDR <= 0; M_AXI_ARPROT <= 0; end else if (!M_AXI_ARVALID || M_AXI_ARREADY) begin M_AXI_ARADDR <= arskd_addr; M_AXI_ARPROT <= arskd_prot; end // }}} // }}} // // Read data channel // {{{ // rskd_valid, rskd_resp, rskd_data skid buffer // {{{ `ifdef FORMAL assign rskd_valid = M_AXI_RVALID; assign M_AXI_RREADY = rskd_ready; assign { rskd_resp, rskd_data } = { M_AXI_RRESP, M_AXI_RDATA }; `else skidbuffer #(.DW(DW+2) ) rskd(S_AXI_ACLK, !S_AXI_ARESETN || !M_AXI_ARESETN, M_AXI_RVALID, M_AXI_RREADY, { M_AXI_RRESP, M_AXI_RDATA }, rskd_valid, rskd_ready, { rskd_resp, rskd_data }); `endif // ?}}} // rskd_ready // {{{ always @(*) if (o_read_fault) rskd_ready = 1; else rskd_ready = (!S_AXI_RVALID || S_AXI_RREADY); // }}} // S_AXI_RVALID // {{{ initial S_AXI_RVALID = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) S_AXI_RVALID <= 1'b0; else if (!S_AXI_RVALID || S_AXI_RREADY) begin if (o_read_fault || !M_AXI_ARESETN) S_AXI_RVALID <= (!S_AXI_RVALID && !r_zero) || (arskd_valid && arskd_ready); else S_AXI_RVALID <= (!downstream_r_zero)&&(rskd_valid); end // }}} // S_AXI_RDATA, S_AXI_RRESP // {{{ always @(posedge S_AXI_ACLK) if (!S_AXI_RVALID || S_AXI_RREADY) begin if (o_read_fault || !M_AXI_ARESETN) S_AXI_RDATA <= 0; else S_AXI_RDATA <= rskd_data; S_AXI_RRESP <= OKAY; if (o_read_fault || rskd_resp == EXOKAY || !M_AXI_ARESETN) S_AXI_RRESP <= SLVERR; else if (!downstream_r_zero) S_AXI_RRESP <= rskd_resp; end // }}} // last_rvalid // {{{ initial last_rvalid = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault) last_rvalid <= 1'b0; else last_rvalid <= (M_AXI_RVALID && !M_AXI_RREADY); // }}} // last_rdata // {{{ always @(posedge S_AXI_ACLK) if (M_AXI_RVALID) last_rdata <= { M_AXI_RRESP, M_AXI_RDATA }; // }}} // last_rchanged // {{{ always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault) last_rchanged <= 1'b0; else last_rchanged <= (last_rvalid && (!M_AXI_RVALID || last_rdata != { M_AXI_RRESP, M_AXI_RDATA })); // }}} // }}} // }}} //////////////////////////////////////////////////////////////////////// // // Usage counters // {{{ //////////////////////////////////////////////////////////////////////// // // // // Write address channel // {{{ initial aw_count = 0; initial aw_zero = 1; initial aw_full = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin aw_count <= 0; aw_zero <= 1; aw_full <= 0; end else case({(awskd_valid && awskd_ready),S_AXI_BVALID&&S_AXI_BREADY}) 2'b10: begin aw_count <= aw_count + 1; aw_zero <= 0; aw_full <= (aw_count == { {(LGDEPTH-1){1'b1}}, 1'b0 }); end 2'b01: begin aw_count <= aw_count - 1; aw_zero <= (aw_count <= 1); aw_full <= 0; end default: begin end endcase // }}} // // Write data channel // {{{ initial w_count = 0; initial w_zero = 1; initial w_full = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin w_count <= 0; w_zero <= 1; w_full <= 0; end else case({(wskd_valid && wskd_ready), S_AXI_BVALID&& S_AXI_BREADY}) 2'b10: begin w_count <= w_count + 1; w_zero <= 0; w_full <= (w_count == { {(LGDEPTH-1){1'b1}}, 1'b0 }); end 2'b01: begin w_count <= w_count - 1; w_zero <= (w_count <= 1); w_full <= 1'b0; end default: begin end endcase // }}} // aw_w_greater, w_aw_greater // {{{ initial aw_w_greater = 0; initial w_aw_greater = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin aw_w_greater <= 0; w_aw_greater <= 0; end else case({(awskd_valid && awskd_ready), (wskd_valid && wskd_ready)}) 2'b10: begin aw_w_greater <= (aw_count + 1 > w_count); w_aw_greater <= ( w_count > aw_count + 1); end 2'b01: begin aw_w_greater <= (aw_count > w_count + 1); w_aw_greater <= ( w_count + 1 > aw_count); end default: begin end endcase // }}} // // Read channel // {{{ // r_count, r_zero, r_full initial r_count = 0; initial r_zero = 1; initial r_full = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) begin r_count <= 0; r_zero <= 1; r_full <= 0; end else case({(arskd_valid&&arskd_ready), S_AXI_RVALID&&S_AXI_RREADY}) 2'b10: begin r_count <= r_count + 1; r_zero <= 0; r_full <= (r_count == { {(LGDEPTH-1){1'b1}}, 1'b0 }); end 2'b01: begin r_count <= r_count - 1; r_zero <= (r_count <= 1); r_full <= 0; end default: begin end endcase // }}} // // Downstream write address channel // {{{ initial downstream_aw_count = 0; initial downstream_aw_zero = 1; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault) begin downstream_aw_count <= 0; downstream_aw_zero <= 1; end else case({(M_AXI_AWVALID && M_AXI_AWREADY), M_AXI_BVALID && M_AXI_BREADY}) 2'b10: begin downstream_aw_count <= downstream_aw_count + 1; downstream_aw_zero <= 0; end 2'b01: begin downstream_aw_count <= downstream_aw_count - 1; downstream_aw_zero <= (downstream_aw_count <= 1); end default: begin end endcase // }}} // // Downstream write data channel // {{{ initial downstream_w_count = 0; initial downstream_w_zero = 1; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault) begin downstream_w_count <= 0; downstream_w_zero <= 1; end else case({(M_AXI_WVALID && M_AXI_WREADY), M_AXI_BVALID && M_AXI_BREADY}) 2'b10: begin downstream_w_count <= downstream_w_count + 1; downstream_w_zero <= 0; end 2'b01: begin downstream_w_count <= downstream_w_count - 1; downstream_w_zero <= (downstream_w_count <= 1); end default: begin end endcase // }}} // // Downstream read channel // {{{ initial downstream_r_count = 0; initial downstream_r_zero = 1; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault) begin downstream_r_count <= 0; downstream_r_zero <= 1; end else case({M_AXI_ARVALID && M_AXI_ARREADY, M_AXI_RVALID && M_AXI_RREADY}) 2'b10: begin downstream_r_count <= downstream_r_count + 1; downstream_r_zero <= 0; end 2'b01: begin downstream_r_count <= downstream_r_count - 1; downstream_r_zero <= (downstream_r_count <= 1); end default: begin end endcase // }}} // }}} //////////////////////////////////////////////////////////////////////// // // Timeout checking // {{{ //////////////////////////////////////////////////////////////////////// // // // The key piece here is that we define the timeout depending upon // what happens (or doesn't happen) *DOWNSTREAM*. These timeouts // will need to propagate upstream before taking place. // // Write address stall counter // {{{ initial aw_stall_counter = 0; initial aw_stall_limit = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || o_write_fault || !M_AXI_ARESETN) begin aw_stall_counter <= 0; aw_stall_limit <= 0; end else if (!M_AXI_AWVALID || M_AXI_AWREADY || M_AXI_BVALID) begin aw_stall_counter <= 0; aw_stall_limit <= 0; end else if (aw_w_greater && !M_AXI_WVALID) begin aw_stall_counter <= 0; aw_stall_limit <= 0; end else // if (!S_AXI_BVALID || S_AXI_BREADY) begin aw_stall_counter <= aw_stall_counter + 1; aw_stall_limit <= (aw_stall_counter+1 >= OPT_TIMEOUT); end // }}} // // Write data stall counter // {{{ initial w_stall_counter = 0; initial w_stall_limit = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault) begin w_stall_counter <= 0; w_stall_limit <= 0; end else if (!M_AXI_WVALID || M_AXI_WREADY || M_AXI_BVALID) begin w_stall_counter <= 0; w_stall_limit <= 0; end else if (w_aw_greater && !M_AXI_AWVALID) begin w_stall_counter <= 0; w_stall_limit <= 0; end else // if (!M_AXI_BVALID || M_AXI_BREADY) begin w_stall_counter <= w_stall_counter + 1; w_stall_limit <= (w_stall_counter + 1 >= OPT_TIMEOUT); end // }}} // // Write acknowledgment delay counter // {{{ initial w_ack_timer = 0; initial w_ack_limit = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault) begin w_ack_timer <= 0; w_ack_limit <= 0; end else if (M_AXI_BVALID || downstream_aw_zero || downstream_w_zero) begin w_ack_timer <= 0; w_ack_limit <= 0; end else begin w_ack_timer <= w_ack_timer + 1; w_ack_limit <= (w_ack_timer + 1 >= OPT_TIMEOUT); end // }}} // // Read request stall counter // {{{ initial r_stall_counter = 0; initial r_stall_limit = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault) begin r_stall_counter <= 0; r_stall_limit <= 0; end else if (!M_AXI_ARVALID || M_AXI_ARREADY || M_AXI_RVALID) begin r_stall_counter <= 0; r_stall_limit <= 0; end else begin r_stall_counter <= r_stall_counter + 1; r_stall_limit <= (r_stall_counter + 1 >= OPT_TIMEOUT); end // }}} // // Read acknowledgement delay counter // {{{ initial r_ack_timer = 0; initial r_ack_limit = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault) begin r_ack_timer <= 0; r_ack_limit <= 0; end else if (M_AXI_RVALID || downstream_r_zero) begin r_ack_timer <= 0; r_ack_limit <= 0; end else begin r_ack_timer <= r_ack_timer + 1; r_ack_limit <= (r_ack_timer + 1 >= OPT_TIMEOUT); end // }}} // }}} //////////////////////////////////////////////////////////////////////// // // Fault detection // {{{ //////////////////////////////////////////////////////////////////////// // // // // Determine if a write fault has taken place // {{{ initial o_write_fault =1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) o_write_fault <= 1'b0; else if (OPT_SELF_RESET && o_write_fault) begin // // Clear any fault on reset if (!M_AXI_ARESETN) o_write_fault <= 1'b0; end else begin // // A write fault takes place if you respond without a prior // bus request on both write address and write data channels. if ((downstream_aw_zero || downstream_w_zero)&&(bskd_valid)) o_write_fault <= 1'b1; // AXI-lite slaves are not allowed to return EXOKAY responses // from the bus if (bskd_valid && bskd_resp == EXOKAY) o_write_fault <= 1'b1; // If the downstream core refuses to accept either a // write address request, or a write data request, or for that // matter if it doesn't return an acknowledgment in a timely // fashion, then a fault has been detected. if (aw_stall_limit || w_stall_limit || w_ack_limit) o_write_fault <= 1'b1; // If the downstream core changes BRESP while VALID && !READY, // then it isn't stalling the channel properly--that's a fault. if (last_bchanged) o_write_fault <= 1'b1; end // }}} // o_read_fault // {{{ initial o_read_fault =1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) o_read_fault <= 1'b0; else if (OPT_SELF_RESET && o_read_fault) begin // // Clear any fault on reset if (!M_AXI_ARESETN) o_read_fault <= 1'b0; end else begin // Responding without a prior request is a fault. Can only // respond after a request has been made. if (downstream_r_zero && rskd_valid) o_read_fault <= 1'b1; // AXI-lite slaves are not allowed to return EXOKAY. This is // an error. if (rskd_valid && rskd_resp == EXOKAY) o_read_fault <= 1'b1; // The slave cannot stall the bus forever, nor should the // master wait forever for a response from the slave. if (r_stall_limit || r_ack_limit) o_read_fault <= 1'b1; // If the slave changes the data, or the RRESP on the wire, // while the incoming bus is stalled, then that's also a fault. if (last_rchanged) o_read_fault <= 1'b1; end // }}} // }}} //////////////////////////////////////////////////////////////////////// // // Self reset handling // {{{ //////////////////////////////////////////////////////////////////////// // // generate if (OPT_SELF_RESET) begin : SELF_RESET_GENERATION // {{{ wire min_reset; if (OPT_MIN_RESET > 1) begin : MIN_RESET // {{{ reg r_min_reset; reg [$clog2(OPT_MIN_RESET+1):0] reset_counter; // // Optionally insist that any downstream reset have a // minimum duration. Many Xilinx components require // a 16-clock reset. This ensures such reset // requirements are achieved. // initial reset_counter = OPT_MIN_RESET-1; initial r_min_reset = 1'b0; always @(posedge S_AXI_ARESETN) if (M_AXI_ARESETN) begin reset_counter <= OPT_MIN_RESET-1; r_min_reset <= 1'b0; end else if (!M_AXI_ARESETN) begin if (reset_counter > 0) reset_counter <= reset_counter-1; min_reset <= (reset_counter <= 1); end assign min_reset = r_min_reset; `ifdef FORMAL always @(*) assert(reset_counter < OPT_MIN_RESET); always @(*) assert(min_reset == (reset_counter == 0)); `endif // }}} end else begin : NO_MIN_RESET // {{{ assign min_reset = 1'b1; // }}} end // M_AXI_ARESETN // {{{ // Reset the downstream bus on either a write or a read fault. // Once the bus returns to idle, and any minimum reset durations // have been achieved, then release the downstream from reset. // initial M_AXI_ARESETN = 1'b0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) M_AXI_ARESETN <= 1'b0; else if (o_write_fault || o_read_fault) M_AXI_ARESETN <= 1'b0; else if (aw_zero && w_zero && r_zero && min_reset && !awskd_valid && !wskd_valid && !arskd_valid) M_AXI_ARESETN <= 1'b1; // }}} // }}} end else begin : SAME_RESET // {{{ // // The downstream reset equals the upstream reset // always @(*) M_AXI_ARESETN = S_AXI_ARESETN; // }}} end endgenerate // }}} //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // // Formal property section // {{{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// `ifdef FORMAL // {{{ // // The following proof comes in several parts. // // 1. PROVE that the upstream properties will hold independent of // what the downstream slave ever does. // // 2. PROVE that if the downstream slave follows protocol, then // neither o_write_fault nor o_read_fault will never get raised. // // We then repeat these proofs again with both OPT_SELF_RESET set and // clear. Which of the four proofs is accomplished is dependent upon // parameters set by the formal engine. // // wire [LGDEPTH:0] faxils_awr_outstanding, faxils_wr_outstanding, faxils_rd_outstanding; reg f_past_valid; initial f_past_valid = 0; always @(posedge S_AXI_ACLK) f_past_valid <= 1; // }}} //////////////////////////////////////////////////////////////////////// // // Upstream master Bus properties // {{{ //////////////////////////////////////////////////////////////////////// // // always @(*) if (!f_past_valid) begin assume(!S_AXI_ARESETN); assert(!M_AXI_ARESETN); end faxil_slave #( // {{{ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH), .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), .F_OPT_ASSUME_RESET(1'b1), .F_OPT_NO_RESET((OPT_MIN_RESET == 0) ? 1:0), // .F_AXI_MAXWAIT((F_OPT_FAULTLESS) ? (2*OPT_TIMEOUT+2) : 0), .F_AXI_MAXRSTALL(3), // .F_AXI_MAXDELAY(OPT_TIMEOUT+OPT_TIMEOUT+5), .F_LGDEPTH(LGDEPTH+1), // .F_AXI_MAXWAIT((F_OPT_FAULTLESS) ? (2*OPT_TIMEOUT+2) : 0), .F_AXI_MAXDELAY(2*OPT_TIMEOUT+3) // }}} ) axils ( // {{{ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN), // .i_axi_awvalid(S_AXI_AWVALID), .i_axi_awready(S_AXI_AWREADY), .i_axi_awaddr( S_AXI_AWADDR), .i_axi_awprot( S_AXI_AWPROT), // .i_axi_wvalid(S_AXI_WVALID), .i_axi_wready(S_AXI_WREADY), .i_axi_wdata( S_AXI_WDATA), .i_axi_wstrb( S_AXI_WSTRB), // .i_axi_bvalid(S_AXI_BVALID), .i_axi_bready(S_AXI_BREADY), .i_axi_bresp( S_AXI_BRESP), // .i_axi_arvalid(S_AXI_ARVALID), .i_axi_arready(S_AXI_ARREADY), .i_axi_araddr( S_AXI_ARADDR), .i_axi_arprot( S_AXI_ARPROT), // .i_axi_rvalid(S_AXI_RVALID), .i_axi_rready(S_AXI_RREADY), .i_axi_rdata( S_AXI_RDATA), .i_axi_rresp( S_AXI_RRESP), // .f_axi_awr_outstanding(faxils_awr_outstanding), .f_axi_wr_outstanding(faxils_wr_outstanding), .f_axi_rd_outstanding(faxils_rd_outstanding) // }}} ); always @(*) if (!F_OPT_WRITES) begin // {{{ assume(!S_AXI_AWVALID); assume(!S_AXI_WVALID); assert(aw_count == 0); assert(w_count == 0); assert(!M_AXI_AWVALID); assert(!M_AXI_WVALID); // }}} end always @(*) if (!F_OPT_READS) begin // {{{ assume(!S_AXI_ARVALID); assert(r_count == 0); assert(!S_AXI_RVALID); assert(!M_AXI_ARVALID); // }}} end // }}} //////////////////////////////////////////////////////////////////////// // // General Induction properties // {{{ //////////////////////////////////////////////////////////////////////// // always @(*) begin // {{{ assert(aw_zero == (aw_count == 0)); assert(w_zero == (w_count == 0)); assert(r_zero == (r_count == 0)); // assert(aw_full == (&aw_count)); assert(w_full == (&w_count)); assert(r_full == (&r_count)); // if (M_AXI_ARESETN && !o_write_fault) begin assert(downstream_aw_zero == (downstream_aw_count == 0)); assert(downstream_w_zero == (downstream_w_count == 0)); assert(downstream_aw_count + (M_AXI_AWVALID ? 1:0) + (S_AXI_BVALID ? 1:0) == aw_count); assert(downstream_w_count + (M_AXI_WVALID ? 1:0) + (S_AXI_BVALID ? 1:0) == w_count); end if (M_AXI_ARESETN && !o_read_fault) begin assert(downstream_r_zero == (downstream_r_count == 0)); assert(downstream_r_count + (M_AXI_ARVALID ? 1:0) + (S_AXI_RVALID ? 1:0) == r_count); end // assert(aw_count == faxils_awr_outstanding); assert(w_count == faxils_wr_outstanding); assert(r_count == faxils_rd_outstanding); assert(aw_w_greater == (aw_count > w_count)); assert(w_aw_greater == (w_count > aw_count)); // }}} end // }}} generate if (F_OPT_FAULTLESS) begin : ASSUME_FAULTLESS // {{{ //////////////////////////////////////////////////////////////// // // Assume the downstream core is protocol compliant, and // prove that o_fault stays low. // {{{ //////////////////////////////////////////////////////////////// // wire [LGDEPTH:0] faxilm_awr_outstanding, faxilm_wr_outstanding, faxilm_rd_outstanding; faxil_master #( // {{{ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH), .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH), .F_OPT_NO_RESET((OPT_MIN_RESET == 0) ? 1:0), .F_AXI_MAXWAIT(OPT_TIMEOUT), .F_AXI_MAXRSTALL(4), .F_AXI_MAXDELAY(OPT_TIMEOUT), .F_LGDEPTH(LGDEPTH+1) // }}} ) axilm ( // {{{ .i_clk(S_AXI_ACLK), .i_axi_reset_n(M_AXI_ARESETN && S_AXI_ARESETN), // .i_axi_awvalid(M_AXI_AWVALID), .i_axi_awready(M_AXI_AWREADY), .i_axi_awaddr( M_AXI_AWADDR), .i_axi_awprot( M_AXI_AWPROT), // .i_axi_wvalid(M_AXI_WVALID), .i_axi_wready(M_AXI_WREADY), .i_axi_wdata( M_AXI_WDATA), .i_axi_wstrb( M_AXI_WSTRB), // .i_axi_bvalid(M_AXI_BVALID), .i_axi_bready(M_AXI_BREADY), .i_axi_bresp( M_AXI_BRESP), // .i_axi_arvalid(M_AXI_ARVALID), .i_axi_arready(M_AXI_ARREADY), .i_axi_araddr( M_AXI_ARADDR), .i_axi_arprot( M_AXI_ARPROT), // .i_axi_rvalid(M_AXI_RVALID), .i_axi_rready(M_AXI_RREADY), .i_axi_rdata( M_AXI_RDATA), .i_axi_rresp( M_AXI_RRESP), // .f_axi_awr_outstanding(faxilm_awr_outstanding), .f_axi_wr_outstanding(faxilm_wr_outstanding), .f_axi_rd_outstanding(faxilm_rd_outstanding) // }}} ); // // Here's the big proof // {{{ always @(*) assert(!o_write_fault); always @(*) assert(!o_read_fault); // }}} // }}} //////////////////////////////////////////////////////////////// // // The following properties are necessary for passing induction // {{{ //////////////////////////////////////////////////////////////// // // always @(*) begin assert(!aw_stall_limit); assert(!w_stall_limit); assert(!w_ack_limit); assert(!r_stall_limit); assert(!r_ack_limit); if (M_AXI_ARESETN) begin assert(downstream_aw_count == faxilm_awr_outstanding); assert(downstream_w_count == faxilm_wr_outstanding); assert(downstream_r_count == faxilm_rd_outstanding); end end if (OPT_SELF_RESET) begin always @(posedge S_AXI_ACLK) if (f_past_valid) assert(M_AXI_ARESETN == $past(S_AXI_ARESETN)); end `ifdef VERIFIC wire [LGDEPTH:0] f_axi_arstall; wire [LGDEPTH:0] f_axi_awstall; wire [LGDEPTH:0] f_axi_wstall; assign f_axi_awstall = axilm.CHECK_STALL_COUNT.f_axi_awstall; assign f_axi_wstall = axilm.CHECK_STALL_COUNT.f_axi_wstall; assign f_axi_arstall = axilm.CHECK_STALL_COUNT.f_axi_arstall; always @(*) if (M_AXI_ARESETN && S_AXI_ARESETN && !o_write_fault) assert(f_axi_awstall == aw_stall_counter); always @(*) if (M_AXI_ARESETN && S_AXI_ARESETN && !o_write_fault) assert(f_axi_wstall == w_stall_counter); always @(*) if (M_AXI_ARESETN && S_AXI_ARESETN && !o_read_fault) assert(f_axi_arstall == r_stall_counter); `endif // }}} // }}} end else begin : WILD_DOWNSTREAM // {{{ //////////////////////////////////////////////////////////////// // // cover() checks, checks that only make sense if faults are // possible // reg write_faulted, read_faulted, faulted; reg [3:0] cvr_writes, cvr_reads; if (OPT_SELF_RESET) begin //////////////////////////////////////////////////////// // // Prove that we can actually reset the downstream // bus/core as desired // {{{ //////////////////////////////////////////////////////// // // initial write_faulted = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) write_faulted <= 0; else if (o_write_fault) write_faulted <= 1; initial faulted = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) read_faulted <= 0; else if (o_read_fault) read_faulted <= 1; always @(*) faulted = (write_faulted || read_faulted); always @(posedge S_AXI_ACLK) cover(write_faulted && $rose(M_AXI_ARESETN)); always @(posedge S_AXI_ACLK) cover(read_faulted && $rose(M_AXI_ARESETN)); always @(posedge S_AXI_ACLK) cover(faulted && M_AXI_ARESETN && S_AXI_BVALID); always @(posedge S_AXI_ACLK) cover(faulted && M_AXI_ARESETN && S_AXI_RVALID); initial cvr_writes = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_write_fault) cvr_writes <= 0; else if (write_faulted && S_AXI_BVALID && S_AXI_BREADY && S_AXI_BRESP == OKAY &&(!(&cvr_writes))) cvr_writes <= cvr_writes + 1; always @(*) cover(cvr_writes > 5); initial cvr_reads = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || !M_AXI_ARESETN || o_read_fault) cvr_reads <= 0; else if (read_faulted && S_AXI_RVALID && S_AXI_RREADY && S_AXI_RRESP == OKAY &&(!(&cvr_reads))) cvr_reads <= cvr_reads + 1; always @(*) cover(cvr_reads > 5); // }}} end // }}} end endgenerate //////////////////////////////////////////////////////////////////////// // // Never data properties // {{{ //////////////////////////////////////////////////////////////////////// // // (* anyconst *) reg [C_AXI_DATA_WIDTH-1:0] fc_never_read_data; (* anyconst *) reg [C_AXI_DATA_WIDTH+C_AXI_DATA_WIDTH/8-1:0] fc_never_write_data; (* anyconst *) reg [C_AXI_ADDR_WIDTH-1:0] fc_never_read_addr, fc_never_write_addr; // Write address checking // {{{ always @(*) if (S_AXI_ARESETN && S_AXI_AWVALID) assume(S_AXI_AWADDR != fc_never_write_addr); always @(*) if (S_AXI_ARESETN && M_AXI_ARESETN && !o_write_fault && M_AXI_AWVALID) assert(M_AXI_AWADDR != fc_never_write_addr); // }}} // Write checking // {{{ always @(*) if (S_AXI_ARESETN && S_AXI_WVALID) assume({ S_AXI_WDATA, S_AXI_WSTRB } != fc_never_write_data); always @(*) if (S_AXI_ARESETN && M_AXI_ARESETN && !o_write_fault && M_AXI_WVALID) assert({ M_AXI_WDATA, M_AXI_WSTRB } != fc_never_write_data); // }}} // Read address checking // {{{ always @(*) if (S_AXI_ARESETN && S_AXI_ARVALID) assume(S_AXI_ARADDR != fc_never_read_addr); always @(*) if (S_AXI_ARESETN && M_AXI_ARESETN && !o_read_fault && M_AXI_ARVALID) assert(M_AXI_ARADDR != fc_never_read_addr); // }}} // Read checking // {{{ always @(*) if (S_AXI_ARESETN && M_AXI_ARESETN && M_AXI_RVALID) assume(M_AXI_RDATA != fc_never_read_data); always @(*) if (S_AXI_ARESETN && S_AXI_RVALID && S_AXI_RRESP == 2'b00) assert(S_AXI_RDATA != fc_never_read_data); // }}} // }}} `endif // }}} endmodule