summaryrefslogtreecommitdiff
path: root/rtl/wb2axip/axis2mm.v
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/wb2axip/axis2mm.v')
-rw-r--r--rtl/wb2axip/axis2mm.v2234
1 files changed, 2234 insertions, 0 deletions
diff --git a/rtl/wb2axip/axis2mm.v b/rtl/wb2axip/axis2mm.v
new file mode 100644
index 0000000..d578e41
--- /dev/null
+++ b/rtl/wb2axip/axis2mm.v
@@ -0,0 +1,2234 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Filename: axis2mm
+// {{{
+// Project: WB2AXIPSP: bus bridges and other odds and ends
+//
+// Purpose: Converts an AXI-stream (input) to an AXI (full) memory
+// interface.
+//
+// {{{
+// While I am aware that other vendors sell similar components, if you
+// look under the hood you'll find no relation to anything but my own
+// work here.
+//
+// Registers:
+//
+// 0: CMD_CONTROL
+// Controls the transaction via either starting or aborting an
+// ongoing transaction, provides feedback regarding the current
+// status.
+//
+// [31] r_busy
+// True if the core is in the middle of a transaction. Set this
+// bit to one to begin a transaction.
+//
+// [30] r_err
+// True if the core has detected an error, such as FIFO
+// overflow while writing, or FIFO overflow in continuous mode.
+//
+// Writing a '1' to this bit while the core is idle will clear it.
+// New transfers will not start until this bit is cleared. For
+// this reason, I often start a new transfer by writing to bits
+// 31 and 30 of this register.
+//
+// _s2mm->a_control = 0xc0000000;
+//
+// Other bits may be appropriate as well, as discussed below,
+// depending on your application.
+//
+// [29] r_complete
+// True if the transaction has completed, whether normally or
+// abnormally (error or abort).
+//
+// Any write to the CMD_CONTROL register will clear this flag.
+//
+// [28] r_continuous
+// Normally the FIFO gets cleared and reset between operations.
+// However, if you set r_continuous, the core will then expectt
+// a second operation to take place following the first one.
+// In this case, the FIFO doesn't get cleared. However, if the
+// FIFO fills and the incoming data is both valid and changing,
+// the r_err flag will be set.
+//
+// Any write to the CMD_CONTROL register while the core is not
+// busy will adjust this bit.
+//
+// [27] !r_increment
+//
+// If clear, the core writes to subsequent and incrementing
+// addresses--the normal copy to memory case. If !r_increment is
+// set, the core writes to the same address throughout the
+// transaction. This is useful if you want to copy data to a
+// FIFO or other device living at a single address in the memory
+// map.
+//
+// Writes to CMD_CONTROL while the core is idle will adjust this
+// bit.
+//
+// [26] !tlast_syncd
+//
+// Read only status indicator. Reads 0 if OPT_TLAST_SYNC isn't
+// set. If OPT_TLAST_SYNC is set, then this bit indicates whether
+// or not the memory transfer is currently aligned with any stream
+// packets, or whether it is out of synchronization and waiting to
+// sync with the incoming stream. If the IP is out of alignment
+// and OPT_TLAST_SYNC is set, then the core will synchronize
+// itself automatically by holding TREADY high and ignoring data
+// until the first sample after TLAST.
+//
+// [25] Error code, decode error
+//
+// Read only bit. True following any AXI decode error. This will
+// also set the error bit. When the error bit is cleared, this
+// bit will be automatically cleared as well.
+//
+// [24] Error code, slave error
+//
+// Read only bit. True following any AXI slave error. This will
+// also set the error bit. When the error bit is cleared, this bit
+// will be automatically cleared as well.
+//
+// [23] Error code, overflow error
+//
+// Read only bit. True following any AXI stream overflow. As with
+// the other two error code bits, this one will also set the error
+// bit. It will also be cleared whenever the error bit is cleared.
+//
+// A "proper" AXI stream will never nor can it ever overflow. This
+// overflow check therefore looks for AXI stream protocol
+// violations. Such a violation might be not maintaining TVALID
+// when !TREADY, or changing TDATA when TVALID && !TREADY.
+// Likewise, if TLAST changes while TVALID && !TREADY an overflow
+// condition will be generated--but in this case only if the
+// OPT_TLAST_SYNC option is set.
+//
+// [22] Abort in progress
+//
+// Read only bit. This bit will be true following any abort until
+// the bus transactions complete. Self-clearing.
+//
+// [20:16] LGFIFO
+// These are read-only bits, returning the size of the FIFO.
+//
+// ABORT
+// If the core is busy, and the ABORT_KEY (currently set to 8'h26
+// below) is written to the top 8-bits ([31:24]) of this command
+// register, then the current transfer will be aborted. Yes, this
+// does repurpose the other bits written above. Any pending writes
+// will be completed, but nothing more will be written.
+//
+// Alternatively, the core will enter into an abort state
+// following any returned bus error indications.
+//
+// x4-c: (Unused and reserved)
+//
+// x10-14: CMD_ADDR
+// [C_AXI_ADDR_WIDTH-1:($clog2(C_AXI_DATA_WIDTH)-3)]
+//
+// If idle, this is address the core will write to when it starts.
+//
+// If busy, this is the address of either the current or next
+// address the core will request writing to.
+//
+// Upon completion, the address either returns to the starting
+// address (if r_continuous is clear), or otherwise becomes the
+// address where the core left off. In the case of an abort or an
+// error, this will be (near) the address that was last written.
+//
+// Why "near"? Because this address records the writes that have
+// been issued while no error is pending. If a bus error return
+// comes back, there may have been several writes issued before
+// that error address. Likewise if an overflow is detected, the
+// data associated with the overflow may have already been
+// somewhat written--the AXI bus doesn't stop on a dime.
+//
+// I hope to eventually add support for unaligned bursts. Such
+// support is not currently part of this core.
+//
+// x18-1c: CMD_LEN
+// [LGLEN-1:0]
+// The size of the transfer in bytes. Only accepts aligned
+// addresses, therefore bits [($clog2(C_AXI_DATA_WIDTH)-3):0]
+// will always be forced to zero. To find out what size bus
+// this core is conencted to, or the maximum transfer length,
+// write a -1 to this value and read the returning result.
+// Only the active bits will be set.
+//
+// While the core is busy, reads from this address will return
+// the number of items still to be written to the bus.
+//
+// }}}
+//
+// Status:
+// {{{
+// 1. The core passes both cover checks and formal property (assertion)
+// based checks. It has not (yet) been tested in real hardware.
+//
+// 2. I'd also like to support unaligned addresses (not lengths). This
+// will require aligning the data coming out of the FIFO as well.
+// As written, the core doesn't yet support these features.
+//
+// }}}
+//
+// Updates:
+// {{{
+// 20210426 - Fix. Upon reading, the current AXI write address and length
+// will (now) be returned. These are word address and lengths, not
+// packet address and lengths, and may (or may not) be aligned with
+// packet boundaries.
+//
+// In a similar fashion, the cmd_addr and cmd_length registers will
+// be adjusted on any error to (approximate) the address and length
+// remaining upon any discovered error.
+// }}}
+//
+// 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 axis2mm #(
+ // {{{
+ //
+ // Downstream AXI (MM) address width. Remember, this is *byte*
+ // oriented, so an address width of 32 means this core can
+ // interact with a full 2^(C_AXI_ADDR_WIDTH) *bytes*.
+ parameter C_AXI_ADDR_WIDTH = 32,
+ // ... and the downstream AXI (MM) data width. High speed can
+ // be achieved by increasing this data width.
+ parameter C_AXI_DATA_WIDTH = 32,
+ parameter C_AXI_ID_WIDTH = 1,
+ parameter C_AXIS_TUSER_WIDTH = 0,
+ //
+ // OPT_AXIS_SKIDBUFFER will place a buffer between the incoming
+ // AXI stream interface and the outgoing AXI stream ready. This
+ // is technically necessary, and probably good practice too,
+ // when dealing with high speed networks.
+ parameter [0:0] OPT_AXIS_SKIDBUFFER = 1,
+ //
+ // OPT_AXIS_SKIDREGISTER will force the outputs of the skid
+ // buffer to be registered. This is something you would
+ // do primarily if you are trying to hit high speeds through
+ // this core.
+ parameter [0:0] OPT_AXIS_SKIDREGISTER = 0,
+ //
+ // OPT_TLAST_SYNC will synchronize the write with any incoming
+ // packets. Packets are assumed to be synchronized initially
+ // after any reset, or on the TVALID following any TLAST
+ parameter [0:0] OPT_TLAST_SYNC = 1,
+ //
+ // OPT_TREADY_WHILE_IDLE controls how the stream idle is set
+ // when the memory copy isn't running. If 1, then TREADY will
+ // be 1 and the core will ignore/throw out data when the core
+ // isn't busy. Otherwise, if this is set to 0, the core will
+ // force the stream to stall if ever no data is being copied.
+ parameter [0:0] OPT_TREADY_WHILE_IDLE = 1,
+ //
+ // If the ABORT_KEY is written to the upper 8-bits of the
+ // control/status word, the current operation will be halted.
+ // Any currently active (AxVALID through xVALID & xREADY)
+ // requests will continue to completion, and the core will then
+ // come to a halt.
+ parameter [7:0] ABORT_KEY = 8'h26,
+ //
+ // The size of the FIFO, log-based two. Hence LGFIFO=9 gives
+ // you a FIFO of size 2^(LGFIFO) or 512 elements. This is about
+ // as big as the FIFO should ever need to be, since AXI bursts
+ // can be 256 in length.
+ parameter LGFIFO = 9,
+ //
+ // Maximum number of bytes that can ever be transferred, in
+ // log-base 2. Hence LGLEN=20 will transfer 1MB of data.
+ parameter LGLEN = C_AXI_ADDR_WIDTH-1,
+ //
+ // We only ever use one AXI ID for all of our transactions.
+ // Here it is given as 0. Feel free to change it as necessary.
+ parameter [C_AXI_ID_WIDTH-1:0] AXI_ID = 0,
+ //
+ // Set OPT_UNALIGNED to be able to transfer from unaligned
+ // addresses. Only applies to non fixed addresses and
+ // (possibly) non-continuous bursts. (THIS IS A PLACEHOLDER.
+ // UNALIGNED ADDRESSING IS NOT CURRENTLY SUPPORTED.)
+ localparam [0:0] OPT_UNALIGNED = 0,
+ //
+ parameter [0:0] OPT_LOWPOWER = 1'b0,
+ parameter [0:0] OPT_CLKGATE = OPT_LOWPOWER,
+ //
+ // OPT_ASYNCMEM. The default FIFO implementation uses an
+ // asynchronous memory read, which will return the result in
+ // the same clock it is requested within. This forces the
+ // FIFO to use distributed RAM. For those architectures that
+ // don't have distributed RAM, or those designs that need to
+ // use block RAM, this flag should be set to zero.
+ parameter [0:0] OPT_ASYNCMEM = 1'b1,
+ //
+ // Size of the AXI-lite bus. These are fixed, since 1) AXI-lite
+ // is fixed at a width of 32-bits by Xilinx def'n, and 2) since
+ // we only ever have 4 configuration words.
+ localparam C_AXIL_ADDR_WIDTH = 5,
+ localparam C_AXIL_DATA_WIDTH = 32
+ // }}}
+ ) (
+ // {{{
+ input wire S_AXI_ACLK,
+ input wire S_AXI_ARESETN,
+ //
+ // The stream interface
+ // {{{
+ input wire S_AXIS_TVALID,
+ output wire S_AXIS_TREADY,
+ input wire [C_AXI_DATA_WIDTH-1:0] S_AXIS_TDATA,
+ input wire S_AXIS_TLAST,
+ input wire [((C_AXIS_TUSER_WIDTH>0) ? C_AXIS_TUSER_WIDTH-1:0):0]
+ S_AXIS_TUSER,
+ // }}}
+ //
+ // The control interface
+ // {{{
+ input wire S_AXIL_AWVALID,
+ output wire S_AXIL_AWREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR,
+ input wire [2:0] S_AXIL_AWPROT,
+ //
+ input wire S_AXIL_WVALID,
+ output wire S_AXIL_WREADY,
+ input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA,
+ input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB,
+ //
+ output wire S_AXIL_BVALID,
+ input wire S_AXIL_BREADY,
+ output wire [1:0] S_AXIL_BRESP,
+ //
+ input wire S_AXIL_ARVALID,
+ output wire S_AXIL_ARREADY,
+ input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR,
+ input wire [2:0] S_AXIL_ARPROT,
+ //
+ output wire S_AXIL_RVALID,
+ input wire S_AXIL_RREADY,
+ output wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA,
+ output wire [1:0] S_AXIL_RRESP,
+ // }}}
+ //
+
+ //
+ // The AXI (full) write interface
+ // {{{
+ 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,
+ output wire [7:0] M_AXI_AWLEN,
+ output wire [2:0] M_AXI_AWSIZE,
+ output wire [1:0] M_AXI_AWBURST,
+ output wire M_AXI_AWLOCK,
+ 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,
+ 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,
+ output wire [(C_AXIS_TUSER_WIDTH>0 ? C_AXIS_TUSER_WIDTH-1:0):0]
+ M_AXI_WUSER,
+ //
+ 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,
+ // }}}
+ //
+ //
+ // Create an output signal to indicate that we've finished
+ output reg o_int
+ // }}}
+ );
+
+ // Local parameters
+ // {{{
+ localparam AXILLSB = $clog2(C_AXIL_DATA_WIDTH)-3;
+ localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3;
+ localparam [2:0] CMD_CONTROL = 3'b000,
+ // CMD_UNUSED_1 = 3'b001,
+ // CMD_UNUSED_2 = 3'b010,
+ // CMD_UNUSED_3 = 3'b011,
+ CMD_ADDRLO = 3'b100,
+ CMD_ADDRHI = 3'b101,
+ CMD_LENLO = 3'b110,
+ CMD_LENHI = 3'b111;
+ // CMD_RESERVED = 2'b11;
+
+ // The maximum burst size is either 256, or half the FIFO size,
+ // whichever is smaller.
+ localparam TMP_LGMAXBURST=(LGFIFO > 8) ? 8 : LGFIFO-1;
+ // Of course, if this busts our 4kB packet size, it's an error.
+ // Let's clip to that size, then, if the LGMAXBURST would otherwise
+ // break it. So .. if 4kB is larger than our maximum burst size, then
+ // no change is required.
+ localparam LGMAXBURST = ((4096 / (C_AXI_DATA_WIDTH / 8))
+ > (1<<TMP_LGMAXBURST))
+ ? TMP_LGMAXBURST : $clog2(4096 * 8 / C_AXI_DATA_WIDTH);
+ localparam LGMAX_FIXED_BURST = (LGMAXBURST > 4) ? 4 : LGMAXBURST;
+ localparam MAX_FIXED_BURST = (1<<LGMAX_FIXED_BURST);
+ localparam LGLENW = LGLEN - ADDRLSB;
+ // localparam LGFIFOB = LGFIFO + ADDRLSB;
+ localparam ERRCODE_NOERR = 0,
+ ERRCODE_OVERFLOW = 0,
+ ERRCODE_SLVERR = 1,
+ ERRCODE_DECERR = 2;
+ // }}}
+
+ // Signal declarations
+ // {{{
+ wire clk_active, gated_clk;
+ wire i_clk = gated_clk;
+ wire i_reset = !S_AXI_ARESETN;
+
+ // Incoming stream buffer
+ wire sskd_valid, sskd_ready, sskd_last;
+ wire [C_AXI_DATA_WIDTH-1:0] sskd_data;
+ wire [((C_AXIS_TUSER_WIDTH>0) ? C_AXIS_TUSER_WIDTH : 1)-1:0] sskd_user;
+
+ reg r_busy, r_err, r_complete, r_continuous, r_increment,
+ cmd_abort, zero_length, r_pre_start,
+ w_cmd_start, w_complete, w_cmd_abort;
+ reg [2:0] r_errcode;
+ // reg cmd_start;
+ reg axi_abort_pending;
+
+ reg [LGLENW-1:0] aw_requests_remaining,
+ aw_bursts_outstanding,
+ aw_next_remaining;
+ reg [LGMAXBURST:0] wr_writes_pending;
+ reg [LGMAXBURST:0] r_max_burst;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_addr;
+
+ reg [C_AXI_ADDR_WIDTH-1:0] cmd_addr;
+ reg [LGLENW-1:0] cmd_length_w;
+
+ reg [2*C_AXIL_DATA_WIDTH-1:0] wide_address, wide_length,
+ new_wideaddr, new_widelen,
+ wide_len_remaining,wide_current_address;
+ wire [C_AXIL_DATA_WIDTH-1:0] new_cmdaddrlo, new_cmdaddrhi,
+ new_lengthlo, new_lengthhi;
+
+ // FIFO signals
+ wire reset_fifo, write_to_fifo,
+ read_from_fifo;
+ wire [C_AXIS_TUSER_WIDTH+C_AXI_DATA_WIDTH-1:0] fifo_data;
+ wire [LGFIFO:0] fifo_fill;
+ wire fifo_full, fifo_empty;
+
+ wire awskd_valid, axil_write_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr;
+ //
+ wire wskd_valid;
+ wire [C_AXIL_DATA_WIDTH-1:0] wskd_data;
+ wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb;
+ reg axil_bvalid;
+ //
+ wire arskd_valid, axil_read_ready;
+ wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr;
+ reg [C_AXIL_DATA_WIDTH-1:0] axil_read_data;
+ reg axil_read_valid;
+ reg last_stalled, overflow, last_tlast;
+ reg [C_AXI_DATA_WIDTH-1:0] last_tdata;
+ reg [C_AXIL_DATA_WIDTH-1:0] w_status_word;
+
+ reg [LGLENW-1:0] r_remaining_w;
+
+ reg axi_awvalid;
+ reg [C_AXI_ADDR_WIDTH-1:0] axi_awaddr;
+ reg [7:0] axi_awlen;
+ reg axi_wvalid, axi_wlast;
+ reg [C_AXI_DATA_WIDTH/8-1:0] axi_wstrb;
+
+ // Speed up checking for zeros
+ reg aw_none_remaining,
+ aw_none_outstanding,
+ aw_last_outstanding,
+ wr_none_pending; // r_none_remaining;
+
+ reg w_phantom_start, phantom_start;
+ reg [LGMAXBURST:0] initial_burstlen;
+ reg [LGMAXBURST-1:0] addralign;
+
+ //
+ // Option processing
+ wire tlast_syncd;
+
+ reg aw_multiple_full_bursts,
+ aw_multiple_fixed_bursts,
+ aw_multiple_bursts_remaining,
+ aw_needs_alignment;
+ reg [LGFIFO:0] data_available;
+ reg sufficiently_filled;
+
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI Stream skidbuffer
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(OPT_AXIS_SKIDREGISTER),
+ .DW(C_AXI_DATA_WIDTH + 1
+ + ((C_AXIS_TUSER_WIDTH > 0) ? C_AXIS_TUSER_WIDTH:1)),
+ .OPT_LOWPOWER(OPT_LOWPOWER),
+ .OPT_PASSTHROUGH(!OPT_AXIS_SKIDBUFFER)
+ // }}}
+ ) skd_stream(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(reset_fifo),
+ .i_valid(S_AXIS_TVALID), .o_ready(S_AXIS_TREADY),
+ .i_data({ S_AXIS_TUSER, S_AXIS_TDATA, S_AXIS_TLAST }),
+ .o_valid(sskd_valid), .i_ready(sskd_ready),
+ .o_data({ sskd_user, sskd_data, sskd_last })
+ // }}}
+ );
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite signaling
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // This is mostly the skidbuffer logic, and handling of the VALID
+ // and READY signals for the AXI-lite control logic in the next
+ // section.
+
+ //
+ // Write signaling
+ //
+ // {{{
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB),
+ .OPT_LOWPOWER(OPT_LOWPOWER)
+ // }}}
+ ) axilawskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY),
+ .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(awskd_valid), .i_ready(axil_write_ready),
+ .o_data(awskd_addr)
+ // }}}
+ );
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8),
+ .OPT_LOWPOWER(OPT_LOWPOWER)
+ // }}}
+ ) axilwskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY),
+ .i_data({ S_AXIL_WDATA, S_AXIL_WSTRB }),
+ .o_valid(wskd_valid), .i_ready(axil_write_ready),
+ .o_data({ wskd_data, wskd_strb })
+ // }}}
+ );
+
+ assign axil_write_ready = clk_active && awskd_valid && wskd_valid
+ && (!S_AXIL_BVALID || S_AXIL_BREADY);
+
+ initial axil_bvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_bvalid <= 0;
+ else if (axil_write_ready)
+ axil_bvalid <= 1;
+ else if (S_AXIL_BREADY)
+ axil_bvalid <= 0;
+
+ assign S_AXIL_BVALID = axil_bvalid;
+ assign S_AXIL_BRESP = 2'b00;
+ // }}}
+
+ //
+ // Read signaling
+ //
+ // {{{
+
+ skidbuffer #(
+ // {{{
+ .OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB),
+ .OPT_LOWPOWER(OPT_LOWPOWER)
+ // }}}
+ ) axilarskid(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_reset(i_reset),
+ .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY),
+ .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]),
+ .o_valid(arskd_valid), .i_ready(axil_read_ready),
+ .o_data(arskd_addr)
+ // }}}
+ );
+
+ assign axil_read_ready = clk_active && arskd_valid
+ && (!axil_read_valid || S_AXIL_RREADY);
+
+ initial axil_read_valid = 1'b0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axil_read_valid <= 1'b0;
+ else if (axil_read_ready)
+ axil_read_valid <= 1'b1;
+ else if (S_AXIL_RREADY)
+ axil_read_valid <= 1'b0;
+
+ assign S_AXIL_RVALID = axil_read_valid;
+ assign S_AXIL_RDATA = axil_read_data;
+ assign S_AXIL_RRESP = 2'b00;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // AXI-lite controlled logic
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // last_stalled -- used in overflow checking
+ // {{{
+ initial last_stalled = 1'b0;
+ always @(posedge i_clk)
+ last_stalled <= (!i_reset) && (S_AXIS_TVALID && !S_AXIS_TREADY);
+ // }}}
+
+ // last_tlast -- used to check for protocol violations in overflow next
+ // {{{
+ always @(posedge i_clk)
+ if (!OPT_TLAST_SYNC)
+ last_tlast <= 0;
+ else
+ last_tlast <= S_AXIS_TLAST;
+ // }}}
+
+ // last_tdata -- used for overflow checking
+ // {{{
+ always @(posedge i_clk)
+ last_tdata <= S_AXIS_TDATA;
+ // }}}
+
+ // overflow
+ // {{{
+ // Capture and check whether or not the incoming data stream overflowed
+ // This is primarily a check for AXI-stream protocol violations, since
+ // you can't really overflow an AXI stream when following protocol. The
+ // problem is that many stream sources--such as ADCs for example--can't
+ // handle back-pressure. Hence, checking for stream violations can
+ // be used in those cases to check for overflows. The check is only
+ // so good, however, since an overflow condition might take place if
+ // an ADC produces two consecutive (identical) values, one of which gets
+ // skipped--and this check will not capture that.
+ initial overflow = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ overflow <= 0;
+ else if (last_stalled)
+ begin
+ // The overflow pulse is only one clock period long
+ overflow <= 0;
+ if (!sskd_valid)
+ overflow <= 1;
+ if (S_AXIS_TDATA != last_tdata)
+ overflow <= 1;
+ if (OPT_TLAST_SYNC && S_AXIS_TLAST != last_tlast)
+ overflow <= 1;
+
+ // This will be caught by r_err and r_continuous
+ end
+ // }}}
+
+ // w_cmd_abort, cmd_abort -- Abort transaction on user request
+ // {{{
+
+ // w_cmd_abort is a combinational value capturing a user abort request
+ // {{{
+ always @(*)
+ begin
+ w_cmd_abort = 0;
+ w_cmd_abort = (axil_write_ready && awskd_addr == CMD_CONTROL)
+ && (wskd_strb[3] && wskd_data[31:24] == ABORT_KEY);
+ if (!r_busy)
+ w_cmd_abort = 0;
+ end
+ // }}}
+
+ // cmd_abort latches the user request until the abort is complete
+ // {{{
+ initial cmd_abort = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ cmd_abort <= 0;
+ else if (!r_busy)
+ cmd_abort <= 0;
+ else
+ cmd_abort <= cmd_abort || w_cmd_abort;
+ // }}}
+ // }}}
+
+ //
+ // Start command
+ //
+ always @(*)
+ if (r_busy)
+ w_cmd_start = 0;
+ else begin
+ w_cmd_start = 0;
+ if ((axil_write_ready && awskd_addr == CMD_CONTROL)
+ && (wskd_strb[3] && wskd_data[31]))
+ w_cmd_start = 1;
+ if (r_err && !wskd_data[30])
+ w_cmd_start = 0;
+ if (zero_length)
+ w_cmd_start = 0;
+ end
+
+ // r_busy, r_complete -- Calculate busy or complete flags
+ // {{{
+ initial r_busy = 0;
+ initial r_complete = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ r_busy <= 0;
+ r_complete <= 0;
+ end else if (!r_busy)
+ begin
+ // Core is idle, waiting for a command to start
+ if (w_cmd_start)
+ r_busy <= 1'b1;
+
+ // Any write to the control register will clear the
+ // completion flag
+ if (axil_write_ready && awskd_addr == CMD_CONTROL)
+ r_complete <= 1'b0;
+ end else if (w_complete)
+ begin
+ // Clear busy once the transaction is complete
+ // This includes clearing busy on any error
+ r_complete <= 1;
+ r_busy <= 1'b0;
+ end
+ // }}}
+
+ // o_int -- interrupt generation
+ // {{{
+ initial o_int = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ o_int <= 0;
+ else
+ o_int <= (r_busy && w_complete)
+ || (r_continuous && overflow);
+ // }}}
+
+ // r_err, r_errcode: Error conditions checking
+ // {{{
+ initial r_err = 0;
+ initial r_errcode = ERRCODE_NOERR;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ r_err <= 0;
+ r_errcode <= ERRCODE_NOERR;
+ end else if (!r_busy)
+ begin
+ if (r_continuous && overflow)
+ begin
+ r_err <= 1;
+ r_errcode[ERRCODE_OVERFLOW] <= 1'b1;
+ end
+
+ if (axil_write_ready && awskd_addr == CMD_CONTROL
+ && wskd_strb[3] && wskd_data[30])
+ begin
+ r_err <= 0;
+ r_errcode <= ERRCODE_NOERR;
+ end
+ end else if (r_busy)
+ begin
+ if (M_AXI_BVALID && M_AXI_BREADY && M_AXI_BRESP[1])
+ begin
+ r_err <= 1'b1;
+ if (M_AXI_BRESP[0])
+ r_errcode[ERRCODE_DECERR] <= 1'b1;
+ else
+ r_errcode[ERRCODE_SLVERR] <= 1'b1;
+ end
+
+ if (overflow)
+ begin
+ r_err <= 1'b1;
+ r_errcode[ERRCODE_OVERFLOW] <= 1'b1;
+ end
+ end
+ // }}}
+
+ // r_continuous
+ // {{{
+ initial r_continuous = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ r_continuous <= 0;
+ else begin
+ if (r_continuous && overflow)
+ r_continuous <= 1'b0;
+ if (!r_busy && axil_write_ready && awskd_addr == CMD_CONTROL)
+ r_continuous <= wskd_strb[3] && wskd_data[28];
+ end
+ // }}}
+
+ // wide_*
+ // {{{
+ always @(*)
+ begin
+ wide_address = 0;
+ wide_address[C_AXI_ADDR_WIDTH-1:0] = cmd_addr;
+
+ wide_current_address = 0;
+ wide_current_address[C_AXI_ADDR_WIDTH-1:0] = axi_addr;
+
+ wide_length = 0;
+ wide_length[ADDRLSB +: LGLENW] = cmd_length_w;
+
+ wide_len_remaining = 0;
+ wide_len_remaining[ADDRLSB +: LGLENW] = r_remaining_w;
+ end
+ // }}}
+
+ // new_* wires created via apply_wstrb
+ // {{{
+ assign new_cmdaddrlo= apply_wstrb(
+ wide_address[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+ assign new_cmdaddrhi=apply_wstrb(
+ wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+ assign new_lengthlo= apply_wstrb(
+ wide_length[C_AXIL_DATA_WIDTH-1:0],
+ wskd_data, wskd_strb);
+ assign new_lengthhi= apply_wstrb(
+ wide_length[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH],
+ wskd_data, wskd_strb);
+ // }}}
+
+ // new_wideaddr, new_widelen
+ // {{{
+ // These are the wide_* adjusted for any write, and then adjusted again
+ // to make sure they are within the correct number of bits for the
+ // interface.
+ always @(*)
+ begin
+ new_wideaddr = wide_address;
+ if (awskd_addr == CMD_ADDRLO)
+ new_wideaddr[C_AXIL_DATA_WIDTH-1:0]
+ = new_cmdaddrlo;
+ if (awskd_addr == CMD_ADDRHI)
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH]
+ = new_cmdaddrhi;
+ if (!OPT_UNALIGNED)
+ new_wideaddr[ADDRLSB-1:0] = 0;
+
+ // We only support C_AXI_ADDR_WIDTH address bits
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] = 0;
+
+ //
+ ///////////////
+ //
+
+ new_widelen = wide_length;
+ if (awskd_addr == CMD_LENLO)
+ new_widelen[C_AXIL_DATA_WIDTH-1:0] = new_lengthlo;
+ if (awskd_addr == CMD_LENHI)
+ new_widelen[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH]
+ = new_lengthhi;
+
+ // We only support integer numbers of words--even if unaligned
+ new_widelen[ADDRLSB-1:0] = 0;
+
+ // We only support LGLEN length bits
+ new_widelen[2*C_AXIL_DATA_WIDTH-1:LGLEN] = 0;
+ end
+ // }}}
+
+ // cmd_addr, cmd_length_w, r_increment, zero_length, aw_multiple_*
+ // {{{
+ initial r_increment = 1'b1;
+ initial cmd_addr = 0;
+ initial cmd_length_w = 0; // Counts in bytes
+ initial zero_length = 1;
+ initial aw_multiple_full_bursts = 0;
+ initial aw_multiple_fixed_bursts = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ // {{{
+ r_increment <= 1'b1;
+ cmd_addr <= 0;
+ cmd_length_w <= 0;
+ zero_length <= 1;
+ aw_multiple_full_bursts <= 0;
+ aw_multiple_fixed_bursts <= 0;
+ // }}}
+ end else if (axil_write_ready && !r_busy)
+ begin // Set the command, address, and length prior to operation
+ // {{{
+ case(awskd_addr)
+ CMD_CONTROL:
+ r_increment <= !wskd_data[27];
+ CMD_ADDRLO:
+ cmd_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0];
+ CMD_ADDRHI: if (C_AXI_ADDR_WIDTH > C_AXIL_DATA_WIDTH)
+ begin
+ cmd_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0];
+ end
+ CMD_LENLO: begin
+ cmd_length_w <= new_widelen[ADDRLSB +: LGLENW];
+ zero_length <= (new_widelen[ADDRLSB +: LGLENW] == 0);
+ aw_multiple_full_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAXBURST)];
+ aw_multiple_fixed_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAX_FIXED_BURST)];
+
+ end
+ CMD_LENHI: if (LGLEN > C_AXIL_DATA_WIDTH)
+ begin
+ cmd_length_w <= new_widelen[ADDRLSB +: LGLENW];
+ zero_length <= (new_widelen[ADDRLSB +: LGLENW] == 0);
+ aw_multiple_full_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAXBURST)];
+ aw_multiple_fixed_bursts <= |new_widelen[LGLEN-1:(ADDRLSB+LGMAX_FIXED_BURST)];
+ end
+ default: begin end
+ endcase
+ // }}}
+ end else if (r_busy)
+ begin // Updated cmd_addr && cmd_length during operation
+ // {{{
+ // Capture the last address written to in case of r_continuous
+ // (where we'll want to start again from this address),
+ // cmd_abort (where we'll want to know how far we got), or
+ // a bus error (where again we'll want to know how far we got)
+ if (r_continuous||axi_abort_pending)
+ cmd_addr <= axi_addr;
+
+ // Capture the number of remaining requests on either an error
+ // or an abort. Need to be careful here that we don't capture
+ // this address twice--hence the check for
+ // w_cmd_abort && !cmd_abort, and again for BVALID && !r_err
+ //
+ // Note that we can't check for axi_abort_pending here, since
+ // as soon as axi_abort_pending becomes true then cmd_length_w
+ // will get set to zero. Hence we need to capture this before
+ // axi_abort_pending gets set.
+ //
+ // Note that this is only an *approximate* length--especially
+ // in the case of either a bus error or an overflow, in which
+ // cases we won't really know what writes have been accomplished
+ // only that the last one failed. In that case, this will
+ // indicate the amount of writes we haven't requested
+ // (yet)--knowing that at least one (or more) of those prior
+ // must've failed. In the case of an overflow error, the
+ // overflow error may (or may not) have been written to memory
+ // by this time.
+ if (!axi_abort_pending && (cmd_abort || r_err
+ || (M_AXI_BVALID && M_AXI_BRESP[1])))
+ begin
+ cmd_length_w <= aw_requests_remaining;
+ zero_length <= (aw_requests_remaining == 0);
+ aw_multiple_full_bursts <= |aw_requests_remaining[LGLENW-1:LGMAXBURST];
+ aw_multiple_fixed_bursts <= |aw_requests_remaining[LGLENW-1:LGMAX_FIXED_BURST];
+ end
+
+ // Note that, because cmd_addr and cmd_length_w here aren't set
+ // on the same conditions that it is possible, on an error,
+ // that the two will not match.
+ // }}}
+ end
+ // }}}
+
+ // w_status_word
+ // {{{
+ always @(*)
+ begin
+ w_status_word = 0;
+
+ // The ABORT_KEY needs to be chosen so as not to look
+ // like these bits, lest someone read from the register
+ // and write back to it accidentally aborting any transaction
+ w_status_word[31] = r_busy;
+ w_status_word[30] = r_err;
+ w_status_word[29] = r_complete;
+ w_status_word[28] = r_continuous;
+ w_status_word[27] = !r_increment;
+ w_status_word[26] = !tlast_syncd;
+ w_status_word[25:23] = r_errcode;
+ w_status_word[22] = cmd_abort;
+ w_status_word[20:16] = LGFIFO;
+ end
+ // }}}
+
+ // axil_read_data
+ // {{{
+ always @(posedge i_clk)
+ if (!axil_read_valid || S_AXIL_RREADY)
+ begin
+ case(arskd_addr)
+ CMD_CONTROL: axil_read_data <= w_status_word;
+ CMD_ADDRLO: begin
+ if (!r_busy)
+ axil_read_data <= wide_address[C_AXIL_DATA_WIDTH-1:0];
+ else
+ axil_read_data <= wide_current_address[C_AXIL_DATA_WIDTH-1:0];
+ end
+ CMD_ADDRHI: begin
+ if (!r_busy)
+ axil_read_data <= wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ else
+ axil_read_data <= wide_current_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ end
+ CMD_LENLO: begin
+ if (!r_busy)
+ axil_read_data <= wide_length[C_AXIL_DATA_WIDTH-1:0];
+ else
+ axil_read_data <= wide_len_remaining[C_AXIL_DATA_WIDTH-1:0];
+ end
+ CMD_LENHI: begin
+ if (!r_busy)
+ axil_read_data <= wide_length[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ else
+ axil_read_data <= wide_len_remaining[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH];
+ end
+ default: axil_read_data <= 0;
+ endcase
+ end
+ // }}}
+
+ function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb;
+ // {{{
+ input [C_AXIL_DATA_WIDTH-1:0] prior_data;
+ input [C_AXIL_DATA_WIDTH-1:0] new_data;
+ input [C_AXIL_DATA_WIDTH/8-1:0] wstrb;
+
+ integer k;
+ for(k=0; k<C_AXIL_DATA_WIDTH/8; k=k+1)
+ begin
+ apply_wstrb[k*8 +: 8]
+ = wstrb[k] ? new_data[k*8 +: 8] : prior_data[k*8 +: 8];
+ end
+ endfunction
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The data FIFO section
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // Reset the FIFO between bursts, as long as r_continuous isn't set
+ assign reset_fifo = i_reset || (!r_busy && (!r_continuous || r_err));
+ assign write_to_fifo = sskd_valid && sskd_ready && tlast_syncd;
+ assign read_from_fifo = M_AXI_WVALID && M_AXI_WREADY
+ && !axi_abort_pending;
+
+ // We are ready if the FIFO isn't full and ...
+ // if OPT_TREADY_WHILE_IDLE is true
+ // at which point we ignore incoming data when we aren't
+ // busy, or
+ // if we aren't resetting the FIFO--that is, if data is actually
+ // going into the FIFO, or
+ // if we are ever out of synchronization--then we can ignore data
+ // until the next TLAST comes, where we must realign
+ // ourselves
+ assign sskd_ready = clk_active && !fifo_full
+ && (OPT_TREADY_WHILE_IDLE
+ || !reset_fifo || !tlast_syncd);
+
+ generate if (OPT_TLAST_SYNC)
+ begin : GEN_TLAST_SYNC
+ reg r_tlast_syncd;
+ // If the user has set OPT_TLAST_SYNC, then he wants to make
+ // certain that we don't start writing until the first stream
+ // value after the TLAST packet indicating an end of packet.
+ // For this cause, we'll maintain an r_tlast_syncd value
+ // indicating that the last value was a TLAST. If, at any
+ // time afterwards, a value is accepted into the stream but not
+ // into the FIFO, then the stream is now out of sync and
+ // r_tlast_syncd will drop.
+ //
+ // Note, this doesn't catch the case where the FIFO can't keep
+ // up. Lost data (might be) caught by overflow below.
+ initial r_tlast_syncd = 1;
+ always @(posedge i_clk)
+ if (!S_AXI_ARESETN)
+ r_tlast_syncd <= 1;
+ else if (sskd_valid && sskd_ready)
+ begin
+ if (sskd_last)
+ r_tlast_syncd <= 1;
+ else if (reset_fifo)
+ r_tlast_syncd <= 0;
+ end
+
+ assign tlast_syncd = r_tlast_syncd;
+ end else begin : NO_TLAST_SYNC
+
+ //
+ // If the option isn't set, then we are always synchronized.
+ //
+ assign tlast_syncd = 1;
+
+ // Verilator lint_off UNUSED
+ wire unused_tlast_sync;
+ assign unused_tlast_sync = &{ 1'b0, sskd_last };
+ // Verilator lint_on UNUSED
+ end endgenerate
+
+ // Incoming FIFO
+ // {{{
+ generate if (C_AXIS_TUSER_WIDTH > 0)
+ begin : FIFO_WITH_USER_DATA
+
+ sfifo #(
+ // {{{
+ .BW(C_AXIS_TUSER_WIDTH + C_AXI_DATA_WIDTH),
+ .LGFLEN(LGFIFO), .OPT_ASYNC_READ(OPT_ASYNCMEM)
+ // }}}
+ ) u_sfifo (
+ // {{{
+ .i_clk(i_clk), .i_reset(reset_fifo),
+ .i_wr(write_to_fifo),
+ .i_data({ sskd_user, sskd_data }),
+ .o_full(fifo_full), .o_fill(fifo_fill),
+ .i_rd(read_from_fifo), .o_data(fifo_data),
+ .o_empty(fifo_empty)
+ // }}}
+ );
+
+ assign { M_AXI_WUSER, M_AXI_WDATA } = fifo_data;
+
+ end else begin : NO_USER_DATA
+
+ sfifo #(
+ // {{{
+ .BW(C_AXI_DATA_WIDTH),
+ .LGFLEN(LGFIFO), .OPT_ASYNC_READ(OPT_ASYNCMEM)
+ // }}}
+ ) u_sfifo (
+ // {{{
+ .i_clk(i_clk), .i_reset(reset_fifo),
+ .i_wr(write_to_fifo), .i_data(sskd_data),
+ .o_full(fifo_full), .o_fill(fifo_fill),
+ .i_rd(read_from_fifo), .o_data(fifo_data),
+ .o_empty(fifo_empty)
+ // }}}
+ );
+
+ assign M_AXI_WDATA = fifo_data;
+ assign M_AXI_WUSER = 0;
+
+ // Make Verilator happy
+ // {{{
+ // Verilator lint_off UNUSED
+ wire unused_tuser;
+ assign unused_tuser = &{ 1'b0, sskd_user };
+ // Verilator lint_on UNUSED
+ // }}}
+ end endgenerate
+
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The outgoing AXI (full) protocol section
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ //
+
+ // Some counters to keep track of our state
+ // {{{
+
+
+ // Count the number of word writes left to be requested, starting
+ // with the overall command length and then reduced by M_AWLEN on
+ // each address write
+ // {{{
+ always @(*)
+ begin
+ // aw_next_remaining = aw_requests_remaining;
+ // if (phantom_start)
+ // aw_next_remaining = aw_requests_remaining
+ // + (~{{ (LGLENW-8){1'b0}}, M_AXI_AWLEN });
+ // // - (M_AXI_AWLEN+1)
+ // // + (~M_AXI_AWLEN+1) - 1
+ // // + ~M_AXI_AWLEN
+ aw_next_remaining = aw_requests_remaining
+ + { {(LGLENW-8){phantom_start}},
+ (phantom_start) ? ~M_AXI_AWLEN : 8'h00};
+ end
+
+ initial r_pre_start = 1;
+ always @(posedge i_clk)
+ if (!r_busy)
+ r_pre_start <= 1;
+ else
+ r_pre_start <= 0;
+
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ aw_needs_alignment <= 0;
+
+ if (|new_wideaddr[ADDRLSB +: LGMAXBURST])
+ begin
+ if (|new_widelen[LGLEN-1:(LGMAXBURST+ADDRLSB)])
+ aw_needs_alignment <= 1;
+ if (~new_wideaddr[ADDRLSB +: LGMAXBURST]
+ < new_widelen[ADDRLSB +: LGMAXBURST])
+ aw_needs_alignment <= 1;
+ end
+ end
+
+ initial aw_none_remaining = 1;
+ initial aw_requests_remaining = 0;
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ aw_requests_remaining <= cmd_length_w;
+ aw_none_remaining <= zero_length;
+ aw_multiple_bursts_remaining <= |cmd_length_w[LGLENW-1:LGMAXBURST+1];
+ end else if (cmd_abort || axi_abort_pending)
+ begin
+ aw_requests_remaining <= 0;
+ aw_none_remaining <= 1;
+ aw_multiple_bursts_remaining <= 0;
+ end else if (phantom_start)
+ begin
+ aw_requests_remaining <= aw_next_remaining;
+ aw_none_remaining<= !aw_multiple_bursts_remaining
+ &&(aw_next_remaining[LGMAXBURST:0] == 0);
+ aw_multiple_bursts_remaining
+ <= |aw_next_remaining[LGLENW-1:LGMAXBURST+1];
+ end
+ // }}}
+
+ // Calculate the maximum possible burst length, ignoring 4kB boundaries
+ // {{{
+ always @(*)
+ addralign = 1+(~cmd_addr[ADDRLSB +: LGMAXBURST]);
+
+ always @(*)
+ begin
+ initial_burstlen = (1<<LGMAXBURST);
+ if (!r_increment)
+ begin
+ initial_burstlen = MAX_FIXED_BURST;
+ if (!aw_multiple_fixed_bursts)
+ initial_burstlen = { 1'b0, cmd_length_w[LGMAXBURST-1:0] };
+ end else if (aw_needs_alignment)
+ initial_burstlen = { 1'b0, addralign };
+ else if (!aw_multiple_full_bursts)
+ initial_burstlen = { 1'b0, cmd_length_w[LGMAXBURST-1:0] };
+ end
+
+ initial r_max_burst = 0;
+ always @(posedge i_clk)
+ if (!r_busy || r_pre_start)
+ begin
+ // Force us to align ourself early
+ // That way we don't need to check for
+ // alignment (again) later
+ r_max_burst <= initial_burstlen;
+ end else if (phantom_start)
+ begin
+ // Verilator lint_off WIDTH
+ if (r_increment || LGMAXBURST <= LGMAX_FIXED_BURST)
+ begin
+ if (!aw_multiple_bursts_remaining
+ && aw_next_remaining[LGMAXBURST:0] < (1<<LGMAXBURST))
+ r_max_burst <= { 1'b0, aw_next_remaining[7:0] };
+ else
+ r_max_burst <= (1<<LGMAXBURST);
+ end else begin
+ if (!aw_multiple_bursts_remaining
+ && aw_next_remaining[LGMAXBURST:0] < MAX_FIXED_BURST)
+ r_max_burst <= { 1'b0, aw_next_remaining[7:0] };
+ else
+ r_max_burst <= MAX_FIXED_BURST;
+ end
+ // Verilator lint_on WIDTH
+ end
+ // }}}
+
+ // Count the number of bursts outstanding--these are the number of
+ // AWVALIDs that have been accepted, but for which the BVALID has not
+ // (yet) been returned.
+ // {{{
+ initial aw_last_outstanding = 0;
+ initial aw_none_outstanding = 1;
+ initial aw_bursts_outstanding = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ aw_bursts_outstanding <= 0;
+ aw_none_outstanding <= 1;
+ aw_last_outstanding <= 0;
+ end else case ({ phantom_start, M_AXI_BVALID && M_AXI_BREADY })
+ 2'b01: begin
+ aw_bursts_outstanding <= aw_bursts_outstanding - 1;
+ aw_none_outstanding <= (aw_bursts_outstanding == 1);
+ aw_last_outstanding <= (aw_bursts_outstanding == 2);
+ end
+ 2'b10: begin
+ aw_none_outstanding <= 0;
+ aw_bursts_outstanding <= aw_bursts_outstanding + 1;
+ aw_last_outstanding <= (aw_bursts_outstanding == 0);
+ end
+ default: begin end
+ endcase
+ // }}}
+
+ // Are we there yet?
+ // {{{
+ // We can't just look for the last BVALID, since ... it might be
+ // possible to receive an abort before the FIFO is full enough to
+ // initiate the first burst.
+ always @(*)
+ if (!r_busy)
+ w_complete = 0;
+ else
+ w_complete = !M_AXI_AWVALID && (aw_none_remaining)
+ &&((aw_last_outstanding && M_AXI_BVALID)
+ || aw_none_outstanding);
+ // }}}
+
+ // Are we stopping early? Aborting something ongoing?
+ // {{{
+ initial axi_abort_pending = 0;
+ always @(posedge i_clk)
+ if (i_reset || !r_busy)
+ axi_abort_pending <= 0;
+ else begin
+ if (M_AXI_BVALID && M_AXI_BREADY && M_AXI_BRESP[1])
+ axi_abort_pending <= 1;
+ if (cmd_abort)
+ axi_abort_pending <= 1;
+ if (r_err)
+ axi_abort_pending <= 1;
+ end
+ // }}}
+
+ // Count the number of WVALIDs yet to be sent on the write channel
+ // {{{
+ initial wr_none_pending = 1;
+ initial wr_writes_pending = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ wr_writes_pending <= 0;
+ wr_none_pending <= 1;
+ end else case ({ phantom_start,
+ M_AXI_WVALID && M_AXI_WREADY })
+ 2'b00: begin end
+ 2'b01: begin
+ wr_writes_pending <= wr_writes_pending - 1;
+ wr_none_pending <= (wr_writes_pending == 1);
+ end
+ 2'b10: begin
+ wr_writes_pending <= wr_writes_pending + (M_AXI_AWLEN[LGMAXBURST-1:0] + 1);
+ wr_none_pending <= 0;
+ end
+ 2'b11: begin
+ wr_writes_pending <= wr_writes_pending + (M_AXI_AWLEN[LGMAXBURST-1:0]);
+ wr_none_pending <= (M_AXI_WLAST);
+ end
+ endcase
+ // }}}
+
+ // So that we can monitor where we are at, and perhaps restart it
+ // later, keep track of the current address used by the W-channel
+ // {{{
+ initial axi_addr = 0;
+ always @(posedge i_clk)
+ begin
+ if (!r_busy)
+ axi_addr <= cmd_addr;
+ else if (axi_abort_pending || !r_increment)
+ // Stop incrementing the address following an abort
+ axi_addr <= axi_addr;
+ else if (M_AXI_WVALID && M_AXI_WREADY)
+ axi_addr <= axi_addr + (1<<ADDRLSB);
+
+ if (!OPT_UNALIGNED)
+ axi_addr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // Count the number of words remaining to be written on the W channel
+ // {{{
+ // initial r_none_remaining = 1;
+ initial r_remaining_w = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ begin
+ r_remaining_w <= 0;
+ // r_none_remaining <= 1;
+ end else if (!r_busy)
+ begin
+ r_remaining_w<= cmd_length_w;
+ // r_none_remaining <= zero_length;
+ end else if (M_AXI_WVALID && M_AXI_WREADY)
+ begin
+ r_remaining_w <= r_remaining_w - 1;
+ // r_none_remaining <= (r_remaining_w == 1);
+ end
+ // }}}
+
+ //
+ // }}}
+
+ // Phantom starts
+ // {{{
+ // Since we can't use the xREADY signals in our signaling, we hvae to
+ // be ready to generate both AWVALID and WVALID on the same cycle,
+ // and then hold AWVALID until it has been accepted. This means we
+ // can't use AWVALID as our burst start signal like we could in the
+ // slave. Instead, we'll use a "phantom" start signal. This signal
+ // is local here in our code. When this signal goes high, AWVALID
+ // and WVALID go high at the same time. Then, if AWREADY isn't held,
+ // we can still update all of our internal counters as though it were,
+ // based upon the phantom_start signal, and continue as though
+ // AWVALID were accepted on its first clock period.
+
+ always @(*)
+ begin
+ // We start again if there's more information to transfer
+ w_phantom_start = !aw_none_remaining;
+
+ // But not if the amount of information we need isn't (yet)
+ // in the FIFO.
+ if (!sufficiently_filled)
+ w_phantom_start = 0;
+
+ // Insist on a minimum of one clock between burst starts,
+ // since our burst length calculation takes a clock to do
+ if (phantom_start || r_pre_start)
+ w_phantom_start = 0;
+
+ if (M_AXI_AWVALID && !M_AXI_AWREADY)
+ w_phantom_start = 0;
+
+ // If we're still writing the last burst, then don't start
+ // any new ones
+ if (M_AXI_WVALID && (!M_AXI_WLAST || !M_AXI_WREADY))
+ w_phantom_start = 0;
+
+ // Finally, don't start any new bursts if we aren't already
+ // busy transmitting, or if we are in the process of aborting
+ // our transfer
+ if (!r_busy || cmd_abort || axi_abort_pending)
+ w_phantom_start = 0;
+ end
+
+ initial phantom_start = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ phantom_start <= 0;
+ else
+ phantom_start <= w_phantom_start;
+ // }}}
+
+
+ //
+ // WLAST
+ // {{{
+ always @(posedge i_clk)
+ if (!r_busy)
+ begin
+ axi_wlast <= (cmd_length_w == 1);
+ end else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (w_phantom_start)
+ axi_wlast <= (r_max_burst == 1);
+ else if (phantom_start)
+ axi_wlast <= (M_AXI_AWLEN == 1);
+ else
+ axi_wlast <= (wr_writes_pending == 1 + (M_AXI_WVALID ? 1:0));
+ end
+ // }}}
+
+ // Calculate AWLEN and AWADDR for the next AWVALID
+ // {{{
+ //
+ initial data_available = 0;
+ always @(posedge i_clk)
+ if (reset_fifo)
+ data_available <= 0;
+ else if (axi_abort_pending)
+ data_available <= fifo_fill + (write_to_fifo ? 1:0);
+ else case({ write_to_fifo, phantom_start })
+ 2'b10: data_available <= data_available + 1;
+ // Verilator lint_off WIDTH
+ 2'b01: data_available <= data_available - (M_AXI_AWLEN+1);
+ 2'b11: data_available <= data_available - (M_AXI_AWLEN);
+ // Verilator lint_on WIDTH
+ default: begin end
+ endcase
+
+ always @(*)
+ if (|aw_requests_remaining[LGLENW-1:LGMAXBURST])
+ sufficiently_filled = |data_available[LGFIFO:LGMAXBURST];
+ else
+ sufficiently_filled = (data_available[LGMAXBURST-1:0]
+ >= aw_requests_remaining[LGMAXBURST-1:0]);
+
+ //
+ // axi_awlen
+ // {{{
+ generate if (LGMAXBURST >= 8)
+ begin : GEN_BIG_AWLEN
+
+ always @(posedge i_clk)
+ if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ axi_awlen <= r_max_burst[7:0] - 8'd1;
+
+ end else begin : GEN_SHORT_AWLEN
+
+ always @(posedge i_clk)
+ if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ begin
+ axi_awlen <= { {(8-LGMAXBURST){1'b0}}, r_max_burst } - 8'd1;
+ axi_awlen[7:LGMAXBURST] <= 0;
+ end
+
+ end endgenerate
+ // }}}
+
+ always @(posedge i_clk)
+ begin
+ if (M_AXI_AWVALID && M_AXI_AWREADY)
+ begin
+ axi_awaddr[ADDRLSB-1:0] <= 0;
+ // Verilator lint_off WIDTH
+ if (r_increment)
+ axi_awaddr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ <= axi_awaddr[C_AXI_ADDR_WIDTH-1:ADDRLSB]
+ + (M_AXI_AWLEN+1);
+ end
+ // Verilator lint_on WIDTH
+
+ if (!r_busy)
+ axi_awaddr<= cmd_addr;
+
+ if (!OPT_UNALIGNED)
+ axi_awaddr[ADDRLSB-1:0] <= 0;
+ end
+ // }}}
+
+ // AWVALID
+ // {{{
+ initial axi_awvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axi_awvalid <= 0;
+ else if (!M_AXI_AWVALID || M_AXI_AWREADY)
+ axi_awvalid <= w_phantom_start;
+ // }}}
+
+ // WVALID
+ // {{{
+ initial axi_wvalid = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ axi_wvalid <= 0;
+ else if (!M_AXI_WVALID || M_AXI_WREADY)
+ begin
+ if (M_AXI_WVALID && !M_AXI_WLAST)
+ axi_wvalid <= 1;
+ else
+ axi_wvalid <= w_phantom_start;
+ end
+ // }}}
+
+ // axi_wstrb
+ // {{{
+ always @(posedge i_clk)
+ if (!M_AXI_WVALID || M_AXI_WREADY)
+ axi_wstrb <= (axi_abort_pending) ? 0:-1;
+ // }}}
+
+ // Fixed bus values
+ // {{{
+ assign M_AXI_AWVALID= axi_awvalid;
+ assign M_AXI_AWID = AXI_ID;
+ assign M_AXI_AWADDR = axi_awaddr;
+ assign M_AXI_AWLEN = axi_awlen;
+ // Verilator lint_off WIDTH
+ assign M_AXI_AWSIZE = $clog2(C_AXI_DATA_WIDTH)-3;
+ // Verilator lint_on WIDTH
+ assign M_AXI_AWBURST= { 1'b0, r_increment };
+ assign M_AXI_AWLOCK = 0;
+ assign M_AXI_AWCACHE= 4'h3;
+ assign M_AXI_AWPROT = 0;
+ assign M_AXI_AWQOS = 0;
+
+ assign M_AXI_WVALID = axi_wvalid;
+ assign M_AXI_WSTRB = axi_wstrb;
+ assign M_AXI_WLAST = axi_wlast;
+ // M_AXI_WLAST = ??
+
+ assign M_AXI_BREADY = 1;
+ // }}}
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // (Optional) Clock gating
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ generate if (OPT_CLKGATE)
+ begin : CLK_GATING
+ // {{{
+ reg gatep, r_clk_active;
+ reg gaten /* verilator clock_enable */;
+
+ // clk_active
+ // {{{
+ initial r_clk_active = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ r_clk_active <= 1'b1;
+ else begin
+ r_clk_active <= 1'b0;
+
+ if (r_busy)
+ r_clk_active <= 1'b1;
+ if (awskd_valid || wskd_valid || arskd_valid)
+ r_clk_active <= 1'b1;
+ if (S_AXIL_BVALID || S_AXIL_RVALID)
+ r_clk_active <= 1'b1;
+
+ // Activate the clock on incoming data
+ // reset_fifo = i_reset || (!r_busy && (!r_continuous || r_err));
+ // !reset_fifo= r_busy || (r_continuous && !r_err)
+ // !reset_fifo= (r_continuous && !r_err)
+ if (sskd_valid && !fifo_full
+ && (!tlast_syncd || (r_continuous && !r_err)))
+ r_clk_active <= 1'b1;
+ end
+
+ assign clk_active = r_clk_active;
+ // }}}
+ // Gate the clock here locally
+ // {{{
+ initial gatep = 1'b1;
+ always @(posedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ gatep <= 1'b1;
+ else
+ gatep <= clk_active;
+
+ initial gaten = 1'b1;
+ always @(negedge S_AXI_ACLK)
+ if (!S_AXI_ARESETN)
+ gaten <= 1'b1;
+ else
+ gaten <= gatep;
+
+ assign gated_clk = S_AXI_ACLK && gaten;
+
+ assign clk_active = r_clk_active;
+ // }}}
+ // }}}
+ end else begin : NO_CLK_GATING
+ // {{{
+ // Always active
+ assign clk_active = 1'b1;
+ assign gated_clk = S_AXI_ACLK;
+ // }}}
+ end endgenerate
+ // }}}
+
+ // Keep Verilator happy
+ // {{{
+ // Verilator coverage_off
+ // Verilator lint_off UNUSED
+ wire unused;
+ assign unused = &{ 1'b0, S_AXIL_AWPROT, S_AXIL_ARPROT, M_AXI_BID,
+ M_AXI_BRESP[0], fifo_empty,
+ wr_none_pending, S_AXIL_ARADDR[AXILLSB-1:0],
+ S_AXIL_AWADDR[AXILLSB-1:0],
+ new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH],
+ new_widelen };
+ // Verilator coverage_on
+ // Verilator lint_on UNUSED
+ // }}}
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+//
+// Formal properties
+// {{{
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+`ifdef FORMAL
+ //
+ // The formal properties for this unit are maintained elsewhere.
+ // This core does, however, pass a full prove (w/ induction) for all
+ // bus properties.
+ //
+ // ...
+ //
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-stream data interface
+ // {{{
+ //
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // (These are captured by the FIFO within)
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI-lite control interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ localparam F_AXIL_LGDEPTH = 4;
+
+ faxil_slave #(
+ // {{{
+ .C_AXI_DATA_WIDTH(C_AXIL_DATA_WIDTH),
+ .C_AXI_ADDR_WIDTH(C_AXIL_ADDR_WIDTH),
+ .F_LGDEPTH(F_AXIL_LGDEPTH),
+ .F_AXI_MAXWAIT(2),
+ .F_AXI_MAXDELAY(2),
+ .F_AXI_MAXRSTALL(3)
+ // }}}
+ ) faxil(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .i_axi_awvalid(S_AXIL_AWVALID),
+ .i_axi_awready(S_AXIL_AWREADY),
+ .i_axi_awaddr( S_AXIL_AWADDR),
+ .i_axi_awprot( S_AXIL_AWPROT),
+ //
+ .i_axi_wvalid(S_AXIL_WVALID),
+ .i_axi_wready(S_AXIL_WREADY),
+ .i_axi_wdata( S_AXIL_WDATA),
+ .i_axi_wstrb( S_AXIL_WSTRB),
+ //
+ .i_axi_bvalid(S_AXIL_BVALID),
+ .i_axi_bready(S_AXIL_BREADY),
+ .i_axi_bresp( S_AXIL_BRESP),
+ //
+ .i_axi_arvalid(S_AXIL_ARVALID),
+ .i_axi_arready(S_AXIL_ARREADY),
+ .i_axi_araddr( S_AXIL_ARADDR),
+ .i_axi_arprot( S_AXIL_ARPROT),
+ //
+ .i_axi_rvalid(S_AXIL_RVALID),
+ .i_axi_rready(S_AXIL_RREADY),
+ .i_axi_rdata( S_AXIL_RDATA),
+ .i_axi_rresp( S_AXIL_RRESP),
+ //
+ .f_axi_rd_outstanding(faxil_rd_outstanding),
+ .f_axi_wr_outstanding(faxil_wr_outstanding),
+ .f_axi_awr_outstanding(faxil_awr_outstanding)
+ // }}}
+ );
+
+ always @(*)
+ begin
+ assert(faxil_rd_outstanding == (S_AXIL_RVALID ? 1:0)
+ +(S_AXIL_ARREADY ? 0:1));
+ assert(faxil_wr_outstanding == (S_AXIL_BVALID ? 1:0)
+ +(S_AXIL_WREADY ? 0:1));
+ assert(faxil_awr_outstanding== (S_AXIL_BVALID ? 1:0)
+ +(S_AXIL_AWREADY ? 0:1));
+ end
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // The AXI master memory interface
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ //
+ // ...
+ //
+
+ 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_EXCLUSIVE(1'b0),
+ .OPT_NARROW_BURST(1'b0),
+ //
+ // ...
+ // }}}
+ ) faxi(
+ // {{{
+ .i_clk(S_AXI_ACLK), .i_axi_reset_n(S_AXI_ARESETN),
+ //
+ .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),
+ //
+ .i_axi_arvalid(1'b0),
+ .i_axi_arready(1'b0),
+ .i_axi_arid( M_AXI_AWID),
+ .i_axi_araddr( M_AXI_AWADDR),
+ .i_axi_arlen( M_AXI_AWLEN),
+ .i_axi_arsize( M_AXI_AWSIZE),
+ .i_axi_arburst(M_AXI_AWBURST),
+ .i_axi_arlock( M_AXI_AWLOCK),
+ .i_axi_arcache(M_AXI_AWCACHE),
+ .i_axi_arprot( M_AXI_AWPROT),
+ .i_axi_arqos( M_AXI_AWQOS),
+ //
+ .i_axi_rvalid(1'b0),
+ .i_axi_rready(1'b0),
+ .i_axi_rdata({(C_AXI_DATA_WIDTH){1'b0}}),
+ .i_axi_rlast(1'b0),
+ .i_axi_rresp(2'b00)
+ //
+ //
+ // }}}
+ );
+
+ //
+ // ...
+ //
+
+ always @(*)
+ assert(aw_bursts_outstanding
+ == faxi_awr_nbursts
+ + ((M_AXI_AWVALID&&!phantom_start) ? 1:0));
+
+ //
+ // ...
+ //
+
+ always @(posedge i_clk)
+ if (M_AXI_AWVALID)
+ begin
+ // ...
+ if (phantom_start)
+ begin
+ assert(wr_writes_pending == 0);
+ assert(wr_none_pending);
+ end else if ($past(phantom_start))
+ begin
+ assert(wr_writes_pending <= M_AXI_AWLEN+1);
+ end
+ end else begin
+ // ...
+ assert(wr_none_pending == (wr_writes_pending == 0));
+ end
+
+ always @(*)
+ if (r_busy && !OPT_UNALIGNED)
+ assert(M_AXI_AWADDR[ADDRLSB-1:0] == 0);
+
+ always @(*)
+ if (!OPT_UNALIGNED)
+ assert(cmd_addr[ADDRLSB-1:0] == 0);
+
+ //
+ // ...
+ //
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Other formal properties
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ // ...
+ //
+
+ always @(*)
+ if (!r_busy)
+ begin
+ assert(!M_AXI_AWVALID);
+ assert(!M_AXI_WVALID);
+ assert(!M_AXI_BVALID);
+ //
+ // ...
+ //
+ end
+
+ always @(*)
+ assert(zero_length == (cmd_length_w == 0));
+
+ //
+ // ...
+ //
+ always @(*)
+ if (phantom_start)
+ begin
+ assert(data_available >= (M_AXI_AWLEN+1));
+ end else if (M_AXI_AWVALID)
+ begin
+ assert(data_available <= (1<<LGFIFO));
+ end
+
+
+ always @(*)
+ if (phantom_start)
+ assert(wr_writes_pending == 0);
+
+ always @(*)
+ if (phantom_start)
+ begin
+ assert(fifo_fill >= (M_AXI_AWLEN+1));
+ end else if (!axi_abort_pending)
+ assert(fifo_fill >= wr_writes_pending);
+
+ always @(*)
+ if (r_busy)
+ begin
+ if (!aw_none_remaining && !phantom_start)
+ begin
+ assert(aw_requests_remaining
+ + wr_writes_pending == r_remaining_w);
+
+ // Make sure we don't wrap
+ assert(wr_writes_pending <= r_remaining_w);
+ end else if (!aw_none_remaining)
+ begin
+ assert(aw_requests_remaining == r_remaining_w);
+
+ // Make sure we don't wrap
+ assert(wr_writes_pending == 0);
+ end
+ end else
+ assert(!M_AXI_WVALID);
+
+ always @(*)
+ assert(aw_none_remaining == (aw_requests_remaining == 0));
+
+ always @(*)
+ if (r_busy)
+ assert(aw_multiple_bursts_remaining == (|aw_requests_remaining[LGLENW-1:LGMAXBURST+1]));
+
+ always @(*)
+ begin
+ assert(aw_last_outstanding == (aw_bursts_outstanding == 1));
+ assert(aw_none_outstanding == (aw_bursts_outstanding == 0));
+ end
+
+ //
+ // ...
+ //
+
+ always @(*)
+ if (r_complete)
+ assert(!r_busy);
+
+ always @(*)
+ assert(fifo_fill >= wr_writes_pending);
+
+ always @(*)
+ if (r_busy)
+ assert(r_max_burst <= (1<<LGMAXBURST));
+
+ always @(*)
+ if (r_busy && !r_pre_start)
+ assert((r_max_burst > 0) || (aw_requests_remaining == 0));
+
+
+ always @(*)
+ if (phantom_start)
+ begin
+ assert(M_AXI_AWVALID && M_AXI_WVALID);
+ assert(wr_none_pending);
+ // assert(drain_triggered);
+ end
+
+ always @(posedge i_clk)
+ if (phantom_start)
+ begin
+ assert(r_max_burst > 0);
+ assert(M_AXI_AWLEN == $past(r_max_burst)-1);
+ end
+
+ always @(*)
+ if (r_busy && !r_err && !cmd_abort && aw_requests_remaining == f_length)
+ assert(initial_burstlen > 0);
+
+ always @(*)
+ if ((LGMAXBURST < 8) && (r_busy))
+ assert(M_AXI_AWLEN+1 <= (1<<LGMAXBURST));
+
+ always @(*)
+ if (r_busy && !r_increment && (M_AXI_AWVALID
+ || ((aw_requests_remaining < cmd_length_w)
+ && (aw_requests_remaining > 0))))
+ assert(M_AXI_AWLEN+1 <= MAX_FIXED_BURST);
+
+ always @(*)
+ if (M_AXI_AWVALID && M_AXI_AWADDR[ADDRLSB +: LGMAXBURST])
+ begin
+ // If we are ever unaligned, our first step should be to
+ // align ourselves
+ assert(M_AXI_AWLEN+1 <=
+ 1 +(~M_AXI_AWADDR[ADDRLSB +: LGMAXBURST]));
+ end
+
+ always @(*)
+ if (!wr_none_pending)
+ begin
+ // Second alignment check: every burst must end aligned
+ if (r_increment)
+ assert(axi_addr[ADDRLSB +: LGMAXBURST] + wr_writes_pending
+ <= (1<<LGMAXBURST));
+ end
+
+ always @(posedge i_clk)
+ if (phantom_start)
+ begin
+ assert(axi_awlen == $past(r_max_burst[7:0]) - 8'd1);
+ if (r_increment && (cmd_length_w > axi_awlen + 1)
+ &&(aw_requests_remaining != cmd_length_w))
+ assert(M_AXI_AWADDR[ADDRLSB +: LGMAXBURST] == 0);
+ end
+
+ //
+ // ...
+ //
+
+ // }}}
+
+ //
+ // Synchronization properties
+ // {{{
+ always @(*)
+ if (fifo_full || !clk_active)
+ begin
+ assert(!sskd_ready);
+ end else if (OPT_TREADY_WHILE_IDLE)
+ begin
+ // If we aren't full, and we set TREADY whenever idle,
+ // then we should otherwise have TREADY set at all times
+ assert(sskd_ready);
+ end else if (!tlast_syncd)
+ begin
+ // If we aren't syncd, always be ready until we finally sync up
+ assert(sskd_ready);
+ end else if (reset_fifo)
+ begin
+ // If we aren't accepting any data, but are idling with TREADY
+ // low, then make sure we drop TREADY when idle
+ assert(!sskd_ready);
+ end else
+ // In all other cases, assert TREADY
+ assert(sskd_ready);
+
+
+ //
+ // ...
+ //
+ // }}}
+
+ //
+ // Error logic checking
+ // {{{
+ always @(*)
+ if (!r_err)
+ begin
+ assert(r_errcode == 0);
+ end else
+ assert(r_errcode != 0);
+
+ //
+ // ...
+ //
+
+ always @(posedge S_AXI_ACLK)
+ if (f_past_valid && $past(S_AXI_ARESETN) && $past(w_cmd_start)
+ &&!$past(overflow && r_continuous))
+ assert(!r_err);
+ // }}}
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Contract checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+
+ // 1. All data values must get sent, and none skipped
+ // Captured in logic above, since M_AXI_WDATA is registered
+ // within the FIFO and not our interface
+ //
+ // 2. No addresses skipped.
+ // ...
+ //
+
+ // 3. If we aren't incrementing addresses, then our current address
+ // should always be the axi address
+ always @(*)
+ if (r_busy && !r_increment)
+ assert(axi_addr == cmd_addr);
+
+ // }}}
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Cover checks
+ // {{{
+ ////////////////////////////////////////////////////////////////////////
+ //
+ //
+ reg cvr_aborted, cvr_buserr, cvr_abort_clear;
+ reg [2:0] cvr_continued;
+
+ initial { cvr_aborted, cvr_buserr } = 0;
+ always @(posedge i_clk)
+ if (i_reset)
+ { cvr_aborted, cvr_buserr } <= 0;
+ else if (r_busy && !axi_abort_pending)
+ begin
+ if (cmd_abort && wr_writes_pending > 0)
+ cvr_aborted <= 1;
+ if (M_AXI_BVALID && M_AXI_BRESP[1])
+ cvr_buserr <= 1;
+ end
+
+ always @(posedge i_clk)
+ if (i_reset)
+ cvr_abort_clear <= 1'b0;
+ else if (cvr_aborted && !cvr_buserr && !cmd_abort)
+ begin
+ cvr_abort_clear <= 1;
+ end
+
+ always @(posedge i_clk)
+ if (!i_reset)
+ begin
+ cover(cvr_abort_clear);
+ end
+
+ initial cvr_continued = 0;
+ always @(posedge i_clk)
+ if (i_reset || r_err || cmd_abort)
+ cvr_continued <= 0;
+ else begin
+ // Cover a continued transaction across two separate bursts
+ if (r_busy && r_continuous)
+ cvr_continued[0] <= 1;
+ if (!r_busy && cvr_continued[0])
+ cvr_continued[1] <= 1;
+ if (r_busy && cvr_continued[1])
+ cvr_continued[2] <= 1;
+
+ //
+ // Artificially force us to look for two separate runs
+ if((!r_busy)&&($changed(cmd_length_w)))
+ cvr_continued <= 0;
+ end
+
+ always @(posedge i_clk)
+ if (f_past_valid && !$past(i_reset) && !i_reset && $fell(r_busy))
+ begin
+ cover( r_err && cvr_aborted);
+ cover( r_err && cvr_buserr);
+ cover(!r_err);
+ if (!r_err && !axi_abort_pending && !cvr_aborted && !cvr_buserr)
+ begin
+ cover(cmd_length_w > 5);
+ cover(cmd_length_w > 8);
+ cover((cmd_length_w > 5)&&(cmd_addr[11:0] == 12'hff0));
+ cover(&cvr_continued && (cmd_length_w > 5));
+ end
+ end
+ // }}}
+
+ // This ends our formal property set
+`endif
+ // }}}
+endmodule
+`ifndef YOSYS
+`default_nettype wire
+`endif