summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/axisafety.v
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2024-03-06 02:38:24 -0600
committerAlejandro Soto <alejandro@34project.org>2024-03-06 02:38:24 -0600
commit3038edc09a2eb15762f2e58533f429489107520b (patch)
treef7a45e424d39e6fef0d59e329c1bf6ea206e2886 /rtl/wb2axip/axisafety.v
parent3b62399f92e9faa2602ac30865e5fc3c7c4e12b8 (diff)
rtl/wb2axip: add to version control
Diffstat (limited to 'rtl/wb2axip/axisafety.v')
-rw-r--r--rtl/wb2axip/axisafety.v2339
1 files changed, 2339 insertions, 0 deletions
diff --git a/rtl/wb2axip/axisafety.v b/rtl/wb2axip/axisafety.v
new file mode 100644
index 0000000..ff7e351
--- /dev/null
+++ b/rtl/wb2axip/axisafety.v
@@ -0,0 +1,2339 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axisafety.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Given that 1) AXI interfaces can be difficult to write and to
+// get right, 2) my experiences with the AXI infrastructure of an
+// ARM+FPGA is that the device needs a power cycle following a bus fault,
+// and given that I've now found multiple interfaces that had some bug
+// or other within them, the following is a bus fault isolator. It acts
+// as a bump in the log between the interconnect and the user core. As
+// such, it has two interfaces: The first is the slave interface,
+// coming from the interconnect. The second interface is the master
+// interface, proceeding to a slave that might be faulty.
+//
+// INTERCONNECT --> (S) AXISAFETY (M) --> POTENTIALLY FAULTY CORE
+//
+// The slave interface has been formally verified to be a valid AXI
+// slave, independent of whatever the potentially faulty core might do.
+// If the user core (i.e. the potentially faulty one) responds validly
+// to the requests of a master, then this core will turn into a simple
+// delay on the bus. If, on the other hand, the user core suffers from
+// a protocol error, then this core will set one of two output
+// flags--either o_write_fault or o_read_fault indicating whether a write
+// or a read fault was detected. Further attempts to access the user
+// core will result in bus errors, generated by the AXISAFETY core.
+//
+// Assuming the bus master can properly detect and deal with a bus error,
+// this should then make it possible to properly recover from a bus
+// protocol error without later needing to cycle power.
+//
+// The core does have a couple of limitations. For example, it can only
+// handle one burst transaction, and one ID at a time. This matches the
+// performances of Xilinx's example AXI full core, from which many user
+// cores have have been copied/modified from. Therefore, there will be
+// no performance loss for such a core.
+//
+// Because of this, it is still possible that the user core might have an
+// undetected fault when using this core. For example, if the interconnect
+// issues more than one bus request before receiving the response from the
+// first request, this safety core will stall the second request,
+// preventing the downstream core from seeing this second request. If the
+// downstream core would suffer from an error while handling the second
+// request, by preventing the user core from seeing the second request this
+// core eliminates that potential for error.
+//
+// Usage: The important part of using this core is to connect the slave
+// side to the interconnect, and the master side to the user core.
+//
+// Some options are available:
+//
+// 1) C_S_AXI_ADDR_WIDTH The number of address bits in the AXI channel
+// 1) C_S_AXI_DATA_WIDTH The number of data bits in the AXI channel
+// 3) C_S_AXI_ID_WIDTH The number of bits in an AXI ID. As currently
+// written, this number cannot be zero. You can, however, set it
+// to '1' and connect all of the relevant ID inputs to zero.
+//
+// These three parameters have currently been abbreviated with AW, DW, and
+// IW. I anticipate returning them to their original meanings, I just
+// haven't done so (yet).
+//
+// 4) OPT_TIMEOUT This is the number of clock periods, from
+// interconnect request to interconnect response, that the
+// slave needs to respond within. (The actual timeout from the
+// user slave's perspective will be shorter than this.)
+//
+// This timeout is part of the check that a slave core will
+// always return a response. From the standpoint of this core,
+// it can be set arbitrarily large. (From the standpoint of
+// formal verification, it needs to be kept short ...)
+// Feel free to set this even as large as 1ms if you would like.
+//
+// 5) OPT_SELF_RESET If set, will send a reset signal to the slave
+// following any write or read fault. This will cause the other
+// side of the link (write or read) to fault as well. Once the
+// channel then becomes inactive, the slave will be released from
+// reset and will be able to interact with the rest of the bus
+// again.
+//
+// 5) OPT_EXCLUSIVE If clear, will prohibit the slave from ever
+// receiving an exclusive access request. This saves the logic
+// in the firewall necessary to check that an EXOKAY response
+// to a write request was truly an allowed response. Since that
+// logic can explode with the ID width, sparing it can be quite
+// useful. If set, provides full exclusive access checking in
+// addition to the normal bus fault checking.
+//
+// Performance: As mentioned above, this core can handle one read burst and one
+// write burst at a time, no more. Further, the core will delay
+// an input path by one clock and the output path by another clock, so that
+// the latency involved with using this core is a minimum of two extra
+// clocks beyond the latency user slave core.
+//
+// Maximum write latency: N+3 clocks for a burst of N values
+// Maximum read latency: N+3 clocks for a burst of N values
+//
+// Faults detected:
+//
+// Write channel:
+// 1. Raising BVALID prior to the last write value being sent
+// to the user/slave core.
+// 2. Raising BVALID prior to accepting the write address
+// 3. A BID return ID that doesn't match the request AWID
+// 4. Sending too many returns, such as not dropping BVALID
+// or raising it when there is no outstanding write
+// request
+//
+// While the following technically isn't a violation of the
+// AXI protocol, it is treated as such by this fault isolator.
+//
+// 5. Accepting write data prior to the write address
+//
+// The fault isolator will guarantee that AWVALID is raised before
+// WVALID, so this shouldn't be a problem.
+//
+// Read channel:
+//
+// 1. Raising RVALID before accepting the read address, ARVALID
+// 2. A return ID that doesn't match the ID that was sent.
+// 3. Raising RVALID after the last return value, and so returning
+// too many response values
+// 4. Setting the RLAST value on anything but the last value from
+// the bus.
+//
+//
+// Creator: Dan Gisselquist, Ph.D.
+// Gisselquist Technology, LLC
+//
+////////////////////////////////////////////////////////////////////////////////
+// }}}
+// Copyright (C) 2019-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 axisafety #(
+ // {{{
+ parameter C_S_AXI_ID_WIDTH = 1,
+ parameter C_S_AXI_DATA_WIDTH = 32,
+ parameter C_S_AXI_ADDR_WIDTH = 16,
+ // OPT_SELF_RESET: Set to true if the downstream slave should be
+ // reset upon detecting an error and separate from the upstream reset
+ // domain
+ parameter [0:0] OPT_SELF_RESET = 1'b0,
+ // OPT_EXCLUSIVE: Set to true if the downstream slave might possibly
+ // offer an exclusive access capability
+ parameter [0:0] OPT_EXCLUSIVE = 1'b0,
+ //
+ // I use the following abbreviations, IW, DW, and AW, to simplify
+ // the code below (and to help it fit within an 80 col terminal)
+ localparam IW = C_S_AXI_ID_WIDTH,
+ localparam DW = C_S_AXI_DATA_WIDTH,
+ localparam AW = C_S_AXI_ADDR_WIDTH,
+ //
+ // OPT_TIMEOUT references the number of clock cycles to wait
+ // between raising the *VALID signal and when the respective
+ // *VALID return signal must be high. You might wish to set this
+ // to a very high number, to allow your core to work its magic.
+ parameter OPT_TIMEOUT = 20
+ // }}}
+ ) (
+ // {{{
+ output reg o_read_fault,
+ output reg o_write_fault,
+ // User ports ends
+ // Do not modify the ports beyond this line
+
+ // Global Clock Signal
+ input wire S_AXI_ACLK,
+ // Global Reset Signal. This Signal is Active LOW
+ input wire S_AXI_ARESETN,
+ output reg M_AXI_ARESETN,
+ //
+ // The input side. This is where slave requests come into
+ // the core.
+ // {{{
+ //
+ // Write address
+ input wire [IW-1 : 0] S_AXI_AWID,
+ input wire [AW-1 : 0] S_AXI_AWADDR,
+ input wire [7 : 0] S_AXI_AWLEN,
+ input wire [2 : 0] S_AXI_AWSIZE,
+ input wire [1 : 0] S_AXI_AWBURST,
+ input wire S_AXI_AWLOCK,
+ input wire [3 : 0] S_AXI_AWCACHE,
+ input wire [2 : 0] S_AXI_AWPROT,
+ input wire [3 : 0] S_AXI_AWQOS,
+ input wire S_AXI_AWVALID,
+ output reg S_AXI_AWREADY,
+ // Write data
+ input wire [DW-1 : 0] S_AXI_WDATA,
+ input wire [(DW/8)-1 : 0] S_AXI_WSTRB,
+ input wire S_AXI_WLAST,
+ input wire S_AXI_WVALID,
+ output reg S_AXI_WREADY,
+ // Write return
+ output reg [IW-1 : 0] S_AXI_BID,
+ output reg [1 : 0] S_AXI_BRESP,
+ output reg S_AXI_BVALID,
+ input wire S_AXI_BREADY,
+ // Read address
+ input wire [IW-1 : 0] S_AXI_ARID,
+ input wire [AW-1 : 0] S_AXI_ARADDR,
+ input wire [7 : 0] S_AXI_ARLEN,
+ input wire [2 : 0] S_AXI_ARSIZE,
+ input wire [1 : 0] S_AXI_ARBURST,
+ input wire S_AXI_ARLOCK,
+ input wire [3 : 0] S_AXI_ARCACHE,
+ input wire [2 : 0] S_AXI_ARPROT,
+ input wire [3 : 0] S_AXI_ARQOS,
+ input wire S_AXI_ARVALID,
+ output reg S_AXI_ARREADY,
+ // Read data
+ output reg [IW-1 : 0] S_AXI_RID,
+ output reg [DW-1 : 0] S_AXI_RDATA,
+ output reg [1 : 0] S_AXI_RRESP,
+ output reg S_AXI_RLAST,
+ output reg S_AXI_RVALID,
+ input wire S_AXI_RREADY,
+ // }}}
+ //
+ // The output side, where slave requests are forwarded to the
+ // actual slave
+ // {{{
+ // Write address
+ // {{{
+ output reg [IW-1 : 0] M_AXI_AWID,
+ output reg [AW-1 : 0] M_AXI_AWADDR,
+ output reg [7 : 0] M_AXI_AWLEN,
+ output reg [2 : 0] M_AXI_AWSIZE,
+ output reg [1 : 0] M_AXI_AWBURST,
+ output reg M_AXI_AWLOCK,
+ output reg [3 : 0] M_AXI_AWCACHE,
+ output reg [2 : 0] M_AXI_AWPROT,
+ output reg [3 : 0] M_AXI_AWQOS,
+ output reg M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ // Write data
+ output reg [DW-1 : 0] M_AXI_WDATA,
+ output reg [(DW/8)-1 : 0] M_AXI_WSTRB,
+ output reg M_AXI_WLAST,
+ output reg M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+ // Write return
+ input wire [IW-1 : 0] M_AXI_BID,
+ input wire [1 : 0] M_AXI_BRESP,
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ // }}}
+ // Read address
+ // {{{
+ output reg [IW-1 : 0] M_AXI_ARID,
+ output reg [AW-1 : 0] M_AXI_ARADDR,
+ output reg [7 : 0] M_AXI_ARLEN,
+ output reg [2 : 0] M_AXI_ARSIZE,
+ output reg [1 : 0] M_AXI_ARBURST,
+ output reg M_AXI_ARLOCK,
+ output reg [3 : 0] M_AXI_ARCACHE,
+ output reg [2 : 0] M_AXI_ARPROT,
+ output reg [3 : 0] M_AXI_ARQOS,
+ output reg M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ // Read data
+ input wire [IW-1 : 0] M_AXI_RID,
+ input wire [DW-1 : 0] M_AXI_RDATA,
+ input wire [1 : 0] M_AXI_RRESP,
+ input wire M_AXI_RLAST,
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY
+ // }}}
+ // }}}
+ // }}}
+ );
+
+ localparam LGTIMEOUT = $clog2(OPT_TIMEOUT+1);
+ localparam [1:0] OKAY = 2'b00, EXOKAY = 2'b01;
+ localparam SLAVE_ERROR = 2'b10;
+ //
+ //
+ // Register declarations
+ // {{{
+ reg faulty_write_return, faulty_read_return;
+ reg clear_fault;
+ //
+ // Timer/timeout variables
+ reg [LGTIMEOUT-1:0] write_timer, read_timer;
+ reg write_timeout, read_timeout;
+
+ //
+ // Double buffer the write address channel
+ reg r_awvalid, m_awvalid;
+ reg [IW-1:0] r_awid, m_awid;
+ reg [AW-1:0] r_awaddr, m_awaddr;
+ reg [7:0] r_awlen, m_awlen;
+ reg [2:0] r_awsize, m_awsize;
+ reg [1:0] r_awburst, m_awburst;
+ reg r_awlock, m_awlock;
+ reg [3:0] r_awcache, m_awcache;
+ reg [2:0] r_awprot, m_awprot;
+ reg [3:0] r_awqos, m_awqos;
+
+ //
+ // Double buffer for the write channel
+ reg r_wvalid,m_wvalid;
+ reg [DW-1:0] r_wdata, m_wdata;
+ reg [DW/8-1:0] r_wstrb, m_wstrb;
+ reg r_wlast, m_wlast;
+
+ //
+ // Double buffer the write response channel
+ reg m_bvalid; // r_bvalid == 0
+ reg [IW-1:0] m_bid;
+ reg [1:0] m_bresp;
+
+ //
+ // Double buffer the read address channel
+ reg r_arvalid, m_arvalid;
+ reg [IW-1:0] r_arid, m_arid;
+ reg [AW-1:0] r_araddr, m_araddr;
+ reg [7:0] r_arlen, m_arlen;
+ reg [2:0] r_arsize, m_arsize;
+ reg [1:0] r_arburst, m_arburst;
+ reg r_arlock, m_arlock;
+ reg [3:0] r_arcache, m_arcache;
+ reg [2:0] r_arprot, m_arprot;
+ reg [3:0] r_arqos, m_arqos;
+
+ //
+ // Double buffer the read data response channel
+ reg r_rvalid,m_rvalid;
+ reg [IW-1:0] m_rid;
+ reg [1:0] r_rresp, m_rresp;
+ reg m_rlast;
+ reg [DW-1:0] r_rdata, m_rdata;
+
+ //
+ // Write FIFO data
+ wire [IW-1:0] wfifo_id;
+ wire wfifo_lock;
+
+ //
+ // Read FIFO data
+ reg [IW-1:0] rfifo_id;
+ reg rfifo_lock;
+ reg [8:0] rfifo_counter;
+ reg rfifo_empty, rfifo_last, rfifo_penultimate;
+
+ //
+ //
+ reg [0:0] s_wbursts;
+ reg [8:0] m_wpending;
+ reg m_wempty, m_wlastctr;
+ wire waddr_valid, raddr_valid;
+
+ wire lock_enabled, lock_failed;
+ wire [(1<<IW)-1:0] lock_active;
+ reg rfifo_first, exread;
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write channel processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Write address processing
+ // {{{
+ // S_AXI_AWREADY
+ // {{{
+ initial S_AXI_AWREADY = (OPT_SELF_RESET) ? 0:1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_AWREADY <= !OPT_SELF_RESET;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ S_AXI_AWREADY <= 0;
+ // else if (clear_fault)
+ // S_AXI_AWREADY <= 1;
+ else if (!S_AXI_AWREADY)
+ S_AXI_AWREADY <= S_AXI_BVALID && S_AXI_BREADY;
+ // }}}
+
+ generate if (OPT_EXCLUSIVE)
+ begin : GEN_LOCK_ENABLED
+ // {{{
+ reg r_lock_enabled, r_lock_failed;
+
+ // lock_enabled
+ // {{{
+ initial r_lock_enabled = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_EXCLUSIVE)
+ r_lock_enabled <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ r_lock_enabled <= S_AXI_AWLOCK && lock_active[S_AXI_AWID];
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ r_lock_enabled <= 0;
+ // }}}
+
+ // lock_failed
+ // {{{
+ initial r_lock_failed = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_EXCLUSIVE)
+ r_lock_failed <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ r_lock_failed <= S_AXI_AWLOCK && !lock_active[S_AXI_AWID];
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ r_lock_failed <= 0;
+ // }}}
+
+ assign lock_enabled = r_lock_enabled;
+ assign lock_failed = r_lock_failed;
+ // }}}
+ end else begin : NO_LOCK_ENABLE
+ // {{{
+ assign lock_enabled = 0;
+ assign lock_failed = 0;
+ // }}}
+ end endgenerate
+
+ // waddr_valid
+ // {{{
+ generate if (OPT_SELF_RESET)
+ begin : GEN_LCL_RESET
+ reg r_waddr_valid;
+
+ initial r_waddr_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_waddr_valid <= 0;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ r_waddr_valid <= 1;
+ else if (waddr_valid)
+ r_waddr_valid <= !S_AXI_BVALID || !S_AXI_BREADY;
+
+ assign waddr_valid = r_waddr_valid;
+ end else begin : NO_LCL_RESET
+ assign waddr_valid = !S_AXI_AWREADY;
+ end endgenerate
+ // }}}
+
+ // r_aw*
+ // {{{
+ // Double buffer for the AW* channel
+ //
+ initial r_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (S_AXI_AWVALID && S_AXI_AWREADY)
+ begin
+ r_awvalid <= 1'b0;
+ r_awid <= S_AXI_AWID;
+ r_awaddr <= S_AXI_AWADDR;
+ r_awlen <= S_AXI_AWLEN;
+ r_awsize <= S_AXI_AWSIZE;
+ r_awburst <= S_AXI_AWBURST;
+ r_awlock <= S_AXI_AWLOCK;
+ r_awcache <= S_AXI_AWCACHE;
+ r_awprot <= S_AXI_AWPROT;
+ r_awqos <= S_AXI_AWQOS;
+ end else if (M_AXI_AWREADY)
+ r_awvalid <= 0;
+
+ if (!S_AXI_ARESETN)
+ begin
+ r_awvalid <= 0;
+ r_awlock <= 0;
+ end
+
+ if (!OPT_EXCLUSIVE)
+ r_awlock <= 1'b0;
+ end
+ // }}}
+
+ // m_aw*
+ // {{{
+ // Second half of the AW* double-buffer. The m_* terms reference
+ // either the value in the double buffer (assuming one is in there),
+ // or the incoming value (if the double buffer is empty)
+ //
+ always @(*)
+ if (r_awvalid)
+ begin
+ m_awvalid = r_awvalid;
+ m_awid = r_awid;
+ m_awaddr = r_awaddr;
+ m_awlen = r_awlen;
+ m_awsize = r_awsize;
+ m_awburst = r_awburst;
+ m_awlock = r_awlock;
+ m_awcache = r_awcache;
+ m_awprot = r_awprot;
+ m_awqos = r_awqos;
+ end else begin
+ m_awvalid = S_AXI_AWVALID && S_AXI_AWREADY;
+ m_awid = S_AXI_AWID;
+ m_awaddr = S_AXI_AWADDR;
+ m_awlen = S_AXI_AWLEN;
+ m_awsize = S_AXI_AWSIZE;
+ m_awburst = S_AXI_AWBURST;
+ m_awlock = S_AXI_AWLOCK && OPT_EXCLUSIVE;
+ m_awcache = S_AXI_AWCACHE;
+ m_awprot = S_AXI_AWPROT;
+ m_awqos = S_AXI_AWQOS;
+ end
+ // }}}
+
+ // M_AXI_AW*
+ // {{{
+ // Set the output AW* channel outputs--but only on no fault
+ //
+ initial M_AXI_AWVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+
+ if (o_write_fault)
+ M_AXI_AWVALID <= 0;
+ else
+ M_AXI_AWVALID <= m_awvalid;
+
+ M_AXI_AWID <= m_awid;
+ M_AXI_AWADDR <= m_awaddr;
+ M_AXI_AWLEN <= m_awlen;
+ M_AXI_AWSIZE <= m_awsize;
+ M_AXI_AWBURST <= m_awburst;
+ M_AXI_AWLOCK <= m_awlock && lock_active[m_awid];
+ M_AXI_AWCACHE <= m_awcache;
+ M_AXI_AWPROT <= m_awprot;
+ M_AXI_AWQOS <= m_awqos;
+ end
+
+ if (!OPT_EXCLUSIVE)
+ M_AXI_AWLOCK <= 0;
+ if (!M_AXI_ARESETN)
+ M_AXI_AWVALID <= 0;
+ end
+ // }}}
+ // }}}
+
+ // s_wbursts
+ // {{{
+ // Count write bursts outstanding from the standpoint of
+ // the incoming (slave) channel
+ //
+ // Notice that, as currently built, the count can only ever be one
+ // or zero.
+ //
+ // We'll use this count in a moment to determine if a response
+ // has taken too long, or if a response is returned when there's
+ // no outstanding request
+ initial s_wbursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_wbursts <= 0;
+ else if (S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST)
+ s_wbursts <= 1;
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ s_wbursts <= 0;
+ // }}}
+
+ // wfifo_id, wfifo_lock
+ // {{{
+ // Keep track of the ID of the last transaction. Since we only
+ // ever have one write transaction outstanding, this will need to be
+ // the ID of the returned value.
+
+ assign wfifo_id = r_awid;
+ assign wfifo_lock = r_awlock;
+ // }}}
+
+ // m_wpending, m_wempty, m_wlastctr
+ // {{{
+ // m_wpending counts the number of (remaining) write data values that
+ // need to be sent to the slave. It counts this number with respect
+ // to the *SLAVE*, not the master. When m_wpending == 1, WLAST shall
+ // be true. To make comparisons of (m_wpending == 0) or (m_wpending>0),
+ // m_wempty is assigned to (m_wpending). Similarly, m_wlastctr is
+ // assigned to (m_wpending == 1).
+ //
+ initial m_wpending = 0;
+ initial m_wempty = 1;
+ initial m_wlastctr = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_write_fault)
+ begin
+ // {{{
+ m_wpending <= 0;
+ m_wempty <= 1;
+ m_wlastctr <= 0;
+ // }}}
+ end else if (M_AXI_AWVALID && M_AXI_AWREADY)
+ begin
+ // {{{
+ if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ // {{{
+ // Accepting AW* and W* packets on the same
+ // clock
+ if (m_wpending == 0)
+ begin
+ // The AW* and W* packets go together
+ m_wpending <= {1'b0, M_AXI_AWLEN};
+ m_wempty <= (M_AXI_AWLEN == 0);
+ m_wlastctr <= (M_AXI_AWLEN == 1);
+ end else begin
+ // The W* packet goes with the last
+ // AW* command, the AW* packet with a
+ // new one
+ m_wpending <= M_AXI_AWLEN+1;
+ m_wempty <= 0;
+ m_wlastctr <= (M_AXI_AWLEN == 0);
+ end
+ // }}}
+ end else begin
+ // {{{
+ m_wpending <= M_AXI_AWLEN+1;
+ m_wempty <= 0;
+ m_wlastctr <= (M_AXI_AWLEN == 0);
+ // }}}
+ end
+ // }}}
+ end else if (M_AXI_WVALID && M_AXI_WREADY && (!m_wempty))
+ begin
+ // {{{
+ // The AW* channel is idle, and we just accepted a value
+ // on the W* channel
+ m_wpending <= m_wpending - 1;
+ m_wempty <= (m_wpending <= 1);
+ m_wlastctr <= (m_wpending == 2);
+ // }}}
+ end
+ // }}}
+
+ // Write data processing
+ // {{{
+ // S_AXI_WREADY
+ // {{{
+ // The S_AXI_WREADY or write channel stall signal
+ //
+ // For this core, we idle at zero (stalled) until an AW* packet
+ // comes through
+ //
+ initial S_AXI_WREADY = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_WREADY <= 0;
+ else if (S_AXI_WVALID && S_AXI_WREADY)
+ begin
+ // {{{
+ if (S_AXI_WLAST)
+ S_AXI_WREADY <= 0;
+ else if (o_write_fault)
+ S_AXI_WREADY <= 1;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ S_AXI_WREADY <= 1;
+ else
+ S_AXI_WREADY <= 0;
+ // }}}
+ end else if ((s_wbursts == 0)&&(waddr_valid)
+ &&(o_write_fault || M_AXI_WREADY))
+ S_AXI_WREADY <= 1;
+ else if (S_AXI_AWVALID && S_AXI_AWREADY)
+ S_AXI_WREADY <= 1;
+ // }}}
+
+ // r_w*
+ // {{{
+ // Double buffer for the write channel
+ //
+ // As before, the r_* values contain the values in the double
+ // buffer itself
+ //
+ initial r_wvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_wvalid <= 0;
+ else if (!r_wvalid)
+ begin
+ // {{{
+ r_wvalid <= (S_AXI_WVALID && S_AXI_WREADY
+ && M_AXI_WVALID && !M_AXI_WREADY);
+ r_wdata <= S_AXI_WDATA;
+ r_wstrb <= S_AXI_WSTRB;
+ r_wlast <= S_AXI_WLAST;
+ // }}}
+ end else if (o_write_fault || M_AXI_WREADY || !M_AXI_ARESETN)
+ r_wvalid <= 0;
+ // }}}
+
+ // m_w*
+ // {{{
+ // Here's the result of our double buffer
+ //
+ // m_* references the value within the double buffer in the case that
+ // something is in the double buffer. Otherwise, it is the values
+ // directly from the inputs. In the case of a fault, neither is true.
+ // Write faults override.
+ //
+ always @(*)
+ if (o_write_fault)
+ begin
+ // {{{
+ m_wvalid = !m_wempty;
+ m_wlast = m_wlastctr;
+ m_wstrb = 0;
+ // }}}
+ end else if (r_wvalid)
+ begin
+ // {{{
+ m_wvalid = 1;
+ m_wstrb = r_wstrb;
+ m_wlast = r_wlast;
+ // }}}
+ end else begin
+ // {{{
+ m_wvalid = S_AXI_WVALID && S_AXI_WREADY;
+ m_wstrb = S_AXI_WSTRB;
+ m_wlast = S_AXI_WLAST;
+ // }}}
+ end
+ // }}}
+
+ // m_wdata
+ // {{{
+ // The logic for the DATA output of the double buffer doesn't
+ // matter so much in the case of o_write_fault
+ always @(*)
+ if (r_wvalid)
+ m_wdata = r_wdata;
+ else
+ m_wdata = S_AXI_WDATA;
+ // }}}
+
+ // M_AXI_W*
+ // {{{
+ // Set the downstream write channel values
+ //
+ // As per AXI spec, these values *must* be registered. Note that our
+ // source here is the m_* double buffer/incoming write data switch.
+ initial M_AXI_WVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ M_AXI_WVALID <= m_wvalid;
+ M_AXI_WDATA <= m_wdata;
+ M_AXI_WSTRB <= m_wstrb;
+ if (OPT_EXCLUSIVE && lock_failed)
+ M_AXI_WSTRB <= 0;
+ M_AXI_WLAST <= m_wlast;
+ end
+
+ // Override the WVALID signal (only) on reset, voiding any
+ // output we might otherwise send.
+ if (!M_AXI_ARESETN)
+ M_AXI_WVALID <= 0;
+ end
+ // }}}
+ // }}}
+
+ // Write fault detection
+ // {{{
+ // write_timer
+ // {{{
+ // The write timer
+ //
+ // The counter counts up to saturation. It is reset any time
+ // the write channel is either clear, or a value is accepted
+ // at the *MASTER* (not slave) side. Why the master side? Simply
+ // because it makes the proof below easier. (At one time I checked
+ // both, but then couldn't prove that the faults wouldn't get hit
+ // if the slave responded in time.)
+ initial write_timer = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !waddr_valid)
+ write_timer <= 0;
+ else if (!o_write_fault && M_AXI_BVALID)
+ write_timer <= 0;
+ else if (S_AXI_WREADY)
+ write_timer <= 0;
+ else if (!(&write_timer))
+ write_timer <= write_timer + 1;
+ // }}}
+
+ // write_timeout
+ // {{{
+ // Write timeout detector
+ //
+ // If the write_timer reaches the OPT_TIMEOUT, then the write_timeout
+ // will get set true. This will force a fault, taking the write
+ // channel off line.
+ initial write_timeout = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ write_timeout <= 0;
+ else if (M_AXI_BVALID)
+ write_timeout <= write_timeout;
+ else if (S_AXI_WVALID && S_AXI_WREADY)
+ write_timeout <= write_timeout;
+ else if (write_timer >= OPT_TIMEOUT)
+ write_timeout <= 1;
+ // }}}
+
+ // faulty_write_return
+ // {{{
+ // fault_write_return
+ //
+ // This combinational logic is used to catch an invalid return, and
+ // so take the slave off-line before the return can corrupt the master
+ // channel. Reasons for taking the write return off line are listed
+ // below.
+ always @(*)
+ begin
+ faulty_write_return = 0;
+ if (M_AXI_WVALID && M_AXI_WREADY
+ && M_AXI_AWVALID && !M_AXI_AWREADY)
+ // Accepting the write *data* prior to the write
+ // *address* is a fault
+ faulty_write_return = 1;
+ if (M_AXI_BVALID)
+ begin
+ if (M_AXI_AWVALID || M_AXI_WVALID)
+ // Returning a B* acknowledgement while the
+ // request remains outstanding is also a fault
+ faulty_write_return = 1;
+ if (!m_wempty)
+ // Same as above, but this time the write
+ // channel is neither complete, nor is *WVALID
+ // active. Values remain to be written,
+ // and so a return is a fault.
+ faulty_write_return = 1;
+ if (s_wbursts <= (S_AXI_BVALID ? 1:0))
+ // Too many acknowledgments
+ //
+ // Returning more than one BVALID&BREADY for
+ // every AWVALID & AWREADY is a fault.
+ faulty_write_return = 1;
+ if (M_AXI_BID != wfifo_id)
+ // An attempt to return the wrong ID
+ faulty_write_return = 1;
+ if (M_AXI_BRESP == EXOKAY && (!OPT_EXCLUSIVE
+ || !lock_enabled))
+ // An attempt to return a valid lock, without
+ // a prior request
+ faulty_write_return = 1;
+ end
+ end
+ // }}}
+
+ // o_write_fault
+ // {{{
+ // On a write fault, we're going to disconnect the write port from
+ // the slave, and return errors on each write connect. o_write_fault
+ // is our signal determining if the write channel is disconnected.
+ //
+ // Most of this work is determined within faulty_write_return above.
+ // Here we do just a bit more:
+ initial o_write_fault = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ o_write_fault <= 0;
+ else if ((!M_AXI_ARESETN&&o_read_fault) || write_timeout)
+ o_write_fault <= 1;
+ else if (M_AXI_BVALID && M_AXI_BREADY)
+ o_write_fault <= (o_write_fault) || faulty_write_return;
+ else if (M_AXI_WVALID && M_AXI_WREADY
+ && M_AXI_AWVALID && !M_AXI_AWREADY)
+ // Accepting the write data prior to the write address
+ // is a fault
+ o_write_fault <= 1;
+ // }}}
+ // }}}
+
+ // Write return generation
+ // {{{
+ // m_b*
+ // {{{
+ // Since we only ever allow a single write burst at a time, we don't
+ // need to double buffer the return channel. Hence, we'll set our
+ // return channel based upon the incoming values alone. Note that
+ // we're overriding the M_AXI_BID below, in order to make certain that
+ // the return goes to the right source.
+ //
+ always @(*)
+ if (o_write_fault)
+ begin
+ m_bvalid = (s_wbursts > (S_AXI_BVALID ? 1:0));
+ m_bid = wfifo_id;
+ m_bresp = SLAVE_ERROR;
+ end else begin
+ m_bvalid = M_AXI_BVALID;
+ if (faulty_write_return)
+ m_bvalid = 0;
+ m_bid = wfifo_id;
+ m_bresp = M_AXI_BRESP;
+ end
+ // }}}
+
+ // S_AXI_B*
+ // {{{
+ // We'll *never* stall the slaves BREADY channel
+ //
+ // If the slave returns the response we are expecting, then S_AXI_BVALID
+ // will be low and it can go directly into the S_AXI_BVALID slot. If
+ // on the other hand the slave returns M_AXI_BVALID at the wrong time,
+ // then we'll quietly accept it and send the write interface into
+ // fault detected mode, setting o_write_fault.
+ //
+ // Sadly, this will create a warning in Vivado. If/when you see it,
+ // see this note and then just ignore it.
+ assign M_AXI_BREADY = 1;
+
+ //
+ // Return a write acknowlegement at the end of every write
+ // burst--regardless of whether or not the slave does so
+ //
+ initial S_AXI_BVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_BVALID <= 0;
+ else if (!S_AXI_BVALID || S_AXI_BREADY)
+ S_AXI_BVALID <= m_bvalid;
+
+ //
+ // Set the values associated with the response
+ //
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_BVALID || S_AXI_BREADY)
+ begin
+ S_AXI_BID <= m_bid;
+ S_AXI_BRESP <= m_bresp;
+ end
+ // }}}
+
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read channel processing
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // Read address channel
+ // {{{
+
+ // S_AXI_ARREADY
+ // {{{
+ initial S_AXI_ARREADY = (OPT_SELF_RESET) ? 0:1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_ARREADY <= !OPT_SELF_RESET;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ S_AXI_ARREADY <= 0;
+ // else if (clear_fault)
+ // S_AXI_ARREADY <= 1;
+ else if (!S_AXI_ARREADY)
+ S_AXI_ARREADY <= (S_AXI_RVALID && S_AXI_RLAST && S_AXI_RREADY);
+ // }}}
+
+ // raddr_valid
+ // {{{
+ generate if (OPT_SELF_RESET)
+ begin : GEN_READ_RESET
+ assign raddr_valid = !rfifo_empty;
+ end else begin : SIMPLE_RADDR_VALID
+ assign raddr_valid = !S_AXI_ARREADY;
+ end endgenerate
+ // }}}
+
+ // r_ar*
+ // {{{
+ // Double buffer the values associated with any read address request
+ //
+ initial r_arvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (S_AXI_ARVALID && S_AXI_ARREADY)
+ begin
+ r_arvalid <= 0; // (M_AXI_ARVALID && !M_AXI_ARREADY);
+ r_arid <= S_AXI_ARID;
+ r_araddr <= S_AXI_ARADDR;
+ r_arlen <= S_AXI_ARLEN;
+ r_arsize <= S_AXI_ARSIZE;
+ r_arburst <= S_AXI_ARBURST;
+ r_arlock <= S_AXI_ARLOCK;
+ r_arcache <= S_AXI_ARCACHE;
+ r_arprot <= S_AXI_ARPROT;
+ r_arqos <= S_AXI_ARQOS;
+ end else if (M_AXI_ARREADY)
+ r_arvalid <= 0;
+
+ if (!M_AXI_ARESETN)
+ r_arvalid <= 0;
+ if (!OPT_EXCLUSIVE || !S_AXI_ARESETN)
+ r_arlock <= 1'b0;
+ end
+ // }}}
+
+ // m_ar*
+ // {{{
+ always @(*)
+ if (r_arvalid)
+ begin
+ m_arvalid = r_arvalid;
+ m_arid = r_arid;
+ m_araddr = r_araddr;
+ m_arlen = r_arlen;
+ m_arsize = r_arsize;
+ m_arburst = r_arburst;
+ m_arlock = r_arlock;
+ m_arcache = r_arcache;
+ m_arprot = r_arprot;
+ m_arqos = r_arqos;
+ end else begin
+ m_arvalid = S_AXI_ARVALID && S_AXI_ARREADY;
+ m_arid = S_AXI_ARID;
+ m_araddr = S_AXI_ARADDR;
+ m_arlen = S_AXI_ARLEN;
+ m_arsize = S_AXI_ARSIZE;
+ m_arburst = S_AXI_ARBURST;
+ m_arlock = S_AXI_ARLOCK && OPT_EXCLUSIVE;
+ m_arcache = S_AXI_ARCACHE;
+ m_arprot = S_AXI_ARPROT;
+ m_arqos = S_AXI_ARQOS;
+ end
+ // }}}
+
+ // M_AXI_AR*
+ // {{{
+ // Set the downstream values according to the transaction we've just
+ // received.
+ //
+ initial M_AXI_ARVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ begin
+
+ if (o_read_fault)
+ M_AXI_ARVALID <= 0;
+ else
+ M_AXI_ARVALID <= m_arvalid;
+
+ M_AXI_ARID <= m_arid;
+ M_AXI_ARADDR <= m_araddr;
+ M_AXI_ARLEN <= m_arlen;
+ M_AXI_ARSIZE <= m_arsize;
+ M_AXI_ARBURST <= m_arburst;
+ M_AXI_ARLOCK <= m_arlock && OPT_EXCLUSIVE;
+ M_AXI_ARCACHE <= m_arcache;
+ M_AXI_ARPROT <= m_arprot;
+ M_AXI_ARQOS <= m_arqos;
+ end
+
+ if (!M_AXI_ARESETN)
+ M_AXI_ARVALID <= 0;
+ end
+ // }}}
+
+ // }}}
+
+ //
+ // Read fault detection
+ // {{{
+
+ // read_timer
+ // {{{
+ // First step: a timer. The timer starts as soon as the S_AXI_ARVALID
+ // is accepted. Once that happens, rfifo_empty will no longer be true.
+ // The count is reset any time the slave produces a value for us to
+ // read. Once the count saturates, it stops counting.
+ initial read_timer = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ read_timer <= 0;
+ else if (rfifo_empty||(S_AXI_RVALID))
+ read_timer <= 0;
+ else if (M_AXI_RVALID)
+ read_timer <= 0;
+ else if (!(&read_timer))
+ read_timer <= read_timer + 1;
+ // }}}
+
+ // read_timeout
+ // {{{
+ // Once the counter > OPT_TIMEOUT, we have a timeout condition.
+ // We'll detect this one clock earlier below. If we ever enter
+ // a read time out condition, we'll set the read fault. The read
+ // timeout condition can only be cleared by a reset.
+ initial read_timeout = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ read_timeout <= 0;
+ else if (rfifo_empty||S_AXI_RVALID)
+ read_timeout <= read_timeout;
+ else if (M_AXI_RVALID)
+ read_timeout <= read_timeout;
+ else if (read_timer == OPT_TIMEOUT)
+ read_timeout <= 1;
+ // }}}
+
+ //
+ // faulty_read_return
+ // {{{
+ // To avoid returning a fault from the slave, it is important to
+ // determine within a single clock period whether or not the slaves
+ // return value is at fault or not. That's the purpose of the code
+ // below.
+ always @(*)
+ begin
+ faulty_read_return = 0;
+ if (M_AXI_RVALID)
+ begin
+ if (M_AXI_ARVALID)
+ // It is a fault to return data before the
+ // request has been accepted
+ faulty_read_return = 1;
+ if (M_AXI_RID != rfifo_id)
+ // It is a fault to return data from a
+ // different ID
+ faulty_read_return = 1;
+ if (rfifo_last && (S_AXI_RVALID || !M_AXI_RLAST))
+ faulty_read_return = 1;
+ if (rfifo_penultimate && S_AXI_RVALID && (r_rvalid || !M_AXI_RLAST))
+ faulty_read_return = 1;
+ if (M_AXI_RRESP == EXOKAY && (!OPT_EXCLUSIVE || !rfifo_lock))
+ faulty_read_return = 1;
+ if (OPT_EXCLUSIVE && exread && M_AXI_RRESP == OKAY)
+ // Can't switch from EXOKAY to OKAY
+ faulty_read_return = 1;
+ if ((!OPT_EXCLUSIVE || (!rfifo_first && !exread))
+ && M_AXI_RRESP == EXOKAY)
+ // Can't switch from OKAY to EXOKAY
+ faulty_read_return = 1;
+ end
+ end
+ // }}}
+
+ // o_read_fault
+ // {{{
+ initial o_read_fault = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || clear_fault)
+ o_read_fault <= 0;
+ else if ((!M_AXI_ARESETN && o_write_fault) || read_timeout)
+ o_read_fault <= 1;
+ else if (M_AXI_RVALID)
+ begin
+ if (faulty_read_return)
+ o_read_fault <= 1;
+ if (!raddr_valid)
+ // It is a fault if we haven't issued an AR* request
+ // yet, and a value is returned
+ o_read_fault <= 1;
+ end
+ // }}}
+
+ // }}}
+
+ //
+ // Read return/acknowledgment processing
+ // {{{
+
+
+ // exread
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!M_AXI_ARESETN || !OPT_EXCLUSIVE)
+ exread <= 0;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ begin
+ if (!M_AXI_RRESP[1])
+ exread <= (M_AXI_RRESP == EXOKAY);
+ end else if (S_AXI_RVALID && S_AXI_RREADY)
+ begin
+ if (S_AXI_RLAST)
+ exread <= 1'b0;
+ end
+ // }}}
+
+ // r_rvalid
+ // {{{
+ // Step one, set/create the read return double buffer. If r_rvalid
+ // is true, there's a valid value in the double buffer location.
+ //
+ initial r_rvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_read_fault)
+ r_rvalid <= 0;
+ else if (!r_rvalid)
+ begin
+ // {{{
+ // Refuse to set the double-buffer valid on any fault.
+ // That will also keep (below) M_AXI_RREADY high--so that
+ // following the fault the slave might still (pretend) to
+ // respond properly
+ if (faulty_read_return)
+ r_rvalid <= 0;
+ else if (o_read_fault)
+ r_rvalid <= 0;
+ // If there's nothing in the double buffer, then we might
+ // place something in there. The double buffer only gets
+ // filled under two conditions: 1) something is pending to be
+ // returned, and 2) there's another return that we can't
+ // stall and so need to accept here.
+ r_rvalid <= M_AXI_RVALID && M_AXI_RREADY
+ && (S_AXI_RVALID && !S_AXI_RREADY);
+ // }}}
+ end else if (S_AXI_RREADY)
+ // Once the up-stream read-channel clears, we can always
+ // clear the double buffer
+ r_rvalid <= 0;
+ // }}}
+
+ // r_rresp, r_rdata
+ // {{{
+ // Here's the actual values kept whenever r_rvalid is true. This is
+ // the double buffer. Notice that r_rid and r_last are generated
+ // locally, so not recorded here.
+ //
+ always @(posedge S_AXI_ACLK)
+ if (!r_rvalid)
+ begin
+ r_rresp <= M_AXI_RRESP;
+ r_rdata <= M_AXI_RDATA;
+ end
+ // }}}
+
+ // M_AXI_RREADY
+ // {{{
+ // Stall the downstream channel any time there's something in the
+ // double buffer. In spite of the ! here, this is a registered value.
+ assign M_AXI_RREADY = !r_rvalid;
+ // }}}
+
+ // rfifo_id, rfifo_lock
+ // {{{
+ // Copy the ID for later comparisons on the return
+ always @(*)
+ rfifo_id = r_arid;
+ always @(*)
+ rfifo_lock = r_arlock;
+ // }}}
+
+ // rfifo_[counter|empty|last|penultimate
+ // {{{
+ // Count the number of outstanding read elements. This is the number
+ // of read returns we still expect--from the upstream perspective. The
+ // downstream perspective will be off by both what's waiting for
+ // S_AXI_RREADY and what's in the double buffer
+ //
+ initial rfifo_counter = 0;
+ initial rfifo_empty = 1;
+ initial rfifo_last = 0;
+ initial rfifo_penultimate= 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ begin
+ rfifo_counter <= 0;
+ rfifo_empty <= 1;
+ rfifo_last <= 0;
+ rfifo_penultimate<= 0;
+ end else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ begin
+ rfifo_counter <= S_AXI_ARLEN+1;
+ rfifo_empty <= 0;
+ rfifo_last <= (S_AXI_ARLEN == 0);
+ rfifo_penultimate <= (S_AXI_ARLEN == 1);
+ end else if (!rfifo_empty)
+ begin
+ if (S_AXI_RVALID && S_AXI_RREADY)
+ begin
+ rfifo_counter <= rfifo_counter - 1;
+ rfifo_empty <= (rfifo_counter <= 1);
+ rfifo_last <= (rfifo_counter == 2);
+ rfifo_penultimate <= (rfifo_counter == 3);
+ end
+ end
+ // }}}
+
+ // lock_active
+ // {{{
+ generate if (OPT_EXCLUSIVE)
+ begin : CALC_LOCK_ACTIVE
+ // {{{
+ genvar gk;
+
+ for(gk=0; gk<(1<<IW); gk=gk+1)
+ begin : LOCK_PER_ID
+ reg r_lock_active;
+
+ initial r_lock_active = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !M_AXI_ARESETN
+ || faulty_read_return || faulty_write_return)
+ r_lock_active <= 0;
+ else if (M_AXI_RVALID && M_AXI_RREADY && rfifo_lock
+ && !S_AXI_ARREADY
+ && r_arid == gk[IW-1:0]
+ &&(!faulty_read_return && !o_read_fault)
+ && M_AXI_RRESP == EXOKAY)
+ r_lock_active <= 1;
+ else if (M_AXI_BVALID && M_AXI_BREADY && wfifo_lock
+ && r_awid == gk[IW-1:0]
+ && (S_AXI_ARREADY
+ || r_arid != gk[IW-1:0]
+ || !r_arlock)
+ && M_AXI_BRESP == OKAY)
+ r_lock_active <= 0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY && S_AXI_ARLOCK
+ && S_AXI_ARID == gk[IW-1:0])
+ r_lock_active <= 0;
+
+ assign lock_active[gk] = r_lock_active;
+ end
+ // }}}
+ end else begin : LOCK_NEVER_ACTIVE
+ // {{{
+ assign lock_active = 0;
+
+ // Keep Verilator happy
+ // Verilator lint_off UNUSED
+ wire unused_lock;
+ assign unused_lock = &{ 1'b0, wfifo_lock };
+ // Verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+ // }}}
+
+ // rfifo_first
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || !OPT_EXCLUSIVE)
+ rfifo_first <= 1'b0;
+ else if (S_AXI_ARVALID && S_AXI_ARREADY)
+ rfifo_first <= 1'b1;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ rfifo_first <= 1'b0;
+ else if (o_read_fault)
+ rfifo_first <= 1'b0;
+ // }}}
+
+ // m_rvalid, m_rresp
+ // {{{
+ // Determine values to send back and return
+ //
+ // This data set includes all the return values, even though only
+ // RRESP and RVALID are set in this block.
+ always @(*)
+ if (o_read_fault || (!M_AXI_ARESETN && OPT_SELF_RESET))
+ begin
+ m_rvalid = !rfifo_empty;
+ if (S_AXI_RVALID && rfifo_last)
+ m_rvalid = 0;
+ m_rresp = SLAVE_ERROR;
+ end else if (r_rvalid)
+ begin
+ m_rvalid = r_rvalid;
+ m_rresp = r_rresp;
+ end else begin
+ m_rvalid = M_AXI_RVALID && raddr_valid && !faulty_read_return;
+ m_rresp = M_AXI_RRESP;
+ end
+ // }}}
+
+ // m_rid
+ // {{{
+ // We've stored the ID locally, so that our response will never be in
+ // error
+ always @(*)
+ m_rid = rfifo_id;
+ // }}}
+
+ // m_rlast
+ // {{{
+ // Ideally, we'd want to say m_rlast = rfifo_last. However, we might
+ // have a value in S_AXI_RVALID already. In that case, the last
+ // value can only be true if we are one further away from the end.
+ // (Remember, rfifo_last and rfifo_penultimate are both dependent upon
+ // the *upstream* number of read values outstanding, not the downstream
+ // number which we can't trust in all cases)
+ always @(*)
+ if (S_AXI_RVALID)
+ m_rlast = rfifo_penultimate;
+ else
+ m_rlast = (rfifo_last);
+ // }}}
+
+ // m_rdata
+ // {{{
+ // In the case of a fault, rdata is a don't care. Therefore we can
+ // always set it based upon the values returned from the slave.
+ always @(*)
+ if (r_rvalid)
+ m_rdata = r_rdata;
+ else
+ m_rdata = M_AXI_RDATA;
+ // }}}
+
+ // S_AXI_R*
+ // {{{
+ // Record our return data values
+ //
+ // These are the values from either the slave, the double buffer,
+ // or an error value bypassing either as determined by the m_* values.
+ // Per spec, all of these values must be registered. First the valid
+ // signal
+ initial S_AXI_RVALID = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ S_AXI_RVALID <= 0;
+ else if (!S_AXI_RVALID || S_AXI_RREADY)
+ S_AXI_RVALID <= m_rvalid;
+
+ // Then the various slave data channels
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_RVALID || S_AXI_RREADY)
+ begin
+ S_AXI_RID <= m_rid;
+ S_AXI_RRESP <= m_rresp;
+ S_AXI_RLAST <= m_rlast;
+ S_AXI_RDATA <= m_rdata;
+ end
+ // }}}
+
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Self-reset
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (OPT_SELF_RESET)
+ begin : GEN_SELFRESET
+ // {{{
+ // Declarations
+ // {{{
+ reg [4:0] reset_counter;
+ reg r_clear_fault, w_clear_fault;
+ wire reset_timeout;
+ // }}}
+
+ // M_AXI_ARESETN
+ // {{{
+ initial M_AXI_ARESETN = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ M_AXI_ARESETN <= 0;
+ else if (clear_fault)
+ M_AXI_ARESETN <= 1;
+ else if (o_read_fault || o_write_fault)
+ M_AXI_ARESETN <= 0;
+ // }}}
+
+ // reset_counter
+ // {{{
+ initial reset_counter = 0;
+ always @(posedge S_AXI_ACLK)
+ if (M_AXI_ARESETN)
+ reset_counter <= 0;
+ else if (!reset_timeout)
+ reset_counter <= reset_counter+1;
+ // }}}
+
+ assign reset_timeout = reset_counter[4];
+
+ // r_clear_fault
+ // {{{
+ initial r_clear_fault <= 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_clear_fault <= 1;
+ else if (!M_AXI_ARESETN && !reset_timeout)
+ r_clear_fault <= 0;
+ else if (!o_read_fault && !o_write_fault)
+ r_clear_fault <= 0;
+ else if (raddr_valid || waddr_valid)
+ r_clear_fault <= 0;
+ else
+ r_clear_fault <= 1;
+ // }}}
+
+ // clear_fault
+ // {{{
+ always @(*)
+ if (S_AXI_AWVALID || S_AXI_ARVALID)
+ w_clear_fault = 0;
+ else if (raddr_valid || waddr_valid)
+ w_clear_fault = 0;
+ else
+ w_clear_fault = r_clear_fault;
+
+ assign clear_fault = w_clear_fault;
+ // }}}
+
+ // }}}
+ end else begin : NO_SELFRESET
+ // {{{
+ always @(*)
+ M_AXI_ARESETN = S_AXI_ARESETN;
+ assign clear_fault = 0;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0 };
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // {{{
+ // Below are just some of the formal properties used to verify
+ // this core. The full property set is maintained elsewhere.
+ //
+ parameter [0:0] F_OPT_FAULTLESS = 1;
+
+ //
+ // ...
+ // }}}
+
+ faxi_slave #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_S_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH)
+ // ...
+ // }}}
+ ) f_slave(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ // Address write channel
+ // {{{
+ .i_axi_awid(S_AXI_AWID),
+ .i_axi_awaddr(S_AXI_AWADDR),
+ .i_axi_awlen(S_AXI_AWLEN),
+ .i_axi_awsize(S_AXI_AWSIZE),
+ .i_axi_awburst(S_AXI_AWBURST),
+ .i_axi_awlock(S_AXI_AWLOCK),
+ .i_axi_awcache(S_AXI_AWCACHE),
+ .i_axi_awprot(S_AXI_AWPROT),
+ .i_axi_awqos(S_AXI_AWQOS),
+ .i_axi_awvalid(S_AXI_AWVALID),
+ .i_axi_awready(S_AXI_AWREADY),
+ // }}}
+ // Write Data Channel
+ // {{{
+ .i_axi_wdata(S_AXI_WDATA),
+ .i_axi_wstrb(S_AXI_WSTRB),
+ .i_axi_wlast(S_AXI_WLAST),
+ .i_axi_wvalid(S_AXI_WVALID),
+ .i_axi_wready(S_AXI_WREADY),
+ // }}}
+ // Write response channel
+ // {{{
+ .i_axi_bid(S_AXI_BID),
+ .i_axi_bresp(S_AXI_BRESP),
+ .i_axi_bvalid(S_AXI_BVALID),
+ .i_axi_bready(S_AXI_BREADY),
+ // }}}
+ // Read address channel
+ // {{{
+ .i_axi_arid(S_AXI_ARID),
+ .i_axi_araddr(S_AXI_ARADDR),
+ .i_axi_arlen(S_AXI_ARLEN),
+ .i_axi_arsize(S_AXI_ARSIZE),
+ .i_axi_arburst(S_AXI_ARBURST),
+ .i_axi_arlock(S_AXI_ARLOCK),
+ .i_axi_arcache(S_AXI_ARCACHE),
+ .i_axi_arprot(S_AXI_ARPROT),
+ .i_axi_arqos(S_AXI_ARQOS),
+ .i_axi_arvalid(S_AXI_ARVALID),
+ .i_axi_arready(S_AXI_ARREADY),
+ // }}}
+ // Read data return channel
+ // {{{
+ .i_axi_rvalid(S_AXI_RVALID),
+ .i_axi_rready(S_AXI_RREADY),
+ .i_axi_rid(S_AXI_RID),
+ .i_axi_rdata(S_AXI_RDATA),
+ .i_axi_rresp(S_AXI_RRESP),
+ .i_axi_rlast(S_AXI_RLAST),
+ // }}}
+ // Formal induction values
+ // {{{
+ .f_axi_awr_nbursts(f_axi_awr_nbursts),
+ .f_axi_wr_pending(f_axi_wr_pending),
+ .f_axi_rd_nbursts(f_axi_rd_nbursts),
+ .f_axi_rd_outstanding(f_axi_rd_outstanding)
+ //
+ // ...
+ // }}}
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // 1. We will only ever handle a single burst--never any more
+ always @(*)
+ assert(f_axi_awr_nbursts <= 1);
+
+ always @(*)
+ if (f_axi_awr_nbursts == 1)
+ begin
+ assert(!S_AXI_AWREADY);
+ if (S_AXI_BVALID)
+ assert(f_axi_wr_pending == 0);
+ if (!o_write_fault && M_AXI_AWVALID)
+ assert(f_axi_wr_pending == M_AXI_AWLEN + 1
+ - (M_AXI_WVALID ? 1:0)
+ - (r_wvalid ? 1 : 0));
+ if (!o_write_fault && f_axi_wr_pending > 0)
+ assert((!M_AXI_WVALID || !M_AXI_WLAST)
+ &&(!r_wvalid || !r_wlast));
+ if (!o_write_fault && M_AXI_WVALID && M_AXI_WLAST)
+ assert(!r_wvalid || !r_wlast);
+ end else begin
+ assert(s_wbursts == 0);
+ assert(!S_AXI_WREADY);
+ if (OPT_SELF_RESET)
+ begin
+ assert(1 || S_AXI_AWREADY || !M_AXI_ARESETN || !S_AXI_ARESETN);
+ end else
+ assert(S_AXI_AWREADY);
+ end
+
+ //
+ // ...
+ //
+
+ //
+ // AWREADY will always be low mid-burst
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assert(S_AXI_AWREADY == !OPT_SELF_RESET);
+ end else if (f_axi_wr_pending > 0)
+ begin
+ assert(!S_AXI_AWREADY);
+ end else if (s_wbursts)
+ begin
+ assert(!S_AXI_AWREADY);
+ end else if (!OPT_SELF_RESET)
+ begin
+ assert(S_AXI_AWREADY);
+ end else if (!o_write_fault && !o_read_fault)
+ assert(S_AXI_AWREADY || OPT_SELF_RESET);
+
+ always @(*)
+ if (f_axi_wr_pending > 0)
+ begin
+ assert(s_wbursts == 0);
+ end else if (f_axi_awr_nbursts > 0)
+ begin
+ assert((s_wbursts ? 1:0) == f_axi_awr_nbursts);
+ end else
+ assert(s_wbursts == 0);
+
+ always @(*)
+ if (!o_write_fault && S_AXI_AWREADY)
+ assert(!M_AXI_AWVALID);
+
+ always @(*)
+ if (M_AXI_ARESETN && (f_axi_awr_nbursts == 0))
+ begin
+ assert(o_write_fault || !M_AXI_AWVALID);
+ assert(!S_AXI_BVALID);
+ assert(s_wbursts == 0);
+ end else if (M_AXI_ARESETN && s_wbursts == 0)
+ assert(f_axi_wr_pending > 0);
+
+ always @(*)
+ if (S_AXI_WREADY)
+ begin
+ assert(waddr_valid);
+ end else if (f_axi_wr_pending > 0)
+ begin
+ if (!o_write_fault)
+ assert(M_AXI_WVALID && r_wvalid);
+ end
+
+ always @(*)
+ if (!OPT_SELF_RESET)
+ begin
+ assert(waddr_valid == !S_AXI_AWREADY);
+ end else begin
+ // ...
+ assert(waddr_valid == (f_axi_awr_nbursts > 0));
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN && !o_write_fault && f_axi_wr_pending > 0 && !S_AXI_WREADY)
+ assert(M_AXI_WVALID);
+
+ always @(*)
+ if (S_AXI_ARESETN && !o_write_fault && m_wempty)
+ begin
+ assert(M_AXI_AWVALID || !M_AXI_WVALID);
+ assert(M_AXI_AWVALID || f_axi_wr_pending == 0);
+ end
+
+ always @(*)
+ if (S_AXI_ARESETN && M_AXI_AWVALID)
+ begin
+ if (!o_write_fault && !M_AXI_AWVALID)
+ begin
+ assert(f_axi_wr_pending + (M_AXI_WVALID ? 1:0) + (r_wvalid ? 1:0) == m_wpending);
+ end else if (!o_write_fault)
+ begin
+ assert(f_axi_wr_pending == M_AXI_AWLEN + 1 - (M_AXI_WVALID ? 1:0) - (r_wvalid ? 1:0));
+ assert(m_wpending == 0);
+ end
+ end
+
+ always @(*)
+ assert(m_wpending <= 9'h100);
+
+ always @(*)
+ if (M_AXI_ARESETN && (!o_write_fault)&&(f_axi_awr_nbursts == 0))
+ assert(!M_AXI_AWVALID);
+
+ always @(*)
+ if (S_AXI_ARESETN && !o_write_fault)
+ begin
+ if (f_axi_awr_nbursts == 0)
+ begin
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ end
+
+ if (!M_AXI_AWVALID)
+ assert(f_axi_wr_pending
+ +(M_AXI_WVALID ? 1:0)
+ + (r_wvalid ? 1:0)
+ == m_wpending);
+ if (f_axi_awr_nbursts == (S_AXI_BVALID ? 1:0))
+ begin
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ end
+
+ if (S_AXI_BVALID)
+ assert(f_axi_awr_nbursts == 1);
+
+ if (M_AXI_AWVALID)
+ assert(m_wpending == 0);
+
+ if (S_AXI_AWREADY)
+ assert(!M_AXI_AWVALID);
+ end
+
+ always @(*)
+ assert(!r_awvalid);
+
+ always @(*)
+ assert(m_wempty == (m_wpending == 0));
+ always @(*)
+ assert(m_wlastctr == (m_wpending == 1));
+
+ //
+ // Verify the contents of the skid buffers
+ //
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (write_timer > OPT_TIMEOUT)
+ assert(o_write_fault || write_timeout);
+
+ always @(*)
+ if (!OPT_SELF_RESET)
+ begin
+ assert(waddr_valid == !S_AXI_AWREADY);
+ end else if (waddr_valid)
+ assert(!S_AXI_AWREADY);
+
+ always @(*)
+ if (!o_write_fault && M_AXI_ARESETN)
+ assert(waddr_valid == !S_AXI_AWREADY);
+
+ always @(*)
+ if (f_axi_wr_pending == 0)
+ assert(!S_AXI_WREADY);
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read induction properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ assert(f_axi_rd_nbursts <= 1);
+ always @(*)
+ assert(raddr_valid == (f_axi_rd_nbursts>0));
+ always @(*)
+ if (f_axi_rdid_nbursts > 0)
+ begin
+ assert(rfifo_id == f_axi_rd_checkid);
+ end else if (f_axi_rd_nbursts > 0)
+ assert(rfifo_id != f_axi_rd_checkid);
+
+ always @(*)
+ if (f_axi_rd_nbursts > 0)
+ assert(raddr_valid);
+
+ always @(*)
+ if (raddr_valid)
+ assert(!S_AXI_ARREADY);
+
+ always @(*)
+ if (!OPT_SELF_RESET)
+ begin
+ assert(raddr_valid == !S_AXI_ARREADY);
+ end else begin
+ // ...
+ assert(raddr_valid == (f_axi_rd_nbursts > 0));
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (f_axi_rd_outstanding == 0)
+ assert(!raddr_valid || OPT_SELF_RESET);
+
+ always @(*)
+ if (!o_read_fault && S_AXI_ARREADY)
+ assert(!M_AXI_ARVALID);
+
+ // Our "FIFO" counter
+ always @(*)
+ assert(rfifo_counter == f_axi_rd_outstanding);
+
+ always @(*)
+ begin
+ assert(rfifo_empty == (rfifo_counter == 0));
+ assert(rfifo_last == (rfifo_counter == 1));
+ assert(rfifo_penultimate == (rfifo_counter == 2));
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_arvalid)
+ assert(skid_arvalid);
+
+ always @(*)
+ if (read_timer > OPT_TIMEOUT)
+ assert(read_timeout);
+
+
+ always @(posedge S_AXI_ACLK)
+ if (OPT_SELF_RESET && (!f_past_valid || !$past(M_AXI_ARESETN)))
+ begin
+ assume(!M_AXI_BVALID);
+ assume(!M_AXI_RVALID);
+ end
+
+ always @(*)
+ if (!o_read_fault && M_AXI_ARESETN)
+ assert(raddr_valid == !S_AXI_ARREADY);
+
+ always @(*)
+ if (f_axi_rd_nbursts > 0)
+ assert(raddr_valid);
+
+ always @(*)
+ if (S_AXI_ARESETN && OPT_SELF_RESET)
+ begin
+ if (!M_AXI_ARESETN)
+ assert(o_read_fault || o_write_fault /* ... */ );
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Exclusive access property checking
+ // {{{
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // ...
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Good interface check
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ generate if (F_OPT_FAULTLESS)
+ begin : ASSUME_FAULTLESS
+ // {{{
+ //
+ // ...
+ //
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_S_AXI_ID_WIDTH),
+ .C_AXI_DATA_WIDTH(C_S_AXI_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_S_AXI_ADDR_WIDTH)
+ // ...
+ // }}}
+ ) f_master(
+ // {{{
+ .i_clk(S_AXI_ACLK),
+ .i_axi_reset_n(M_AXI_ARESETN),
+ //
+ // Address write channel
+ // {{{
+ .i_axi_awid(M_AXI_AWID),
+ .i_axi_awaddr(M_AXI_AWADDR),
+ .i_axi_awlen(M_AXI_AWLEN),
+ .i_axi_awsize(M_AXI_AWSIZE),
+ .i_axi_awburst(M_AXI_AWBURST),
+ .i_axi_awlock(M_AXI_AWLOCK),
+ .i_axi_awcache(M_AXI_AWCACHE),
+ .i_axi_awprot(M_AXI_AWPROT),
+ .i_axi_awqos(M_AXI_AWQOS),
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ // }}}
+ // Write Data Channel
+ // {{{
+ .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_wlast(M_AXI_WLAST),
+ // }}}
+ // Write response channel
+ // {{{
+ .i_axi_bvalid(M_AXI_BVALID),
+ .i_axi_bready(M_AXI_BREADY),
+ .i_axi_bid(M_AXI_BID),
+ .i_axi_bresp(M_AXI_BRESP),
+ // }}}
+ // Read address channel
+ // {{{
+ .i_axi_arid(M_AXI_ARID),
+ .i_axi_araddr(M_AXI_ARADDR),
+ .i_axi_arlen(M_AXI_ARLEN),
+ .i_axi_arsize(M_AXI_ARSIZE),
+ .i_axi_arburst(M_AXI_ARBURST),
+ .i_axi_arlock(M_AXI_ARLOCK),
+ .i_axi_arcache(M_AXI_ARCACHE),
+ .i_axi_arprot(M_AXI_ARPROT),
+ .i_axi_arqos(M_AXI_ARQOS),
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ // }}}
+ // Read data return channel
+ // {{{
+ .i_axi_rvalid(M_AXI_RVALID),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rid(M_AXI_RID),
+ .i_axi_rdata(M_AXI_RDATA),
+ .i_axi_rresp(M_AXI_RRESP),
+ .i_axi_rlast(M_AXI_RLAST),
+ // }}}
+ // Formal outputs
+ // {{{
+ .f_axi_awr_nbursts(fm_axi_awr_nbursts),
+ .f_axi_wr_pending(fm_axi_wr_pending),
+ .f_axi_rd_nbursts(fm_axi_rd_nbursts),
+ .f_axi_rd_outstanding(fm_axi_rd_outstanding)
+ //
+ // ...
+ // }}}
+ // }}}
+ );
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Contract: If the downstream has no faults, then we
+ // shouldn't detect any here.
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ always @(*)
+ if (OPT_SELF_RESET)
+ begin
+ assert(!o_write_fault || !M_AXI_ARESETN);
+ end else
+ assert(!o_write_fault);
+
+ always @(*)
+ if (OPT_SELF_RESET)
+ begin
+ assert(!o_read_fault || !M_AXI_ARESETN);
+ end else
+ assert(!o_read_fault);
+
+ always @(*)
+ if (OPT_SELF_RESET)
+ begin
+ assert(!read_timeout || !M_AXI_ARESETN);
+ end else
+ assert(!read_timeout);
+
+ always @(*)
+ if (OPT_SELF_RESET)
+ begin
+ assert(!write_timeout || !M_AXI_ARESETN);
+ end else
+ assert(!write_timeout);
+
+ always @(*)
+ if (S_AXI_AWREADY)
+ assert(!M_AXI_AWVALID);
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (M_AXI_ARESETN && f_axi_rd_nbursts > 0)
+ assert(f_axi_rd_nbursts == fm_axi_rd_nbursts
+ + (M_AXI_ARVALID ? 1 : 0)
+ + (r_rvalid&&m_rlast ? 1 : 0)
+ + (S_AXI_RVALID&&S_AXI_RLAST ? 1 : 0));
+
+ always @(*)
+ if (M_AXI_ARESETN && f_axi_rd_outstanding > 0)
+ assert(f_axi_rd_outstanding == fm_axi_rd_outstanding
+ + (M_AXI_ARVALID ? M_AXI_ARLEN+1 : 0)
+ + (r_rvalid ? 1 : 0)
+ + (S_AXI_RVALID ? 1 : 0));
+
+ //
+ // ...
+ //
+ always @(*)
+ if (M_AXI_RVALID)
+ assert(!M_AXI_ARVALID);
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ assert(m_wpending == fm_axi_wr_pending);
+
+ always @(*)
+ if (S_AXI_ARESETN && fm_axi_awr_nbursts > 0)
+ begin
+ assert(fm_axi_awr_nbursts== f_axi_awr_nbursts);
+ assert(fm_axi_awr_nbursts == 1);
+ //
+ // ...
+ //
+ end
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // Exclusive address checking
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////
+ //
+ // "Careless" assumptions
+ // {{{
+ ////////////////////////////////////////////////////////////////
+ //
+ //
+ end endgenerate
+
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Never path check
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ (* anyconst *) reg [C_S_AXI_ADDR_WIDTH-1:0] fc_never_write_addr,
+ fc_never_read_addr;
+ (* anyconst *) reg [C_S_AXI_DATA_WIDTH + C_S_AXI_DATA_WIDTH/8-1:0]
+ fc_never_write_data;
+ (* anyconst *) reg [C_S_AXI_DATA_WIDTH-1:0] fc_never_read_data;
+
+ // Write address
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && S_AXI_AWVALID)
+ assume(S_AXI_AWADDR != fc_never_write_addr);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !o_write_fault && M_AXI_ARESETN && M_AXI_AWVALID)
+ assert(M_AXI_AWADDR != fc_never_write_addr);
+ // }}}
+
+ // Write data
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && S_AXI_WVALID)
+ assume({ S_AXI_WDATA, S_AXI_WSTRB } != fc_never_write_data);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !o_write_fault && M_AXI_ARESETN && M_AXI_WVALID)
+ begin
+ if (lock_failed)
+ assert(M_AXI_WSTRB == 0);
+ else
+ assert({ M_AXI_WDATA, M_AXI_WSTRB } != fc_never_write_data);
+ end
+ // }}}
+
+ // Read address
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && S_AXI_ARVALID)
+ assume(S_AXI_ARADDR != fc_never_read_addr);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && r_arvalid)
+ assume(r_araddr != fc_never_read_addr);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !o_read_fault && M_AXI_ARESETN && M_AXI_ARVALID)
+ assert(M_AXI_ARADDR != fc_never_read_addr);
+ // }}}
+
+ // Read data
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && M_AXI_ARESETN && M_AXI_RVALID)
+ assume(M_AXI_RDATA != fc_never_read_data);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && S_AXI_RVALID && S_AXI_RRESP != SLAVE_ERROR)
+ assert(S_AXI_RDATA != fc_never_read_data);
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // We'll use these to determine the best performance this core
+ // can achieve
+ //
+ reg [4:0] f_dbl_rd_count, f_dbl_wr_count;
+ reg [4:0] f_rd_count, f_wr_count;
+
+ // f_wr_count
+ // {{{
+ initial f_wr_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_write_fault)
+ f_wr_count <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ begin
+ if (!(&f_wr_count))
+ f_wr_count <= f_wr_count + 1;
+ end
+ // }}}
+
+ // f_dbl_wr_count
+ // {{{
+ initial f_dbl_wr_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_write_fault||(S_AXI_AWVALID && S_AXI_AWLEN < 3))
+ f_dbl_wr_count <= 0;
+ else if (S_AXI_BVALID && S_AXI_BREADY)
+ begin
+ if (!(&f_dbl_wr_count))
+ f_dbl_wr_count <= f_dbl_wr_count + 1;
+ end
+ // }}}
+
+ // f_rd_count
+ // {{{
+ initial f_rd_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_read_fault)
+ f_rd_count <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
+ begin
+ if (!(&f_rd_count))
+ f_rd_count <= f_rd_count + 1;
+ end
+ // }}}
+
+ // f_dbl_rd_count
+ // {{{
+ initial f_dbl_rd_count = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || o_read_fault||(S_AXI_ARVALID && S_AXI_ARLEN < 3))
+ f_dbl_rd_count <= 0;
+ else if (S_AXI_RVALID && S_AXI_RREADY && S_AXI_RLAST)
+ begin
+ if (!(&f_dbl_rd_count))
+ f_dbl_rd_count <= f_dbl_rd_count + 1;
+ end
+ // }}}
+
+ // Can we complete a single write burst without fault?
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_wr_count > 1) && (!o_write_fault));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_dbl_wr_count > 1) && (!o_write_fault));
+ // }}}
+
+ // Can we complete four write bursts without fault? (and return to idle)
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_wr_count > 3) && (!o_write_fault)
+ &&(!S_AXI_AWVALID && !S_AXI_WVALID
+ && !S_AXI_BVALID)
+ && (f_axi_awr_nbursts == 0)
+ && (f_axi_wr_pending == 0));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_dbl_wr_count > 3) && (!o_write_fault)
+ &&(!S_AXI_AWVALID && !S_AXI_WVALID
+ && !S_AXI_BVALID)
+ && (f_axi_awr_nbursts == 0)
+ && (f_axi_wr_pending == 0));
+ // }}}
+
+ // Can we complete a single read burst without fault?
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_rd_count > 1) && (!o_read_fault));
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_dbl_rd_count > 1) && (!o_read_fault));
+ // }}}
+
+ // Can we complete four read bursts without fault?
+ // {{{
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_rd_count > 3) && (f_axi_rd_nbursts == 0)
+ && !S_AXI_ARVALID && !S_AXI_RVALID);
+
+ always @(*)
+ if (S_AXI_ARESETN)
+ cover((f_dbl_rd_count > 3) && (f_axi_rd_nbursts == 0)
+ && !S_AXI_ARVALID && !S_AXI_RVALID);
+ // }}}
+`endif
+// }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif