From 3038edc09a2eb15762f2e58533f429489107520b Mon Sep 17 00:00:00 2001 From: Alejandro Soto Date: Wed, 6 Mar 2024 02:38:24 -0600 Subject: rtl/wb2axip: add to version control --- rtl/wb2axip/axivfifo.v | 1405 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1405 insertions(+) create mode 100644 rtl/wb2axip/axivfifo.v (limited to 'rtl/wb2axip/axivfifo.v') 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<= (1< 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<= 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)); + 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< 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 -- cgit v1.2.3