summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/axivfifo.v
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/wb2axip/axivfifo.v')
-rw-r--r--rtl/wb2axip/axivfifo.v1405
1 files changed, 1405 insertions, 0 deletions
diff --git a/rtl/wb2axip/axivfifo.v b/rtl/wb2axip/axivfifo.v
new file mode 100644
index 0000000..2dc5369
--- /dev/null
+++ b/rtl/wb2axip/axivfifo.v
@@ -0,0 +1,1405 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axivfifo.v
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: A virtual FIFO, using an AXI based memory on the back end. Data
+// written via the AXI stream interface is written to an external
+// memory once enough is available to fill a burst. It's then copied from
+// this external memory to a FIFO from which it can drive an outgoing
+// stream.
+//
+// Registers:
+// This core is simple--providing no control interface nor registers
+// whereby it may be controlled. To place it in a particular region of
+// SDRAM, limit the address width and fill the rest of the address with
+// the region you want. Note: THIS CORE DEPENDS UPON ALIGNED MEMORY
+// ACCESSES. Hence, it must be aligned to the memory to keep these
+// accesses aligned.
+//
+// Performance goals:
+// 100% throughput
+// Stay off the bus until you can drive it hard
+//
+// 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
+//
+// `define AXI3
+// }}}
+module axivfifo #(
+ // {{{
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter C_AXI_ADDR_WIDTH = 32,
+ parameter C_AXI_DATA_WIDTH = 32,
+ //
+ // This core requires that the stream data width be identical
+ // to the bus data width. Use an upstream core if you need to
+ // pack a smaller width into your bus's width, or a downstream
+ // core if you need to unpack it.
+ localparam C_AXIS_DATA_WIDTH = C_AXI_DATA_WIDTH,
+ //
+ // LGMAXBURST determines the size of the maximum AXI burst.
+ // In AXI4, the maximum burst size is 256 beats the log_2()
+ // of which is 8. In AXI3, it's a 16 beat burst of which the
+ // log_2() is 4. Smaller numbers are also permissible here,
+ // although not verified. I expect problems if LGMAXBURST is
+ // ever set to zero (no bursting). An upgrade should fix that.
+ // Lower LGMAXBURST values will decrease the latency in this
+ // core while possibly causing throughput to be decreased
+ // (in the rest of the system--this core can handle 100%
+ // throughput either way.)
+ //
+ // Beware of the AXI requirement that bursts cannot cross
+ // 4kB boundaries. If your bus is larger than 128 bits wide,
+ // you'll need to lower this maximum burst size to meet that
+ // requirement.
+`ifdef AXI3
+ parameter LGMAXBURST=4, // 16 beats max
+`else
+ parameter LGMAXBURST=8, // 256 beats
+`endif
+ //
+ // LGFIFO: This is the (log-based-2) size of the internal FIFO.
+ // Hence if LGFIFO=8, the internal FIFO will have 256 elements
+ // (words) in it. High throughput transfers are accomplished
+ // by first storing data into a FIFO, then once a full burst
+ // size is available bursting that data over the bus. In
+ // order to be able to keep receiving data while bursting it
+ // out, the FIFO size must be at least twice the size of the
+ // maximum burst size--that is LGFIFO must be at least one more
+ // than LGMAXBURST. Larger sizes are possible as well.
+ parameter LGFIFO = LGMAXBURST+1, // 512 element FIFO
+ //
+ // AXI uses ID's to transfer information. This core rather
+ // ignores them. Instead, it uses a constant ID for all
+ // transfers. The following two parameters control that ID.
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_READ_ID = 0,
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_WRITE_ID = 0,
+ //
+ localparam ADDRLSB= $clog2(C_AXI_DATA_WIDTH)-3
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The AXI4-stream interface
+ // {{{
+ // This core does not support TLAST, TKEEP, or TSTRB. If you
+ // want to support these extra values, expand the width of
+ // TDATA, and unpack them on the output of the FIFO.
+ //
+ // The incoming stream
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXIS_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ //
+ // The outgoing stream
+ output wire M_AXIS_TVALID,
+ input wire M_AXIS_TREADY,
+ output reg [C_AXIS_DATA_WIDTH-1:0] M_AXIS_TDATA,
+ // }}}
+ //
+ // The AXI Master (DMA) interface
+ // {{{
+ // First to write data to the (external) AXI buffer
+ output wire M_AXI_AWVALID,
+ input wire M_AXI_AWREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR,
+`ifdef AXI3
+ output wire [3:0] M_AXI_AWLEN,
+`else
+ output wire [7:0] M_AXI_AWLEN,
+`endif
+ output wire [2:0] M_AXI_AWSIZE,
+ output wire [1:0] M_AXI_AWBURST,
+`ifdef AXI3
+ output wire [1:0] M_AXI_AWLOCK,
+`else
+ output wire M_AXI_AWLOCK,
+`endif
+ output wire [3:0] M_AXI_AWCACHE,
+ output wire [2:0] M_AXI_AWPROT,
+ output wire [3:0] M_AXI_AWQOS,
+ //
+ //
+ output wire M_AXI_WVALID,
+ input wire M_AXI_WREADY,
+`ifdef AXI3
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_WID,
+`endif
+ output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA,
+ output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB,
+ output wire M_AXI_WLAST,
+ //
+ //
+ input wire M_AXI_BVALID,
+ output wire M_AXI_BREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID,
+ input wire [1:0] M_AXI_BRESP,
+ //
+ // Then the read interface to read the data back
+ output wire M_AXI_ARVALID,
+ input wire M_AXI_ARREADY,
+ output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID,
+ output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR,
+`ifdef AXI3
+ output wire [3:0] M_AXI_ARLEN,
+`else
+ output wire [7:0] M_AXI_ARLEN,
+`endif
+ output wire [2:0] M_AXI_ARSIZE,
+ output wire [1:0] M_AXI_ARBURST,
+`ifdef AXI3
+ output wire [1:0] M_AXI_ARLOCK,
+`else
+ output wire M_AXI_ARLOCK,
+`endif
+ output wire [3:0] M_AXI_ARCACHE,
+ output wire [2:0] M_AXI_ARPROT,
+ output wire [3:0] M_AXI_ARQOS,
+ //
+ input wire M_AXI_RVALID,
+ output wire M_AXI_RREADY,
+ input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID,
+ input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA,
+ input wire M_AXI_RLAST,
+ input wire [1:0] M_AXI_RRESP,
+ // }}}
+ //
+ // {{{
+ // Request a soft-reset: reset the FIFO without resetting th bus
+ input wire i_reset,
+ // o_err is a hard error. If ever true, the core will come
+ // to a hard stop.
+ output reg o_err,
+ // o_overflow is an indication of data changing before it is
+ // accepted.
+ output reg o_overflow,
+ // o_mm2s_full is a reference to the last FIFO in our
+ // processing pipeline. It's true if a burst of (1<<LGMAXBURST)
+ // words of (1<<ADDRLSB) bytes may be read from the downstream
+ // FIFO without waiting on the external memory.
+ output reg o_mm2s_full,
+ // o_empty is true if nothing is in the core.
+ output reg o_empty,
+ // o_fill counts the number of items in the core. Just because
+ // the number of items is non-zero, however, doesn't mean you
+ // can read them out. In general, you will need to write at
+ // least a full (1<<LGMAXBURST) words to the core, those will
+ // need to be written to memory, read from memory, and then
+ // used to fill the downstream FIFO before you can read. This
+ // number is just available for your informational use.
+ output reg [C_AXI_ADDR_WIDTH-ADDRLSB:0] o_fill
+ // }}}
+ // }}}
+ );
+
+ // Register and signal definitions
+ // {{{
+ // The number of beats in this maximum burst size is automatically
+ // determined from LGMAXBURST, and so its forced to be a power of
+ // two this way.
+ localparam MAXBURST=(1<<LGMAXBURST);
+ localparam BURSTAW = C_AXI_ADDR_WIDTH-LGMAXBURST-ADDRLSB;
+
+ reg soft_reset, vfifo_empty, vfifo_full;
+ wire reset_fifo;
+ reg [C_AXI_ADDR_WIDTH-ADDRLSB:0] vfifo_fill;
+ reg [BURSTAW:0] mem_data_available_w,
+ writes_outstanding;
+ reg [BURSTAW:0] mem_space_available_w,
+ reads_outstanding;
+ reg s_last_stalled;
+ reg [C_AXI_DATA_WIDTH-1:0] s_last_tdata;
+ wire read_from_fifo, ififo_full, ififo_empty;
+ wire [C_AXI_DATA_WIDTH-1:0] ififo_data;
+ wire [LGFIFO:0] ififo_fill;
+
+ reg start_write, phantom_write,
+ axi_awvalid, axi_wvalid, axi_wlast,
+ writes_idle;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_awaddr;
+ reg [LGMAXBURST:0] writes_pending;
+
+ reg start_read, phantom_read, reads_idle,
+ axi_arvalid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_araddr;
+
+ reg skd_valid;
+ reg [C_AXI_DATA_WIDTH-1:0] skd_data;
+
+ reg [LGFIFO:0] ofifo_space_available;
+ wire write_to_fifo, ofifo_empty, ofifo_full;
+ wire [LGFIFO:0] ofifo_fill;
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Global FIFO signal handling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // A soft reset
+ // {{{
+ // This is how we reset the FIFO without resetting the rest of the AXI
+ // bus. On a reset request, we raise the soft_reset flag and reset all
+ // of our internal FIFOs. We also stop issuing bus commands. Once all
+ // outstanding bus commands come to a halt, then we release from reset
+ // and start operating as a FIFO.
+ initial soft_reset = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ soft_reset <= 0;
+ else if (i_reset)
+ soft_reset <= 1;
+ else if (writes_idle && reads_idle)
+ soft_reset <= 0;
+
+ assign reset_fifo = soft_reset || !S_AXI_ARESETN;
+ // }}}
+
+ //
+ // Calculating the fill of the virtual FIFO, and the associated
+ // full/empty flags that go with it
+ // {{{
+ initial vfifo_fill = 0;
+ initial vfifo_empty = 1;
+ initial vfifo_full = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset)
+ begin
+ vfifo_fill <= 0;
+ vfifo_empty <= 1;
+ vfifo_full <= 0;
+ end else case({ S_AXIS_TVALID && S_AXIS_TREADY,
+ M_AXIS_TVALID && M_AXIS_TREADY })
+ 2'b10: begin
+ vfifo_fill <= vfifo_fill + 1;
+ vfifo_empty <= 0;
+ vfifo_full <= (&vfifo_fill[C_AXI_ADDR_WIDTH-ADDRLSB-1:0]);
+ end
+ 2'b01: begin
+ vfifo_fill <= vfifo_fill - 1;
+ vfifo_full <= 0;
+ vfifo_empty<= (vfifo_fill <= 1);
+ end
+ default: begin end
+ endcase
+
+ always @(*)
+ o_fill = vfifo_fill;
+
+ always @(*)
+ o_empty = vfifo_empty;
+ // }}}
+
+ // Determining when the write half is idle
+ // {{{
+ // This is required to know when to come out of soft reset.
+ //
+ // The first step is to count the number of bursts that remain
+ // outstanding
+ initial writes_outstanding = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ writes_outstanding <= 0;
+ else case({ phantom_write,M_AXI_BVALID && M_AXI_BREADY})
+ 2'b01: writes_outstanding <= writes_outstanding - 1;
+ 2'b10: writes_outstanding <= writes_outstanding + 1;
+ default: begin end
+ endcase
+
+ // The second step is to use this counter to determine if we are idle.
+ // If WVALID is ever high, or start_write goes high, then we are
+ // obviously not idle. Otherwise, we become idle when the number of
+ // writes outstanding transitions to (or equals) zero.
+ initial writes_idle = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ writes_idle <= 1;
+ else if (start_write || M_AXI_WVALID)
+ writes_idle <= 0;
+ else
+ writes_idle <= (writes_outstanding
+ == ((M_AXI_BVALID && M_AXI_BREADY) ? 1:0));
+ // }}}
+
+ // Count how much space is used in the memory device
+ // {{{
+ // Well, obviously, we can't fill our memory device or we have problems.
+ // To make sure we don't overflow, we count memory usage here. We'll
+ // count memory usage in units of bursts of (1<<LGMAXBURST) words of
+ // (1<<ADDRLSB) bytes each. So ... here we count the amount of device
+ // memory that hasn't (yet) been committed. This is different from the
+ // memory used (which we don't calculate), or the memory which may yet
+ // be read--which we'll calculate in a moment.
+ initial mem_space_available_w = (1<<BURSTAW);
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset)
+ mem_space_available_w <= (1<<BURSTAW);
+ else case({ phantom_write,M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST })
+ 2'b01: mem_space_available_w <= mem_space_available_w + 1;
+ 2'b10: mem_space_available_w <= mem_space_available_w - 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // Determining when the read half is idle
+ // {{{
+ // Count the number of read bursts that we've committed to. This
+ // includes bursts that have ARVALID but haven't been accepted, as well
+ // as any the downstream device will yet return an RLAST for.
+ initial reads_outstanding = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ reads_outstanding <= 0;
+ else case({ phantom_read,M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST})
+ 2'b01: reads_outstanding <= reads_outstanding - 1;
+ 2'b10: reads_outstanding <= reads_outstanding + 1;
+ default: begin end
+ endcase
+
+ // Now, using the reads_outstanding counter, we can check whether or not
+ // we are idle (and can exit a reset) of if instead there are more
+ // bursts outstanding to wait for.
+ //
+ // By registering this counter, we can keep the soft_reset release
+ // simpler. At least this way, it doesn't need to check two counters
+ // for zero.
+ initial reads_idle = 1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ reads_idle <= 1;
+ else if (start_read || M_AXI_ARVALID)
+ reads_idle <= 0;
+ else
+ reads_idle <= (reads_outstanding
+ == ((M_AXI_RVALID && M_AXI_RREADY && M_AXI_RLAST) ? 1:0));
+ // }}}
+
+ // Count how much data is in the memory device that we can read out
+ // {{{
+ // In AXI, after you issue a write, you can't depend upon that data
+ // being present on the device and available for a read until the
+ // associated BVALID is returned. Therefore we don't count any memory
+ // as available to be read until BVALID comes back. Once a read
+ // command is issued, the memory is again no longer available to be
+ // read. Note also that we are counting bursts here. A second
+ // conversion below converts this count to bytes.
+ initial mem_data_available_w = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset)
+ mem_data_available_w <= 0;
+ else case({ M_AXI_BVALID, phantom_read })
+ 2'b10: mem_data_available_w <= mem_data_available_w + 1;
+ 2'b01: mem_data_available_w <= mem_data_available_w - 1;
+ default: begin end
+ endcase
+ // }}}
+
+ //
+ // Error detection
+ // {{{
+ initial o_err = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ o_err <= 1'b0;
+ else begin
+ if (M_AXI_BVALID && M_AXI_BRESP[1])
+ o_err <= 1'b1;
+ if (M_AXI_RVALID && M_AXI_RRESP[1])
+ o_err <= 1'b1;
+ end
+ // }}}
+
+ //
+ // Incoming stream overflow detection
+ // {{{
+ // The overflow flag is set if ever an incoming value violates the
+ // stream protocol and changes while stalled. Internally, however,
+ // the overflow flag is ignored. It's provided for your information.
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ s_last_stalled <= 0;
+ else
+ s_last_stalled <= S_AXIS_TVALID && !S_AXIS_TREADY;
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXIS_TVALID)
+ s_last_tdata <= S_AXIS_TDATA;
+
+ initial o_overflow = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset)
+ o_overflow <= 0;
+ else if (s_last_stalled)
+ begin
+ if (!S_AXIS_TVALID)
+ o_overflow <= 1;
+ if (S_AXIS_TDATA != s_last_tdata)
+ o_overflow <= 1;
+ end
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Incoming FIFO
+ // {{{
+ // Incoming data stream info the FIFO
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ assign S_AXIS_TREADY = !reset_fifo && !ififo_full && !vfifo_full;
+ assign read_from_fifo= (!skd_valid || (M_AXI_WVALID && M_AXI_WREADY));
+
+ sfifo #(.BW(C_AXIS_DATA_WIDTH), .LGFLEN(LGFIFO))
+ ififo(S_AXI_ACLK, reset_fifo,
+ S_AXIS_TVALID && S_AXIS_TREADY,
+ S_AXIS_TDATA, ififo_full, ififo_fill,
+ read_from_fifo, ififo_data, ififo_empty);
+
+ //
+ // We need a quick 1-element buffer here in order to keep the soft
+ // reset, which resets the FIFO pointer, from adjusting any FIFO data.
+ // {{{
+ // Here's the rule: we need to fill the buffer before it ever gets
+ // used. Then, once used, it should be able to maintain 100%
+ // throughput.
+ initial skd_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (reset_fifo)
+ skd_valid <= 0;
+ else if (!ififo_empty)
+ skd_valid <= 1;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ skd_valid <= 0;
+
+ always @(posedge S_AXI_ACLK)
+ if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (!skd_valid || M_AXI_WREADY)
+ skd_data <= ififo_data;
+ end
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI write processing
+ // {{{
+ // Write data from our FIFO onto the bus
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // start_write: determining when to issue a write burst
+ // {{{
+ always @(*)
+ begin
+ start_write = 0;
+
+ if (ififo_fill >= (1<<LGMAXBURST))
+ start_write = 1;
+ if (vfifo_full || soft_reset || phantom_write)
+ start_write = 0;
+ if (mem_space_available_w == 0)
+ start_write = 0;
+
+ if (M_AXI_WVALID && (!M_AXI_WREADY || !M_AXI_WLAST))
+ start_write = 0;
+ if (M_AXI_AWVALID && !M_AXI_AWREADY)
+ start_write = 0;
+ if (o_err)
+ start_write = 0;
+ end
+ // }}}
+
+ // Register the start write signal into AWVALID and phantom write
+ // {{{
+ // phantom_write contains the start signal, but immediately clears
+ // on the next clock cycle. This allows us some time to calculate
+ // the data for the next burst which and if AWVALID remains high and
+ // not yet accepted.
+ initial phantom_write = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ phantom_write <= 0;
+ else
+ phantom_write <= start_write;
+
+ // Set AWVALID to start_write if every the channel isn't stalled.
+ // Incidentally, start_write is guaranteed to be zero if the channel
+ // is stalled, since that signal is used by other things as well.
+ initial axi_awvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_awvalid <= 0;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ axi_awvalid <= start_write;
+ // }}}
+
+ // Write address
+ // {{{
+ // We insist on alignment. On every accepted burst, we step forward by
+ // one burst length. On reset, we reset the address at our first
+ // opportunity.
+ initial axi_awaddr = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (M_AXI_AWVALID && M_AXI_AWREADY)
+ axi_awaddr[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB]
+ <= axi_awaddr[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB] +1;
+
+ if ((!M_AXI_AWVALID || M_AXI_AWREADY) && soft_reset)
+ axi_awaddr <= 0;
+
+ if (!S_AXI_ARESETN)
+ axi_awaddr <= 0;
+
+ axi_awaddr[LGMAXBURST+ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // Write data channel valid
+ // {{{
+ initial axi_wvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_wvalid <= 0;
+ else if (start_write)
+ axi_wvalid <= 1;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ axi_wvalid <= M_AXI_WVALID && !M_AXI_WLAST;
+ // }}}
+
+ // WLAST generation
+ // {{{
+ // On the beginning of any burst, start a counter of the number of items
+ // in it. Once the counter gets to 1, set WLAST.
+ initial writes_pending = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ writes_pending <= 0;
+ else if (start_write)
+ writes_pending <= MAXBURST;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ writes_pending <= writes_pending -1;
+
+ always @(posedge S_AXI_ACLK)
+ if (start_write)
+ axi_wlast <= (LGMAXBURST == 0);
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ axi_wlast <= (writes_pending == 1 + (M_AXI_WVALID ? 1:0));
+ // }}}
+
+ // Bus assignments based upon the above
+ // {{{
+ assign M_AXI_AWVALID = axi_awvalid;
+ assign M_AXI_AWID = AXI_WRITE_ID;
+ assign M_AXI_AWADDR = axi_awaddr;
+ assign M_AXI_AWLEN = MAXBURST-1;
+ assign M_AXI_AWSIZE = ADDRLSB[2:0];
+ assign M_AXI_AWBURST = 2'b01;
+ assign M_AXI_AWLOCK = 0;
+ assign M_AXI_AWCACHE = 0;
+ assign M_AXI_AWPROT = 0;
+ assign M_AXI_AWQOS = 0;
+
+ assign M_AXI_WVALID = axi_wvalid;
+ assign M_AXI_WDATA = skd_data;
+`ifdef AXI3
+ assign M_AXI_WID = AXI_WRITE_ID;
+`endif
+ assign M_AXI_WLAST = axi_wlast;
+ assign M_AXI_WSTRB = -1;
+
+ assign M_AXI_BREADY = 1;
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI read processing
+ // {{{
+ // Read data into our FIFO
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // How much FIFO space is available?
+ // {{{
+ // One we issue a read command, the FIFO space isn't available any more.
+ // That way we can determine when a second read can be issued--even
+ // before the first has returned--while also guaranteeing that there's
+ // always room in the outgoing FIFO for anything that might return.
+ // Remember: NEVER generate backpressure in a bus master
+ initial ofifo_space_available = (1<<LGFIFO);
+ always @(posedge S_AXI_ACLK)
+ if (reset_fifo)
+ ofifo_space_available <= (1<<LGFIFO);
+ else case({phantom_read, M_AXIS_TVALID && M_AXIS_TREADY})
+ 2'b10: ofifo_space_available <= ofifo_space_available - MAXBURST;
+ 2'b01: ofifo_space_available <= ofifo_space_available + 1;
+ 2'b11: ofifo_space_available <= ofifo_space_available - MAXBURST + 1;
+ default: begin end
+ endcase
+ // }}}
+
+ // Determine when to start a next read-from-memory-to-FIFO burst
+ // {{{
+ always @(*)
+ begin
+ start_read = 1;
+
+ // We can't read yet if we don't have space available.
+ // Note the comparison is carefully chosen to make certain
+ // it doesn't use all ofifo_space_available bits, but rather
+ // only the number of bits between LGFIFO and
+ // LGMAXBURST--nominally a single bit.
+ if (ofifo_space_available < MAXBURST) // FIFO space ?
+ start_read = 0;
+
+ // If there's no memory available for us to read from, then
+ // we can't start a read yet.
+ if (!M_AXI_BVALID && mem_data_available_w == 0)
+ start_read = 0;
+
+ // Don't start anything while waiting on a reset. Likewise,
+ // insist on a minimum one clock between read burst issuances.
+ if (soft_reset || phantom_read)
+ start_read = 0;
+
+ // We can't start a read request if the AR* channel is stalled
+ if (M_AXI_ARVALID && !M_AXI_ARREADY)
+ start_read = 0;
+
+ // Following a bus error, we come to a complete halt. Such a
+ // bus error is an indication that something *SERIOUSLY* went
+ // wrong--perhaps we aren't accessing the memory we are supposed
+ // to. To prevent damage to external devices, we disable
+ // ourselves entirely. There is no fall back. We only
+ // restart on a full bus restart.
+ if (o_err)
+ start_read = 0;
+ end
+ // }}}
+
+ // Set phantom_read and ARVALID
+ // {{{
+ initial phantom_read = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ phantom_read <= 0;
+ else
+ phantom_read <= start_read;
+
+ initial axi_arvalid = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ axi_arvalid <= 0;
+ else if (!M_AXI_ARVALID || M_AXI_ARREADY)
+ axi_arvalid <= start_read;
+ // }}}
+
+ // Calculate the next ARADDR
+ // {{{
+ initial axi_araddr = 0;
+ always @(posedge S_AXI_ACLK)
+ begin
+ if (M_AXI_ARVALID && M_AXI_ARREADY)
+ axi_araddr[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB]
+ <= axi_araddr[C_AXI_ADDR_WIDTH-1:LGMAXBURST+ADDRLSB]+ 1;
+
+ if ((!M_AXI_ARVALID || M_AXI_ARREADY) && soft_reset)
+ axi_araddr <= 0;
+
+ if (!S_AXI_ARESETN)
+ axi_araddr <= 0;
+
+ axi_araddr[LGMAXBURST+ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // Assign values to our bus wires
+ // {{{
+ assign M_AXI_ARVALID = axi_arvalid;
+ assign M_AXI_ARID = AXI_READ_ID;
+ assign M_AXI_ARADDR = axi_araddr;
+ assign M_AXI_ARLEN = MAXBURST-1;
+ assign M_AXI_ARSIZE = ADDRLSB[2:0];
+ assign M_AXI_ARBURST = 2'b01;
+ assign M_AXI_ARLOCK = 0;
+ assign M_AXI_ARCACHE = 0;
+ assign M_AXI_ARPROT = 0;
+ assign M_AXI_ARQOS = 0;
+
+ assign M_AXI_RREADY = 1;
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Outgoing AXI stream processing
+ // {{{
+ // Send data out from the MM2S FIFO
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // We basically just stuff the data read from memory back into our
+ // outgoing FIFO here. The logic is quite straightforward.
+ assign write_to_fifo = M_AXI_RVALID && M_AXI_RREADY;
+ assign M_AXIS_TVALID = !ofifo_empty;
+
+ sfifo #(.BW(C_AXIS_DATA_WIDTH), .LGFLEN(LGFIFO))
+ ofifo(S_AXI_ACLK, reset_fifo,
+ write_to_fifo,
+ M_AXI_RDATA, ofifo_full, ofifo_fill,
+ M_AXIS_TVALID && M_AXIS_TREADY, M_AXIS_TDATA, ofifo_empty);
+
+ always @(*)
+ o_mm2s_full = |ofifo_fill[LGFIFO:LGMAXBURST];
+ // }}}
+
+ // Keep Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, M_AXI_BID, M_AXI_RID,
+ M_AXI_BRESP[0], M_AXI_RRESP[0],
+ ififo_empty, ofifo_full, ofifo_fill
+ // fifo_full, fifo_fill, fifo_empty,
+ };
+
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal property section
+// {{{
+//
+// The following are a subset of the formal properties used to verify this
+// core. The full set of formal properties, together with the formal
+// property set itself, are available for purchase.
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ // FAXI_DEPTH controls the width of the counters in the bus property
+ // interface file. In order to support bursts of length 8 (or more),
+ // there's a minimum of 9. Otherwise, we'll just set this to the width
+ // of the AXI address bus.
+ localparam FAXI_DEPTH = (C_AXI_ADDR_WIDTH > 8)
+ ? (C_AXI_ADDR_WIDTH+1) : 9;
+
+ reg f_past_valid;
+
+ initial f_past_valid = 0;
+ always @(posedge S_AXI_ACLK)
+ f_past_valid <= 1;
+
+ //
+ // Wires necessary for interacting with the formal property file
+ // {{{
+ // ...
+ // }}}
+
+ // Other registers to keep simplify keeping track of our progress
+ // {{{
+ reg [C_AXI_ADDR_WIDTH-1:0] faxi_rd_cknext, faxi_wr_cknext,
+ f_read_beat_addr, f_write_beat_addr,
+ faxi_read_beat_addr;
+ reg [C_AXI_ADDR_WIDTH-1:0] f_read_ckbeat_addr;
+ reg [FAXI_DEPTH-1:0] f_rd_bursts_after_check;
+
+ reg [C_AXI_ADDR_WIDTH:0] f_vfill;
+ reg [C_AXI_ADDR_WIDTH:0] f_space_available,
+ f_data_available;
+
+ reg [C_AXI_ADDR_WIDTH-1:0] f_read_checksum;
+ reg [C_AXI_ADDR_WIDTH:0] mem_space_available, mem_data_available;
+ // }}}
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The main AXI data interface bus property check
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // The number of beats in the maximum burst size is
+ // automatically determined from LGMAXBURST, and so its
+ // forced to be a power of two this way.
+ localparam MAXBURST=(1<<LGMAXBURST);
+
+ faxi_master #(
+ // {{{
+ .C_AXI_ID_WIDTH(C_AXI_ID_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXI_ADDR_WIDTH),
+ .C_AXI_DATA_WIDTH(C_AXI_DATA_WIDTH),
+ .OPT_MAXBURST(MAXBURST-1),
+ .OPT_NARROW_BURST(1'b0),
+ .OPT_ASYNC_RESET(1'b0), // We don't use asynchronous resets
+ .OPT_EXCLUSIVE(1'b0), // We don't use the LOCK signal
+ .F_OPT_ASSUME_RESET(1'b1), // We aren't generating the reset
+ .F_OPT_NO_RESET(1'b1),
+ .F_LGDEPTH(FAXI_DEPTH) // Width of the counters
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ // Write signals
+ // {{{
+ .i_axi_awvalid(M_AXI_AWVALID),
+ .i_axi_awready(M_AXI_AWREADY),
+ .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_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),
+ //
+ .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 signals
+ // {{{
+ .i_axi_arvalid(M_AXI_ARVALID),
+ .i_axi_arready(M_AXI_ARREADY),
+ .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_rvalid(M_AXI_RVALID),
+ .i_axi_rready(M_AXI_RREADY),
+ .i_axi_rid( M_AXI_RID),
+ .i_axi_rdata( M_AXI_RDATA),
+ .i_axi_rlast( M_AXI_RLAST),
+ .i_axi_rresp( M_AXI_RRESP),
+ // }}}
+ // Induction signals
+ // {{{
+ // ...
+ // }}}
+ // }}}
+ );
+
+ // Some quick sanity checks
+ // {{{
+ always @(*)
+ begin
+ //
+ // ...
+ //
+ end
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write sanity checking
+ // {{{
+ always @(*)
+ mem_space_available = { mem_space_available_w,
+ {(LGMAXBURST+ADDRLSB){1'b0} } };
+
+ // Let's calculate the address of each write beat
+ // {{{
+ initial f_write_beat_addr = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_write_beat_addr <= 0;
+ else if ((!M_AXI_WVALID || (M_AXI_WREADY && M_AXI_WLAST)) && soft_reset)
+ f_write_beat_addr <= 0;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ f_write_beat_addr <= f_write_beat_addr + (1<<ADDRLSB);
+ // }}}
+
+ //
+ // ...
+ //
+
+ // Verify during any write burst that all the burst parameters are
+ // correct
+ // {{{
+ // ...
+ // }}}
+
+ always @(*)
+ if (M_AXI_AWVALID)
+ begin
+ assert(writes_pending == (1<<LGMAXBURST));
+ // ...
+ end else
+ // ...
+
+ always @(*)
+ assert(M_AXI_WVALID == (writes_pending != 0));
+
+ //
+ // ...
+ //
+
+ // Check the writes-idle signal
+ // {{{
+ // ...
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read sanity checking
+ // {{{
+
+ always @(*)
+ mem_data_available = { mem_data_available_w,
+ {(LGMAXBURST+ADDRLSB){1'b0} } };
+ //
+ // ...
+ // Check the reads-idle signal
+ // {{{
+ // ...
+ // }}}
+
+ // Regenerate the read beat address
+ // {{{
+ // This works because we are using a single AXI ID for all of our reads.
+ // Therefore we can guarantee that reads will come back in order, thus
+ // we can count the return address here.
+ initial f_read_beat_addr = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ f_read_beat_addr <= 0;
+ else if (soft_reset && reads_idle)
+ f_read_beat_addr <= 0;
+ else if (M_AXI_RVALID && M_AXI_RREADY)
+ f_read_beat_addr <= f_read_beat_addr + (1<<ADDRLSB);
+ // }}}
+
+ // Read burst checking
+ // {{{
+
+ //
+ // ...
+ //
+
+ // }}}
+
+
+ // Match our read beat address to ARADDR
+ // {{{
+ // ...
+ // }}}
+
+ // Insist that our burst counters are consistent with bursts of a full
+ // length only
+ // {{{
+ // ...
+ // }}}
+
+ // RLAST checking
+ // {{{
+ // ...
+ // }}}
+
+ // Match the read beat address to the number of items remaining in this
+ // burst
+ // {{{
+ // ...
+ // }}}
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Internal assertions (Induction)
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // On either error, or while waiting for a soft reset to complete
+ // nothing new should issue
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && ($past(soft_reset) || $past(o_err)))
+ begin
+ assert(!$rose(M_AXI_AWVALID));
+ assert(!$rose(M_AXI_WVALID));
+ assert(!$rose(M_AXI_ARVALID));
+ assert(!phantom_write);
+ assert(!phantom_read);
+ end
+ // }}}
+
+ //
+ // Writes and reads always have enough room
+
+ // Bus writes aren't allowed to drain the incoming FIFO dry
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!soft_reset && M_AXI_WVALID)
+ assert(ififo_fill + (skd_valid ? 1:0) >= writes_pending);
+ // }}}
+
+ // Bus reads aren't allowed to overflow the return FIFO
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (!soft_reset && M_AXI_RVALID)
+ assert(!ofifo_full);
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Write checks
+ //
+
+ // Make sure the skid buffer only reads when either there's nothing
+ // held in the buffer, or the write channel has accepted data. Indeed,
+ // the write channel should never be active unless the skid buffer is
+ // full.
+ // {{{
+ always @(*)
+ if (!soft_reset && !skd_valid)
+ assert(!M_AXI_WVALID);
+
+ always @(*)
+ if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ assert(read_from_fifo);
+ end else if (!skd_valid && !ififo_empty)
+ assert(read_from_fifo);
+ // }}}
+
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Read checks
+ // {{{
+
+ // No read checks in addition to the ones above
+
+ // }}}
+
+ ////////////////////////////////////////
+ //
+ // Errors or resets -- do we properly end gracefully
+ //
+ // {{{
+ // Set o_err on any bus error. Only clear it on reset
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN))
+ begin
+ if ($past(M_AXI_BVALID && M_AXI_BRESP[1]))
+ assert(o_err);
+ if ($past(M_AXI_RVALID && M_AXI_RRESP[1]))
+ assert(o_err);
+ if ($past(!M_AXI_BVALID || !M_AXI_BRESP[1])
+ && $past(!M_AXI_RVALID || !M_AXI_RRESP[1]))
+ assert(!$rose(o_err));
+ end
+
+ // Only release soft_reset once the bus is idle
+ // {{{
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN) && $fell(soft_reset))
+ begin
+ //
+ // ...
+ //
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assert(!M_AXI_ARVALID);
+ end
+ // }}}
+ // }}}
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // FIFO checks
+ // {{{
+
+ // Check the global fill/emtpy values
+ // {{{
+ always @(*)
+ assert(vfifo_full == (vfifo_fill == (1<<(C_AXI_ADDR_WIDTH-ADDRLSB))));
+
+ always @(*)
+ assert(vfifo_fill <= (1<<(C_AXI_ADDR_WIDTH-ADDRLSB)));
+
+ always @(*)
+ assert(vfifo_empty == (vfifo_fill == 0));
+ // }}}
+
+ // An equation for vfill based upon our property checker's counters
+ // {{{
+ always @(*)
+ begin
+ f_vfill = M_AXI_AWADDR - M_AXI_ARADDR;
+ f_vfill[C_AXI_ADDR_WIDTH] = 0;
+ if (!M_AXI_AWVALID)
+ f_vfill = f_vfill - (writes_pending << ADDRLSB);
+ // ...
+ if (skd_valid)
+ f_vfill = f_vfill + (1<<ADDRLSB);
+ f_vfill = f_vfill + ((ififo_fill + ofifo_fill)<<ADDRLSB);
+ end
+ // }}}
+
+ // Make sure the virtual FIFO's fill matches that counter
+ // {{{
+ always @(*)
+ if (!soft_reset)
+ begin
+ assert(vfifo_fill == (f_vfill >> ADDRLSB));
+ assert(f_vfill <= (1<<(C_AXI_ADDR_WIDTH)));
+
+ // Before the equation check, double check that we
+ // don't overflow any of our arithmetic. Then check our
+ // virtual FIFO's fill counter against the various internal
+ // FIFO fills and read counters.
+
+ // These are just inequality checks, however, so we'll still
+ // need a full equation check elsewhere
+ //
+ // ...
+ //
+ end
+
+ // Make certain we aren't overflowing in our subtraction above
+ always @(*)
+ if (M_AXI_ARVALID && !soft_reset)
+ assert(M_AXI_ARADDR != M_AXI_AWADDR);
+ // }}}
+
+ // Check mem_space_available
+ // {{{
+ always @(*)
+ begin
+ f_space_available = (M_AXI_AWADDR - M_AXI_ARADDR);
+ f_space_available[C_AXI_ADDR_WIDTH] = 0;
+ f_space_available = (1<<C_AXI_ADDR_WIDTH) - f_space_available;
+ if (M_AXI_AWVALID && !phantom_write)
+ f_space_available = f_space_available
+ - (1 << (LGMAXBURST+ADDRLSB));
+ //
+ // ...
+ end
+
+ always @(*)
+ begin
+ assert({ mem_data_available_w, {(LGMAXBURST){1'b0}} }
+ <= vfifo_fill);
+ // ...
+ assert(mem_data_available <= (1<<C_AXI_ADDR_WIDTH));
+ assert(mem_space_available <= (1<<C_AXI_ADDR_WIDTH));
+ if (!soft_reset)
+ begin
+ assert(mem_space_available == f_space_available);
+
+ if (mem_space_available[C_AXI_ADDR_WIDTH])
+ begin
+ assert(M_AXI_ARADDR == M_AXI_AWADDR);
+ assert(!M_AXI_AWVALID || phantom_write);
+ // ...
+ end
+ end
+ end
+ // }}}
+
+ // Check mem_data_available
+ // {{{
+ // First, generate an equation to describe it
+ always @(*)
+ begin
+ f_data_available = M_AXI_AWADDR - M_AXI_ARADDR;
+ f_data_available[C_AXI_ADDR_WIDTH] = 0;
+ // ...
+ if (M_AXI_ARVALID && !phantom_read)
+ f_data_available = f_data_available
+ - (1 << (ADDRLSB + LGMAXBURST));
+ end
+
+ // Then compare it against that equation
+ always @(*)
+ if (!soft_reset)
+ begin
+ assert(mem_data_available == f_data_available);
+ if (mem_data_available[C_AXI_ADDR_WIDTH])
+ begin
+ assert(vfifo_fill[C_AXI_ADDR_WIDTH]);
+ assert(ofifo_empty);
+ end
+ end
+ // }}}
+
+ // ofifo_space_available
+ // {{{
+ always @(*)
+ if (!reset_fifo)
+ begin
+ // ...
+
+ // Make sure we don't overflow above
+ assert(ofifo_space_available <= (1<<LGFIFO));
+ end
+ // }}}
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Initial (only) constraints
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ always @(posedge S_AXI_ACLK)
+ if (!f_past_valid || $past(!S_AXI_ARESETN))
+ begin
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assert(!M_AXI_ARVALID);
+
+ assert(mem_space_available == (1<<C_AXI_ADDR_WIDTH));
+ assert(mem_data_available == 0);
+
+ assert(!phantom_read);
+ assert(!phantom_write);
+
+ assert(vfifo_fill == 0);
+ end
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Formal contract checking
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // This core doesn't (yet) have any formal contract check
+
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg [2:0] cvr_write_bursts, cvr_read_bursts;
+ (* anyconst *) reg cvr_high_speed;
+
+ always @(posedge S_AXI_ACLK)
+ if (cvr_high_speed)
+ begin
+ assume(!$fell(S_AXIS_TVALID));
+ // if (S_AXI_ARESETN && $past(S_AXIS_TVALID && !S_AXIS_TREADY))
+ // assume(S_AXIS_TVALID && $stable(S_AXIS_TDATA));
+
+ assume(M_AXI_AWREADY || writes_pending > 0);
+ assume(M_AXIS_TREADY);
+ assume(M_AXI_WREADY);
+ assume(M_AXI_ARREADY);
+ end
+
+ initial cvr_write_bursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset || o_err || o_overflow)
+ cvr_write_bursts <= 0;
+ else if (M_AXI_BVALID && !cvr_write_bursts[2])
+ cvr_write_bursts <= cvr_write_bursts + 1;
+
+ initial cvr_read_bursts = 0;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN || soft_reset || o_err || o_overflow)
+ cvr_read_bursts <= 0;
+ else if (M_AXI_RVALID && M_AXI_RLAST && !cvr_read_bursts[2])
+ cvr_read_bursts <= cvr_read_bursts + 1;
+
+ //
+ // ...
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !soft_reset)
+ cover(cvr_read_bursts > 1 && cvr_write_bursts > 1);
+
+ always @(posedge S_AXI_ACLK)
+ if (S_AXI_ARESETN && !soft_reset)
+ cover(cvr_read_bursts > 1 && cvr_write_bursts > 1
+ && cvr_high_speed);
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Careless assumptions (i.e. constraints)
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // No careless assumptions. Indeed, no assumptions at all beyond
+ // what's in the bus property file, and some optional cover assumptions.
+
+ // }}}
+ // }}}
+`endif
+endmodule