aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/blocks
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2020-01-23 16:10:22 -0800
committerMartin Braun <martin.braun@ettus.com>2020-01-28 09:35:36 -0800
commitbafa9d95453387814ef25e6b6256ba8db2df612f (patch)
tree39ba24b5b67072d354775272e687796bb511848d /fpga/usrp3/lib/rfnoc/blocks
parent3075b981503002df3115d5f1d0b97d2619ba30f2 (diff)
downloaduhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip
Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce the size of the repository. However, over the last half-decade, the split between the repositories has proven more burdensome than it has been helpful. By merging the FPGA code back, it will be possible to create atomic commits that touch both FPGA and UHD codebases. Continuous integration testing is also simplified by merging the repositories, because it was previously difficult to automatically derive the correct UHD branch when testing a feature branch on the FPGA repository. This commit also updates the license files and paths therein. We are therefore merging the repositories again. Future development for FPGA code will happen in the same repository as the UHD host code and MPM code. == Original Codebase and Rebasing == The original FPGA repository will be hosted for the foreseeable future at its original local location: https://github.com/EttusResearch/fpga/ It can be used for bisecting, reference, and a more detailed history. The final commit from said repository to be merged here is 05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as v4.0.0.0-pre-uhd-merge. If you have changes in the FPGA repository that you want to rebase onto the UHD repository, simply run the following commands: - Create a directory to store patches (this should be an empty directory): mkdir ~/patches - Now make sure that your FPGA codebase is based on the same state as the code that was merged: cd src/fpga # Or wherever your FPGA code is stored git rebase v4.0.0.0-pre-uhd-merge Note: The rebase command may look slightly different depending on what exactly you're trying to rebase. - Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge: git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches Note: Make sure that only patches are stored in your output directory. It should otherwise be empty. Make sure that you picked the correct range of commits, and only commits you wanted to rebase were exported as patch files. - Go to the UHD repository and apply the patches: cd src/uhd # Or wherever your UHD repository is stored git am --directory fpga ~/patches/* rm -rf ~/patches # This is for cleanup == Contributors == The following people have contributed mainly to these files (this list is not complete): Co-authored-by: Alex Williams <alex.williams@ni.com> Co-authored-by: Andrej Rode <andrej.rode@ettus.com> Co-authored-by: Ashish Chaudhari <ashish@ettus.com> Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com> Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com> Co-authored-by: Daniel Jepson <daniel.jepson@ni.com> Co-authored-by: Derek Kozel <derek.kozel@ettus.com> Co-authored-by: EJ Kreinar <ej@he360.com> Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com> Co-authored-by: Ian Buckley <ian.buckley@gmail.com> Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com> Co-authored-by: Jon Kiser <jon.kiser@ni.com> Co-authored-by: Josh Blum <josh@joshknows.com> Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com> Co-authored-by: Martin Braun <martin.braun@ettus.com> Co-authored-by: Matt Ettus <matt@ettus.com> Co-authored-by: Michael West <michael.west@ettus.com> Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com> Co-authored-by: Nick Foster <nick@ettus.com> Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com> Co-authored-by: Paul Butler <paul.butler@ni.com> Co-authored-by: Paul David <paul.david@ettus.com> Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com> Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com> Co-authored-by: Sylvain Munaut <tnt@246tNt.com> Co-authored-by: Trung Tran <trung.tran@ettus.com> Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com> Co-authored-by: Wade Fife <wade.fife@ettus.com>
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/blocks')
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/Makefile45
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/Makefile.srcs18
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo.v1228
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_bist.v294
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_bist_regs.v206
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_regs.v207
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_regs.vh228
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/noc_shell_axi_ram_fifo.v319
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo.v485
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_all_tb.sv70
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_tb.sv1114
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/sim_axi_ram.sv637
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/Makefile68
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/Makefile.srcs11
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/noc_shell_ddc.v291
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc.v420
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_regs.vh27
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_tb.sv386
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile67
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile.srcs11
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc.v387
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_regs.vh25
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv387
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/Makefile62
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/Makefile.srcs10
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/noc_shell_fft.v294
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft.v559
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft_tb.sv263
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/Makefile46
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/Makefile.srcs12
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/noc_shell_fir_filter.v297
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter.v343
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter_tb.sv524
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_fir_filter_core.v228
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_fir_filter_regs.vh51
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/Makefile45
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/Makefile.srcs12
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink.v338
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink_tb.sv268
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/Makefile47
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/Makefile.srcs20
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/noc_shell_radio.v290
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/quarter_rate_downconverter.v138
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_core.v370
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_rx_core.v521
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_tx_core.v417
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio.v546
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_all_tb.sv68
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_regs.vh125
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_tb.sv1382
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v246
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/sim_radio_gen.sv104
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/tx_frontend_gen3.v173
53 files changed, 14730 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/Makefile
new file mode 100644
index 000000000..acee50882
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/Makefile
@@ -0,0 +1,45 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../../../top)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
+include Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_BLOCK_AXI_RAM_FIFO_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+SIM_TOP = rfnoc_block_axi_ram_fifo_all_tb
+
+SIM_SRCS = \
+$(abspath sim_axi_ram.sv) \
+$(abspath rfnoc_block_axi_ram_fifo_tb.sv) \
+$(abspath rfnoc_block_axi_ram_fifo_all_tb.sv)
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+# Include all simulator specific makefiles here
+# Each should define a unique target to simulate
+# e.g. xsim, vsim, etc and a common "clean" target
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/Makefile.srcs
new file mode 100644
index 000000000..9faa27321
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/Makefile.srcs
@@ -0,0 +1,18 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+##################################################
+# RFNoC Utility Sources
+##################################################
+RFNOC_BLOCK_AXI_RAM_FIFO_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/, \
+noc_shell_axi_ram_fifo.v \
+axi_ram_fifo_regs.vh \
+axi_ram_fifo_regs.v \
+axi_ram_fifo_bist.v \
+axi_ram_fifo_bist_regs.v \
+axi_ram_fifo.v \
+rfnoc_block_axi_ram_fifo.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo.v
new file mode 100644
index 000000000..5dd5f5ec4
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo.v
@@ -0,0 +1,1228 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi_ram_fifo
+//
+// Description:
+//
+// Implements a FIFO using a memory-mapped AXI interface as storage. This can
+// be connected to any memory-mapped AXI4 bus interface, such as DRAM, SRAM,
+// or AXI interconnect IP. The input and output interfaces to the FIFO are
+// AXI-Stream.
+//
+// The logic is designed to buffer up multiple words so that writes and reads
+// can be implemented as efficient burst transactions on the AXI4 bus. This
+// core never crosses 4 KiB boundaries, per AXI4 rules (a burst must not
+// cross a 4 KiB boundary).
+//
+// The FIFO must be at least 4 KiB in size so that the 4 KiB page boundary
+// protection also handles/prevents the FIFO wrap corner case.
+//
+// Parameters:
+//
+// MEM_ADDR_W : The width of the byte address to use for the AXI4 memory
+// mapped interface.
+//
+// MEM_DATA_W : The width of the data port to use for the AXI4 memory
+// mapped interface.
+//
+// KEEP_W : Width of tkeep on the AXI-Stream interface. Set to 1 if
+// tkeep is not used.
+//
+// FIFO_ADDR_BASE : Default base address to use for this FIFO.
+//
+// FIFO_ADDR_MASK : Default byte address mask, which defines which memory
+// address bits can be used for the FIFO. For example, an 64
+// KiB memory region, or 2^16 bytes, would require the mask
+// 0xFFFF (16 ones). In other words, the mask should be the
+// size of the memory region minus 1.
+//
+// BURST_TIMEOUT : Default number of memory clock cycles to wait for new
+// data before performing a short, sub-optimal burst. One
+// value per FIFO.
+//
+// BIST : If true, BIST logic will be included in the build.
+//
+// CLK_RATE : Frequency of clk in Hz
+//
+// IN_FIFO_SIZE : The input FIFO size will be 2**IN_FIFO_SIZE in depth.
+//
+// OUT_FIFO_SIZE : The output FIFO size will be 2**OUT_FIFO_SIZE in depth.
+// This must be at least 9 so that there is enough space to
+// accept a full AXI4 burst and then accept additional
+// bursts while the FIFO is reading out.
+//
+
+module axi_ram_fifo #(
+ parameter MEM_ADDR_W = 32,
+ parameter MEM_DATA_W = 64,
+ parameter KEEP_W = 1,
+ parameter [MEM_ADDR_W-1:0] FIFO_ADDR_BASE = 'h0,
+ parameter [MEM_ADDR_W-1:0] FIFO_ADDR_MASK = 'h00FFFFFF,
+ parameter BURST_TIMEOUT = 256,
+ parameter BIST = 1,
+ parameter CLK_RATE = 200e6,
+ parameter IN_FIFO_SIZE = 11,
+ parameter OUT_FIFO_SIZE = 10
+) (
+
+ input clk,
+ input rst,
+
+ //--------------------------------------------------------------------------
+ // CTRL Port
+ //--------------------------------------------------------------------------
+
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ output wire s_ctrlport_resp_ack,
+ output wire [31:0] s_ctrlport_resp_data,
+
+ //--------------------------------------------------------------------------
+ // AXI-Stream Interface
+ //--------------------------------------------------------------------------
+
+ // FIFO Input
+ input wire [MEM_DATA_W-1:0] s_tdata,
+ input wire [ KEEP_W-1:0] s_tkeep,
+ input wire s_tlast,
+ input wire s_tvalid,
+ output wire s_tready,
+
+ // FIFO Output
+ output wire [MEM_DATA_W-1:0] m_tdata,
+ output wire [ KEEP_W-1:0] m_tkeep,
+ output wire m_tlast,
+ output wire m_tvalid,
+ input wire m_tready,
+
+ //--------------------------------------------------------------------------
+ // AXI4 Memory Interface
+ //--------------------------------------------------------------------------
+
+ // AXI Write Address Channel
+ output wire [ 0:0] m_axi_awid, // Write address ID. This signal is the identification tag for the write address signals.
+ output wire [ MEM_ADDR_W-1:0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst.
+ output wire [ 7:0] m_axi_awlen, // Burst length. The burst length gives the exact number of transfers in a burst.
+ output wire [ 2:0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
+ output wire [ 1:0] m_axi_awburst, // Burst type. The burst type and the size information, determine how the address is calculated.
+ output wire [ 0:0] m_axi_awlock, // Lock type. Provides additional information about the atomic characteristics of the transfer.
+ output wire [ 3:0] m_axi_awcache, // Memory type. This signal indicates how transactions are required to progress.
+ output wire [ 2:0] m_axi_awprot, // Protection type. This signal indicates the privilege and security level of the transaction.
+ output wire [ 3:0] m_axi_awqos, // Quality of Service, QoS. The QoS identifier sent for each write transaction.
+ output wire [ 3:0] m_axi_awregion, // Region identifier. Permits a single physical interface on a slave to be re-used.
+ output wire [ 0:0] m_axi_awuser, // User signal. Optional User-defined signal in the write address channel.
+ output wire m_axi_awvalid, // Write address valid. This signal indicates that the channel is signaling valid write addr.
+ input wire m_axi_awready, // Write address ready. This signal indicates that the slave is ready to accept an address.
+ //
+ // AXI Write Data Channel
+ output wire [ MEM_DATA_W-1:0] m_axi_wdata, // Write data
+ output wire [MEM_DATA_W/8-1:0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
+ output wire m_axi_wlast, // Write last. This signal indicates the last transfer in a write burst.
+ output wire [ 0:0] m_axi_wuser, // User signal. Optional User-defined signal in the write data channel.
+ output wire m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
+ input wire m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
+ //
+ // AXI Write Response Channel
+ input wire [ 0:0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
+ input wire [ 1:0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
+ input wire [ 0:0] m_axi_buser, // User signal. Optional User-defined signal in the write response channel.
+ input wire m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response.
+ output wire m_axi_bready, // Response ready. This signal indicates that the master can accept a write response.
+ //
+ // AXI Read Address Channel
+ output wire [ 0:0] m_axi_arid, // Read address ID. This signal is the identification tag for the read address group of signals.
+ output wire [ MEM_ADDR_W-1:0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst.
+ output wire [ 7:0] m_axi_arlen, // Burst length. This signal indicates the exact number of transfers in a burst.
+ output wire [ 2:0] m_axi_arsize, // Burst size. This signal indicates the size of each transfer in the burst.
+ output wire [ 1:0] m_axi_arburst, // Burst type. The burst type and the size information determine how the address for each transfer.
+ output wire [ 0:0] m_axi_arlock, // Lock type. This signal provides additional information about the atomic characteristics.
+ output wire [ 3:0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress.
+ output wire [ 2:0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction.
+ output wire [ 3:0] m_axi_arqos, // Quality of Service, QoS. QoS identifier sent for each read transaction.
+ output wire [ 3:0] m_axi_arregion, // Region identifier. Permits a single physical interface on a slave to be re-used.
+ output wire [ 0:0] m_axi_aruser, // User signal. Optional User-defined signal in the read address channel.
+ output wire m_axi_arvalid, // Read address valid. This signal indicates that the channel is signaling valid read addr.
+ input wire m_axi_arready, // Read address ready. This signal indicates that the slave is ready to accept an address.
+ //
+ // AXI Read Data Channel
+ input wire [ 0:0] m_axi_rid, // Read ID tag. This signal is the identification tag for the read data group of signals.
+ input wire [ MEM_DATA_W-1:0] m_axi_rdata, // Read data.
+ input wire [ 1:0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer.
+ input wire m_axi_rlast, // Read last. This signal indicates the last transfer in a read burst.
+ input wire [ 0:0] m_axi_ruser, // User signal. Optional User-defined signal in the read data channel.
+ input wire m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
+ output wire m_axi_rready // Read ready. This signal indicates that the master can accept the read data and response.
+);
+
+ `include "axi_ram_fifo_regs.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Parameter Checking
+ //---------------------------------------------------------------------------
+
+ // The input FIFO size must be at least 9 so that there is enough space to
+ // hold an entire burst and be able to accept new data while that burst is
+ // waiting to be ready out.
+ if (IN_FIFO_SIZE < 9) begin
+ IN_FIFO_SIZE_must_be_at_least_9();
+ end
+
+ // The output FIFO size must be at least 9 so that there is enough space to
+ // accept a full AXI4 burst (255 words) and then accept additional bursts
+ // while the FIFO is waiting to be read out.
+ if (OUT_FIFO_SIZE < 9) begin
+ OUT_FIFO_SIZE_must_be_at_least_9();
+ end
+
+ // The memory must be at least as big as the default FIFO mask
+ if (2.0**MEM_ADDR_W < FIFO_ADDR_MASK+1) begin
+ MEM_ADDR_W_must_be_larger_than_size_indicated_by_FIFO_ADDR_MASK();
+ end
+
+ // The FIFO memory must be large enough for a full AXI4 burst + 64 words
+ // that's allocated to allow for read/write reordering.
+ // TODO: Is the 64-word extra needed? Why 64?
+ //
+ // Min size allowed for memory region in bytes
+ localparam FIFO_MIN_RAM_SIZE = (256+64) * (MEM_DATA_W/8);
+ //
+ // Equivalent mask
+ localparam FIFO_ADDR_MASK_MIN = 2**($clog2(FIFO_MIN_RAM_SIZE))-1;
+ //
+ // Check the parameter
+ if (FIFO_ADDR_MASK < FIFO_ADDR_MASK_MIN) begin
+ FIFO_ADDR_MASK_must_be_at_least_256_plus_64_words();
+ end
+
+ // The 4 KiB page-crossing detection logic assumes that the memory is more
+ // than 4 kiB in size. This could be fixed in the code, but 8 KiB is already
+ // pretty small for an external memory.
+ if (2.0**MEM_ADDR_W < 8192) begin
+ MEM_ADDR_W_must_be_at_least_8_KiB();
+ end
+
+ // Make sure the default burst timeout is not too big for the register
+ if ($clog2(BURST_TIMEOUT+1) > REG_TIMEOUT_W) begin
+ BURST_TIMEOUT_must_not_exceed_the_range_of_REG_TIMEOUT_W();
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Width of the timeout counter
+ localparam TIMEOUT_W = REG_TIMEOUT_W;
+
+ // Address widths. Each memory byte address can be broken up into the word
+ // address portion (the upper bits) and the byte address portion (lower
+ // bits). Although the memory is byte addressable, we only read/write whole
+ // words.
+ localparam BYTE_ADDR_W = $clog2(MEM_DATA_W/8);
+ localparam WORD_ADDR_W = MEM_ADDR_W - BYTE_ADDR_W;
+
+
+ //---------------------------------------------------------------------------
+ // Functions
+ //---------------------------------------------------------------------------
+
+ function automatic integer min(input integer a, b);
+ min = a < b ? a : b;
+ endfunction
+
+
+ //---------------------------------------------------------------------------
+ // Signal Declarations
+ //---------------------------------------------------------------------------
+
+ // Track RAM FIFO state, in number of words
+ reg [WORD_ADDR_W:0] space;
+ reg [WORD_ADDR_W:0] occupied;
+ reg [WORD_ADDR_W:0] occupied_minus_one; // Maintain a -1 version to break critical timing paths
+
+ reg [31:0] out_pkt_count = 0;
+
+ //
+ // Input Side
+ //
+ reg [MEM_DATA_W-1:0] s_tdata_fifo;
+ reg s_tvalid_fifo;
+ wire s_tready_fifo;
+
+ wire [MEM_DATA_W-1:0] m_tdata_fifo;
+ wire m_tvalid_fifo;
+ reg m_tready_fifo;
+
+ wire [MEM_DATA_W-1:0] s_tdata_i1;
+ wire [ KEEP_W-1:0] s_tkeep_i1;
+ wire s_tvalid_i1, s_tready_i1, s_tlast_i1;
+
+ wire [MEM_DATA_W-1:0] s_tdata_i2;
+ wire s_tvalid_i2, s_tready_i2;
+
+ wire [MEM_DATA_W-1:0] s_tdata_i3;
+ wire s_tvalid_i3;
+ reg s_tready_i3;
+
+ wire [MEM_DATA_W-1:0] s_tdata_input;
+ wire s_tvalid_input, s_tready_input;
+
+ wire [15:0] space_input, occupied_input;
+ reg [15:0] space_input_reg;
+ reg suppress_reads;
+
+ //
+ // Output Side
+ //
+ wire [MEM_DATA_W-1:0] m_tdata_output;
+ wire m_tvalid_output, m_tready_output;
+
+ reg [MEM_DATA_W-1:0] m_tdata_i0;
+ reg m_tvalid_i0;
+ wire m_tready_i0;
+
+ wire [MEM_DATA_W-1:0] m_tdata_i1;
+ wire m_tvalid_i1, m_tready_i1;
+
+ wire [MEM_DATA_W-1:0] m_tdata_i2;
+ wire m_tvalid_i2, m_tready_i2;
+
+ wire [MEM_DATA_W-1:0] m_tdata_i3;
+ wire [ KEEP_W-1:0] m_tkeep_i3;
+ wire m_tvalid_i3, m_tready_i3, m_tlast_i3;
+
+ wire [15:0] space_output;
+
+
+ //---------------------------------------------------------------------------
+ // Registers
+ //---------------------------------------------------------------------------
+
+ wire [ 15:0] set_suppress_threshold;
+ wire [ TIMEOUT_W-1:0] set_timeout;
+ wire set_clear = 1'b0; // Clear no longer needed in RFNoC
+ wire [MEM_ADDR_W-1:0] set_fifo_addr_base;
+ wire [MEM_ADDR_W-1:0] set_fifo_addr_mask;
+
+ wire s_ctrlport_resp_ack_regs;
+ wire [31:0] s_ctrlport_resp_data_regs;
+
+ axi_ram_fifo_regs #(
+ .MEM_ADDR_W (MEM_ADDR_W),
+ .MEM_DATA_W (MEM_DATA_W),
+ .FIFO_ADDR_BASE (FIFO_ADDR_BASE),
+ .FIFO_ADDR_MASK (FIFO_ADDR_MASK),
+ .FIFO_ADDR_MASK_MIN (FIFO_ADDR_MASK_MIN),
+ .BIST (BIST),
+ .IN_FIFO_SIZE (IN_FIFO_SIZE),
+ .WORD_ADDR_W (WORD_ADDR_W),
+ .BURST_TIMEOUT (BURST_TIMEOUT),
+ .TIMEOUT_W (TIMEOUT_W)
+ ) axi_ram_fifo_regs_i (
+ .clk (clk),
+ .rst (rst),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr),
+ .s_ctrlport_req_data (s_ctrlport_req_data),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack_regs),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data_regs),
+ .rb_out_pkt_count (out_pkt_count),
+ .rb_occupied (occupied),
+ .set_suppress_threshold (set_suppress_threshold),
+ .set_timeout (set_timeout),
+ .set_fifo_addr_base (set_fifo_addr_base),
+ .set_fifo_addr_mask (set_fifo_addr_mask)
+ );
+
+ //synthesis translate_off
+ // Check the address mask at run-time
+ always @(set_fifo_addr_mask) begin
+ if (set_fifo_addr_mask < FIFO_ADDR_MASK_MIN) begin
+ $display("ERROR: set_fifo_addr_mask was set too small!");
+ end
+ if (2**$clog2(set_fifo_addr_mask)-1 != set_fifo_addr_mask) begin
+ $display("ERROR: set_fifo_addr_mask must be a power of 2, minus 1!");
+ end
+ end
+ //synthesis translate_on
+
+
+ //---------------------------------------------------------------------------
+ // BIST for production testing
+ //---------------------------------------------------------------------------
+
+ if (BIST) begin : gen_bist
+ wire s_ctrlport_resp_ack_bist;
+ wire [ 31:0] s_ctrlport_resp_data_bist;
+ wire [MEM_DATA_W-1:0] m_tdata_bist;
+ wire m_tvalid_bist;
+ reg m_tready_bist;
+ reg [MEM_DATA_W-1:0] s_tdata_bist;
+ reg s_tvalid_bist;
+ wire s_tready_bist;
+
+ wire bist_running;
+
+ axi_ram_fifo_bist #(
+ .DATA_W (MEM_DATA_W),
+ .COUNT_W (48),
+ .CLK_RATE (CLK_RATE),
+ .RAND (1)
+ ) axi_ram_fifo_bist_i (
+ .clk (clk),
+ .rst (rst),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr),
+ .s_ctrlport_req_data (s_ctrlport_req_data),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack_bist),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data_bist),
+ .m_tdata (m_tdata_bist),
+ .m_tvalid (m_tvalid_bist),
+ .m_tready (m_tready_bist),
+ .s_tdata (s_tdata_bist),
+ .s_tvalid (s_tvalid_bist),
+ .s_tready (s_tready_bist),
+ .running (bist_running)
+ );
+
+ // Use a multiplexer to decide where the data flows, using the BIST when
+ // ever the BIST is running.
+ always @(*) begin
+ if (bist_running) begin
+ // Insert the BIST logic
+ s_tdata_fifo = m_tdata_bist;
+ s_tvalid_fifo = m_tvalid_bist;
+ m_tready_bist = s_tready_fifo;
+ //
+ s_tdata_bist = m_tdata_fifo;
+ s_tvalid_bist = m_tvalid_fifo;
+ m_tready_fifo = s_tready_bist;
+
+ // Disable output-logic
+ s_tready_i3 = 0;
+ m_tdata_i0 = m_tdata_fifo;
+ m_tvalid_i0 = 0;
+ end else begin
+ // Disable BIST
+ m_tready_bist = 0;
+ s_tdata_bist = m_tdata_fifo;
+ s_tvalid_bist = 0;
+
+ // Bypass BIST
+ s_tdata_fifo = s_tdata_i3;
+ s_tvalid_fifo = s_tvalid_i3;
+ s_tready_i3 = s_tready_fifo;
+ //
+ m_tdata_i0 = m_tdata_fifo;
+ m_tvalid_i0 = m_tvalid_fifo;
+ m_tready_fifo = m_tready_i0;
+ end
+ end
+
+ // Combine register responses
+ ctrlport_resp_combine #(
+ .NUM_SLAVES (2)
+ ) ctrlport_resp_combine_i (
+ .ctrlport_clk (clk),
+ .ctrlport_rst (rst),
+ .m_ctrlport_resp_ack ({s_ctrlport_resp_ack_bist, s_ctrlport_resp_ack_regs}),
+ .m_ctrlport_resp_status ({2{2'b00}}),
+ .m_ctrlport_resp_data ({s_ctrlport_resp_data_bist, s_ctrlport_resp_data_regs}),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data)
+ );
+
+ end else begin : gen_no_bist
+ assign s_ctrlport_resp_ack = s_ctrlport_resp_ack_regs;
+ assign s_ctrlport_resp_data = s_ctrlport_resp_data_regs;
+ always @(*) begin
+ // Bypass the BIST logic
+ s_tdata_fifo = s_tdata_i3;
+ s_tvalid_fifo = s_tvalid_i3;
+ s_tready_i3 = s_tready_fifo;
+ //
+ m_tdata_i0 = m_tdata_fifo;
+ m_tvalid_i0 = m_tvalid_fifo;
+ m_tready_fifo = m_tready_i0;
+ //
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Input Handling and Buffer
+ //---------------------------------------------------------------------------
+ //
+ // This block embeds TLAST into the data stream using an escape code and
+ // buffers up input data.
+ //
+ //---------------------------------------------------------------------------
+
+ // Insert flops to improve timing
+ axi_fifo_flop2 #(
+ .WIDTH (MEM_DATA_W+1+KEEP_W)
+ ) input_pipe_i0 (
+ .clk (clk),
+ .reset (rst),
+ .clear (set_clear),
+ //
+ .i_tdata ({s_tkeep, s_tlast, s_tdata}),
+ .i_tvalid (s_tvalid),
+ .i_tready (s_tready),
+ //
+ .o_tdata ({s_tkeep_i1, s_tlast_i1, s_tdata_i1}),
+ .o_tvalid (s_tvalid_i1),
+ .o_tready (s_tready_i1),
+ //
+ .space (),
+ .occupied ()
+ );
+
+ axi_embed_tlast_tkeep #(
+ .DATA_W (MEM_DATA_W),
+ .KEEP_W (KEEP_W)
+ ) axi_embed_tlast_tkeep_i (
+ .clk (clk),
+ .rst (rst | set_clear),
+ //
+ .i_tdata (s_tdata_i1),
+ .i_tkeep (s_tkeep_i1),
+ .i_tlast (s_tlast_i1),
+ .i_tvalid (s_tvalid_i1),
+ .i_tready (s_tready_i1),
+ //
+ .o_tdata (s_tdata_i2),
+ .o_tvalid (s_tvalid_i2),
+ .o_tready (s_tready_i2)
+ );
+
+ // Insert flops to improve timing
+ axi_fifo_flop2 #(
+ .WIDTH (MEM_DATA_W)
+ ) input_pipe_i1 (
+ .clk (clk),
+ .reset (rst),
+ .clear (set_clear),
+ //
+ .i_tdata (s_tdata_i2),
+ .i_tvalid (s_tvalid_i2),
+ .i_tready (s_tready_i2),
+ //
+ .o_tdata (s_tdata_i3),
+ .o_tvalid (s_tvalid_i3),
+ .o_tready (s_tready_i3),
+ //
+ .space (),
+ .occupied ()
+ );
+
+ axi_fifo #(
+ .WIDTH (MEM_DATA_W),
+ .SIZE (IN_FIFO_SIZE)
+ ) input_fifo (
+ .clk (clk),
+ .reset (rst),
+ .clear (set_clear),
+ //
+ .i_tdata (s_tdata_fifo),
+ .i_tvalid (s_tvalid_fifo),
+ .i_tready (s_tready_fifo),
+ //
+ .o_tdata (s_tdata_input),
+ .o_tvalid (s_tvalid_input),
+ .o_tready (s_tready_input),
+ //
+ .space (space_input),
+ .occupied (occupied_input)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Input (Memory Write) Logic
+ //---------------------------------------------------------------------------
+ //
+ // The input state machine waits for enough entries in input FIFO to trigger
+ // RAM write burst. A timeout can also trigger a burst so that smaller chunks
+ // of data are not left to rot in the input FIFO. Also, if enough data is
+ // present in the input FIFO to complete a burst up to the edge of a 4 KiB
+ // page then we do a burst up to the 4 KiB boundary.
+ //
+ //---------------------------------------------------------------------------
+
+ //
+ // Input side declarations
+ //
+ localparam [2:0] INPUT_IDLE = 0;
+ localparam [2:0] INPUT1 = 1;
+ localparam [2:0] INPUT2 = 2;
+ localparam [2:0] INPUT3 = 3;
+ localparam [2:0] INPUT4 = 4;
+ localparam [2:0] INPUT5 = 5;
+ localparam [2:0] INPUT6 = 6;
+
+ wire write_ctrl_ready;
+
+ reg [ 2:0] input_state;
+ reg input_timeout_triggered;
+ reg input_timeout_reset;
+ reg [ TIMEOUT_W-1:0] input_timeout_count;
+ reg [MEM_ADDR_W-1:0] write_addr;
+ reg write_ctrl_valid;
+ reg [ 7:0] write_count = 0;
+ reg [ 8:0] write_count_plus_one = 1; // Maintain a +1 version to break critical timing paths
+ reg update_write;
+
+ reg [WORD_ADDR_W-1:0] input_page_boundary;
+
+ //
+ // Input timeout counter. Timeout count only increments when there is some
+ // data waiting to be written to the RAM.
+ //
+ always @(posedge clk) begin
+ if (rst | set_clear) begin
+ input_timeout_count <= 0;
+ input_timeout_triggered <= 0;
+ end else if (input_timeout_reset) begin
+ input_timeout_count <= 0;
+ input_timeout_triggered <= 0;
+ end else if (input_timeout_count == set_timeout) begin
+ input_timeout_triggered <= 1;
+ end else if (input_state == INPUT_IDLE) begin
+ input_timeout_count <= input_timeout_count + ((occupied_input != 0) ? 1 : 0);
+ end
+ end
+
+ //
+ // Input State Machine
+ //
+ always @(posedge clk)
+ if (rst | set_clear) begin
+ input_state <= INPUT_IDLE;
+ write_addr <= set_fifo_addr_base & ~set_fifo_addr_mask;
+ input_timeout_reset <= 1'b0;
+ write_ctrl_valid <= 1'b0;
+ write_count <= 8'd0;
+ write_count_plus_one <= 9'd1;
+ update_write <= 1'b0;
+ end else begin
+ case (input_state)
+ //
+ // INPUT_IDLE.
+ // To start an input transfer to DRAM need:
+ // 1) Space in the RAM
+ // and either
+ // 2) 256 entries in the input FIFO
+ // or
+ // 3) Timeout occurred while waiting for more data, which can only happen
+ // if there's at least one word in the input FIFO).
+ //
+ INPUT_IDLE: begin
+ write_ctrl_valid <= 1'b0;
+ update_write <= 1'b0;
+ input_timeout_reset <= 1'b0;
+ if (space[WORD_ADDR_W:8] != 'd0) begin // (space > 255): 256 or more slots in the RAM
+ if (occupied_input[15:8] != 'd0) begin // (occupied_input > 255): 256 or more words in input FIFO
+ input_state <= INPUT1;
+ input_timeout_reset <= 1'b1;
+
+ // Calculate the number of entries remaining until next 4 KiB page
+ // boundary is crossed, minus 1. The units of calculation are
+ // words. The address is always word aligned.
+ input_page_boundary <= { write_addr[MEM_ADDR_W-1:12], {12-BYTE_ADDR_W{1'b1}} } -
+ write_addr[MEM_ADDR_W-1 : BYTE_ADDR_W];
+ end else if (input_timeout_triggered) begin // input FIFO timeout waiting for new data.
+ input_state <= INPUT2;
+ input_timeout_reset <= 1'b1;
+ // Calculate the number of entries remaining until next 4 KiB page
+ // boundary is crossed, minus 1. The units of calculation are
+ // words. The address is always word-aligned.
+ input_page_boundary <= { write_addr[MEM_ADDR_W-1:12], {12-BYTE_ADDR_W{1'b1}} } -
+ write_addr[MEM_ADDR_W-1 : BYTE_ADDR_W];
+ end
+ end
+ end
+ //
+ // INPUT1.
+ // Caused by input FIFO reaching 256 entries.
+ // Request write burst of lesser of:
+ // 1) Entries until page boundary crossed
+ // 2) 256
+ //
+ INPUT1: begin
+ // Replicated write logic to break a read timing critical path for
+ // write_count.
+ write_count <= input_page_boundary[min(12, WORD_ADDR_W)-1:8] == 0 ?
+ input_page_boundary[7:0] :
+ 255;
+ write_count_plus_one <= input_page_boundary[min(12, WORD_ADDR_W)-1:8] == 0 ?
+ input_page_boundary[7:0] + 1 :
+ 256;
+ write_ctrl_valid <= 1'b1;
+ if (write_ctrl_ready)
+ input_state <= INPUT4; // Preemptive ACK
+ else
+ input_state <= INPUT3; // Wait for ACK
+ end
+ //
+ // INPUT2.
+ // Caused by timeout of input FIFO (occupied_input must now be 256 or
+ // less since it was 255 or less in the INPUT_IDLE state; otherwise we
+ // would have gone to INPUT1). Request write burst of lesser of:
+ // 1) Entries until page boundary crossed
+ // 2) Entries in input FIFO
+ //
+ INPUT2: begin
+ // Replicated write logic to break a read timing critical path for
+ // write_count.
+ write_count <= input_page_boundary < occupied_input[8:0] - 1 ?
+ input_page_boundary[7:0] :
+ occupied_input[8:0] - 1; // Max result of 255
+ write_count_plus_one <= input_page_boundary < occupied_input[8:0] - 1 ?
+ input_page_boundary[7:0] + 1 :
+ occupied_input[8:0];
+ write_ctrl_valid <= 1'b1;
+ if (write_ctrl_ready)
+ input_state <= INPUT4; // Preemptive ACK
+ else
+ input_state <= INPUT3; // Wait for ACK
+ end
+ //
+ // INPUT3.
+ // Wait in this state for AXI4 DMA engine to accept transaction.
+ //
+ INPUT3: begin
+ if (write_ctrl_ready) begin
+ write_ctrl_valid <= 1'b0;
+ input_state <= INPUT4; // ACK
+ end else begin
+ write_ctrl_valid <= 1'b1;
+ input_state <= INPUT3; // Wait for ACK
+ end
+ end
+ //
+ // INPUT4.
+ // Wait here until write_ctrl_ready_deasserts. This is important as the
+ // next time it asserts we know that a write response was received.
+ INPUT4: begin
+ write_ctrl_valid <= 1'b0;
+ if (!write_ctrl_ready)
+ input_state <= INPUT5; // Move on
+ else
+ input_state <= INPUT4; // Wait for deassert
+ end
+ //
+ // INPUT5.
+ // Transaction has been accepted by AXI4 DMA engine. Now we wait for the
+ // re-assertion of write_ctrl_ready which signals that the AXI4 DMA
+ // engine has received a response for the whole write transaction and we
+ // assume that this means it is committed to DRAM. We are now free to
+ // update write_addr pointer and go back to idle state.
+ //
+ INPUT5: begin
+ write_ctrl_valid <= 1'b0;
+ if (write_ctrl_ready) begin
+ write_addr <= ((write_addr + (write_count_plus_one << $clog2(MEM_DATA_W/8))) & set_fifo_addr_mask) | (write_addr & ~set_fifo_addr_mask);
+ input_state <= INPUT6;
+ update_write <= 1'b1;
+ end else begin
+ input_state <= INPUT5;
+ end
+ end
+ //
+ // INPUT6:
+ // Need to let space update before looking if there's more to do.
+ //
+ INPUT6: begin
+ input_state <= INPUT_IDLE;
+ update_write <= 1'b0;
+ end
+
+ default:
+ input_state <= INPUT_IDLE;
+ endcase // case(input_state)
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Read Suppression Logic
+ //---------------------------------------------------------------------------
+ //
+ // Monitor occupied_input to deduce when DRAM FIFO is running short of
+ // bandwidth and there is a danger of back-pressure passing upstream of the
+ // DRAM FIFO. In this situation, we suppress read requests to the DRAM FIFO
+ // so that more bandwidth is available to writes.
+ //
+ // However, not reading can actually cause the FIFO to fill up and stall, so
+ // if the input is stalled, allow switching back to reads. This allows the
+ // memory to fill up without causing deadlock.
+ //
+ //---------------------------------------------------------------------------
+
+ reg input_idle, input_idle_d1, input_stalled;
+
+ always @(posedge clk) begin
+ // We consider the input to be stalled when the input state machine is idle
+ // for 2 or more clock cycles.
+ input_idle <= (input_state == INPUT_IDLE);
+ input_idle_d1 <= input_idle;
+ input_stalled <= input_idle && input_idle_d1;
+
+ space_input_reg <= space_input;
+ if (space_input_reg < set_suppress_threshold && !input_stalled)
+ suppress_reads <= 1'b1;
+ else
+ suppress_reads <= 1'b0;
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Output Handling and Buffer
+ //---------------------------------------------------------------------------
+ //
+ // This block buffers output data and extracts the TLAS signal that was
+ // embedded into the data stream.
+ //
+ //---------------------------------------------------------------------------
+
+ // Large FIFO to buffer data read from DRAM. This FIFO must be large enough
+ // to accept a full burst read.
+ axi_fifo #(
+ .WIDTH (MEM_DATA_W),
+ .SIZE (OUT_FIFO_SIZE)
+ ) output_fifo (
+ .clk (clk),
+ .reset (rst),
+ .clear (set_clear),
+ //
+ .i_tdata (m_tdata_output),
+ .i_tvalid (m_tvalid_output),
+ .i_tready (m_tready_output),
+ //
+ .o_tdata (m_tdata_fifo),
+ .o_tvalid (m_tvalid_fifo),
+ .o_tready (m_tready_fifo),
+ //
+ .space (space_output),
+ .occupied ()
+ );
+
+ // Place flops right after FIFO to improve timing
+ axi_fifo_flop2 #(
+ .WIDTH (MEM_DATA_W)
+ ) output_pipe_i0 (
+ .clk (clk),
+ .reset (rst),
+ .clear (set_clear),
+ //
+ .i_tdata (m_tdata_i0),
+ .i_tvalid (m_tvalid_i0),
+ .i_tready (m_tready_i0),
+ //
+ .o_tdata (m_tdata_i1),
+ .o_tvalid (m_tvalid_i1),
+ .o_tready (m_tready_i1),
+ //
+ .space (),
+ .occupied ()
+ );
+
+ // Pipeline flop before TLAST extraction logic
+ axi_fifo_flop2 #(
+ .WIDTH (MEM_DATA_W)
+ ) output_pipe_i1 (
+ .clk (clk),
+ .reset (rst),
+ .clear (set_clear),
+ //
+ .i_tdata (m_tdata_i1),
+ .i_tvalid (m_tvalid_i1),
+ .i_tready (m_tready_i1),
+ //
+ .o_tdata (m_tdata_i2),
+ .o_tvalid (m_tvalid_i2),
+ .o_tready (m_tready_i2),
+ //
+ .space (),
+ .occupied ()
+ );
+
+ axi_extract_tlast_tkeep #(
+ .DATA_W (MEM_DATA_W),
+ .KEEP_W (KEEP_W)
+ ) axi_extract_tlast_tkeep_i (
+ .clk (clk),
+ .rst (rst | set_clear),
+ //
+ .i_tdata (m_tdata_i2),
+ .i_tvalid (m_tvalid_i2),
+ .i_tready (m_tready_i2),
+ //
+ .o_tdata (m_tdata_i3),
+ .o_tkeep (m_tkeep_i3),
+ .o_tlast (m_tlast_i3),
+ .o_tvalid (m_tvalid_i3),
+ .o_tready (m_tready_i3)
+ );
+
+ // Pipeline flop after TLAST extraction logic
+ axi_fifo_flop2 #(
+ .WIDTH (MEM_DATA_W+1+KEEP_W)
+ ) output_pipe_i3 (
+ .clk (clk),
+ .reset (rst),
+ .clear (set_clear),
+ //
+ .i_tdata ({m_tkeep_i3, m_tlast_i3, m_tdata_i3}),
+ .i_tvalid (m_tvalid_i3),
+ .i_tready (m_tready_i3),
+ //
+ .o_tdata ({m_tkeep, m_tlast, m_tdata}),
+ .o_tvalid (m_tvalid),
+ .o_tready (m_tready),
+ //
+ .space (),
+ .occupied ()
+ );
+
+
+ //-------------------------------------------------------------------------
+ // Output (Memory Read) Logic
+ //-------------------------------------------------------------------------
+ //
+ // The output state machine Wait for enough entries in RAM to trigger read
+ // burst. A timeout can also trigger a burst so that smaller chunks of data
+ // are not left to rot in the RAM. Also, if enough data is present in the RAM
+ // to complete a burst up to the edge of a 4 KiB page boundary then we do a
+ // burst up to the 4 KiB boundary.
+ //
+ //---------------------------------------------------------------------------
+
+ //
+ // Output side declarations
+ //
+ localparam [2:0] OUTPUT_IDLE = 0;
+ localparam [2:0] OUTPUT1 = 1;
+ localparam [2:0] OUTPUT2 = 2;
+ localparam [2:0] OUTPUT3 = 3;
+ localparam [2:0] OUTPUT4 = 4;
+ localparam [2:0] OUTPUT5 = 5;
+ localparam [2:0] OUTPUT6 = 6;
+
+ reg [ 2:0] output_state;
+ reg output_timeout_triggered;
+ reg output_timeout_reset;
+ reg [ TIMEOUT_W-1:0] output_timeout_count;
+ reg [MEM_ADDR_W-1:0] read_addr;
+ reg read_ctrl_valid;
+ wire read_ctrl_ready;
+ reg [ 7:0] read_count = 0;
+ reg [ 8:0] read_count_plus_one = 1; // Maintain a +1 version to break critical timing paths
+ reg update_read;
+
+ reg [WORD_ADDR_W-1:0] output_page_boundary; // Cache in a register to break critical timing paths
+
+ //
+ // Output Packet Counter
+ //
+ always @(posedge clk) begin
+ if (rst) begin
+ out_pkt_count <= 0;
+ end else if (m_tlast & m_tvalid & m_tready) begin
+ out_pkt_count <= out_pkt_count + 1;
+ end
+ end
+
+ //
+ // Output timeout counter. Timeout count only increments when there is some
+ // data waiting to be read from the RAM.
+ //
+ always @(posedge clk) begin
+ if (rst | set_clear) begin
+ output_timeout_count <= 0;
+ output_timeout_triggered <= 0;
+ end else if (output_timeout_reset) begin
+ output_timeout_count <= 0;
+ output_timeout_triggered <= 0;
+ end else if (output_timeout_count == set_timeout) begin
+ output_timeout_triggered <= 1;
+ end else if (output_state == OUTPUT_IDLE) begin
+ output_timeout_count <= output_timeout_count + ((occupied != 0) ? 1 : 0);
+ end
+ end
+
+ //
+ // Output State Machine
+ //
+ always @(posedge clk)
+ if (rst | set_clear) begin
+ output_state <= OUTPUT_IDLE;
+ read_addr <= set_fifo_addr_base & ~set_fifo_addr_mask;
+ output_timeout_reset <= 1'b0;
+ read_ctrl_valid <= 1'b0;
+ read_count <= 8'd0;
+ read_count_plus_one <= 9'd1;
+ update_read <= 1'b0;
+ end else begin
+ case (output_state)
+ //
+ // OUTPUT_IDLE.
+ // To start an output transfer from DRAM
+ // 1) Space in the output FIFO
+ // and either
+ // 2) 256 entries in the RAM
+ // or
+ // 3) Timeout occurred while waiting for more data, which can only happen
+ // if there's at least one word in the RAM.
+ //
+ OUTPUT_IDLE: begin
+ read_ctrl_valid <= 1'b0;
+ update_read <= 1'b0;
+ output_timeout_reset <= 1'b0;
+ if (space_output[15:8] != 'd0 && !suppress_reads) begin // (space_output > 255): 256 or more words in the output FIFO
+ if (occupied[WORD_ADDR_W:8] != 'd0) begin // (occupied > 255): 256 or more words in RAM
+ output_state <= OUTPUT1;
+ output_timeout_reset <= 1'b1;
+
+ // Calculate the number of entries remaining until next 4 KiB page
+ // boundary is crossed, minus 1. The units of calculation are
+ // words. The address is always word-aligned.
+ output_page_boundary <= { read_addr[MEM_ADDR_W-1:12], {12-BYTE_ADDR_W{1'b1}} } -
+ read_addr[MEM_ADDR_W-1 : BYTE_ADDR_W];
+ end else if (output_timeout_triggered) begin // output FIFO timeout waiting for new data.
+ output_state <= OUTPUT2;
+ output_timeout_reset <= 1'b1;
+ // Calculate the number of entries remaining until next 4 KiB page
+ // boundary is crossed, minus 1. The units of calculation are
+ // words. The address is always word-aligned.
+ output_page_boundary <= { read_addr[MEM_ADDR_W-1:12], {12-BYTE_ADDR_W{1'b1}} } -
+ read_addr[MEM_ADDR_W-1 : BYTE_ADDR_W];
+ end
+ end
+ end
+ //
+ // OUTPUT1.
+ // Caused by RAM FIFO reaching 256 entries.
+ // Request read burst of lesser of lesser of:
+ // 1) Entries until page boundary crossed
+ // 2) 256
+ //
+ OUTPUT1: begin
+ // Replicated write logic to break a read timing critical path for read_count
+ read_count <= output_page_boundary[min(12, WORD_ADDR_W)-1:8] == 0 ?
+ output_page_boundary[7:0] :
+ 255;
+ read_count_plus_one <= output_page_boundary[min(12, WORD_ADDR_W)-1:8] == 0 ?
+ output_page_boundary[7:0] + 1 :
+ 256;
+ read_ctrl_valid <= 1'b1;
+ if (read_ctrl_ready)
+ output_state <= OUTPUT4; // Preemptive ACK
+ else
+ output_state <= OUTPUT3; // Wait for ACK
+ end
+ //
+ // OUTPUT2.
+ // Caused by timeout of main FIFO
+ // Request read burst of lesser of:
+ // 1) Entries until page boundary crossed
+ // 2) Entries in main FIFO
+ //
+ OUTPUT2: begin
+ // Replicated write logic to break a read timing critical path for read_count
+ read_count <= output_page_boundary < occupied_minus_one ?
+ output_page_boundary[7:0] :
+ occupied_minus_one[7:0];
+ read_count_plus_one <= output_page_boundary < occupied_minus_one ?
+ output_page_boundary[7:0] + 1 :
+ occupied[7:0];
+ read_ctrl_valid <= 1'b1;
+ if (read_ctrl_ready)
+ output_state <= OUTPUT4; // Preemptive ACK
+ else
+ output_state <= OUTPUT3; // Wait for ACK
+ end
+ //
+ // OUTPUT3.
+ // Wait in this state for AXI4 DMA engine to accept transaction.
+ //
+ OUTPUT3: begin
+ if (read_ctrl_ready) begin
+ read_ctrl_valid <= 1'b0;
+ output_state <= OUTPUT4; // ACK
+ end else begin
+ read_ctrl_valid <= 1'b1;
+ output_state <= OUTPUT3; // Wait for ACK
+ end
+ end
+ //
+ // OUTPUT4.
+ // Wait here until read_ctrl_ready_deasserts. This is important as the
+ // next time it asserts we know that a read response was received.
+ OUTPUT4: begin
+ read_ctrl_valid <= 1'b0;
+ if (!read_ctrl_ready)
+ output_state <= OUTPUT5; // Move on
+ else
+ output_state <= OUTPUT4; // Wait for deassert
+ end
+ //
+ // OUTPUT5.
+ // Transaction has been accepted by AXI4 DMA engine. Now we wait for the
+ // re-assertion of read_ctrl_ready which signals that the AXI4 DMA engine
+ // has received a last signal and good response for the whole read
+ // transaction. We are now free to update read_addr pointer and go back
+ // to idle state.
+ //
+ OUTPUT5: begin
+ read_ctrl_valid <= 1'b0;
+ if (read_ctrl_ready) begin
+ read_addr <= ((read_addr + (read_count_plus_one << $clog2(MEM_DATA_W/8))) & set_fifo_addr_mask) | (read_addr & ~set_fifo_addr_mask);
+ output_state <= OUTPUT6;
+ update_read <= 1'b1;
+ end else begin
+ output_state <= OUTPUT5;
+ end
+ end
+ //
+ // OUTPUT6.
+ // Need to get occupied value updated before checking if there's more to do.
+ //
+ OUTPUT6: begin
+ update_read <= 1'b0;
+ output_state <= OUTPUT_IDLE;
+ end
+
+ default:
+ output_state <= OUTPUT_IDLE;
+ endcase // case(output_state)
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Shared Read/Write Logic
+ //---------------------------------------------------------------------------
+
+ //
+ // Count number of words stored in the RAM FIFO.
+ //
+ always @(posedge clk) begin
+ if (rst | set_clear) begin
+ occupied <= 0;
+ occupied_minus_one <= -1;
+ end else begin
+ occupied <= occupied + (update_write ? write_count_plus_one : 0) - (update_read ? read_count_plus_one : 0);
+ occupied_minus_one <= occupied_minus_one + (update_write ? write_count_plus_one : 0) - (update_read ? read_count_plus_one : 0);
+ end
+ end
+
+ //
+ // Count amount of space in the RAM FIFO, in words.
+ //
+ always @(posedge clk) begin
+ if (rst | set_clear) begin
+ // Set to the FIFO size minus 64 words to make allowance for read/write
+ // reordering in DRAM controller.
+ // TODO: Is the 64-word extra needed? Why 64?
+ space <= set_fifo_addr_mask[MEM_ADDR_W-1 -: WORD_ADDR_W] & ~('d63);
+ end else begin
+ space <= space - (update_write ? write_count_plus_one : 0) + (update_read ? read_count_plus_one : 0);
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // AXI4 DMA Master
+ //---------------------------------------------------------------------------
+
+ axi_dma_master #(
+ .AWIDTH (MEM_ADDR_W),
+ .DWIDTH (MEM_DATA_W)
+ ) axi_dma_master_i (
+ .aclk (clk),
+ .areset (rst | set_clear),
+ // Write Address
+ .m_axi_awid (m_axi_awid),
+ .m_axi_awaddr (m_axi_awaddr),
+ .m_axi_awlen (m_axi_awlen),
+ .m_axi_awsize (m_axi_awsize),
+ .m_axi_awburst (m_axi_awburst),
+ .m_axi_awvalid (m_axi_awvalid),
+ .m_axi_awready (m_axi_awready),
+ .m_axi_awlock (m_axi_awlock),
+ .m_axi_awcache (m_axi_awcache),
+ .m_axi_awprot (m_axi_awprot),
+ .m_axi_awqos (m_axi_awqos),
+ .m_axi_awregion (m_axi_awregion),
+ .m_axi_awuser (m_axi_awuser),
+ // Write Data
+ .m_axi_wdata (m_axi_wdata),
+ .m_axi_wstrb (m_axi_wstrb),
+ .m_axi_wlast (m_axi_wlast),
+ .m_axi_wvalid (m_axi_wvalid),
+ .m_axi_wready (m_axi_wready),
+ .m_axi_wuser (m_axi_wuser),
+ // Write Response
+ .m_axi_bid (m_axi_bid),
+ .m_axi_bresp (m_axi_bresp),
+ .m_axi_bvalid (m_axi_bvalid),
+ .m_axi_bready (m_axi_bready),
+ .m_axi_buser (m_axi_buser),
+ // Read Address
+ .m_axi_arid (m_axi_arid),
+ .m_axi_araddr (m_axi_araddr),
+ .m_axi_arlen (m_axi_arlen),
+ .m_axi_arsize (m_axi_arsize),
+ .m_axi_arburst (m_axi_arburst),
+ .m_axi_arvalid (m_axi_arvalid),
+ .m_axi_arready (m_axi_arready),
+ .m_axi_arlock (m_axi_arlock),
+ .m_axi_arcache (m_axi_arcache),
+ .m_axi_arprot (m_axi_arprot),
+ .m_axi_arqos (m_axi_arqos),
+ .m_axi_arregion (m_axi_arregion),
+ .m_axi_aruser (m_axi_aruser),
+ // Read Data
+ .m_axi_rid (m_axi_rid),
+ .m_axi_rdata (m_axi_rdata),
+ .m_axi_rresp (m_axi_rresp),
+ .m_axi_rlast (m_axi_rlast),
+ .m_axi_rvalid (m_axi_rvalid),
+ .m_axi_rready (m_axi_rready),
+ .m_axi_ruser (m_axi_ruser),
+ //
+ // DMA interface for Write transactions
+ //
+ .write_addr (write_addr), // Byte address for start of write transaction (should be 64-bit aligned)
+ .write_count (write_count), // Count of 64-bit words to write.
+ .write_ctrl_valid (write_ctrl_valid),
+ .write_ctrl_ready (write_ctrl_ready),
+ .write_data (s_tdata_input),
+ .write_data_valid (s_tvalid_input),
+ .write_data_ready (s_tready_input),
+ //
+ // DMA interface for Read transactions
+ //
+ .read_addr (read_addr), // Byte address for start of read transaction (should be 64-bit aligned)
+ .read_count (read_count), // Count of 64-bit words to read.
+ .read_ctrl_valid (read_ctrl_valid),
+ .read_ctrl_ready (read_ctrl_ready),
+ .read_data (m_tdata_output),
+ .read_data_valid (m_tvalid_output),
+ .read_data_ready (m_tready_output),
+ //
+ // Debug
+ //
+ .debug ()
+ );
+
+endmodule
+
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_bist.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_bist.v
new file mode 100644
index 000000000..2dd3f99d3
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_bist.v
@@ -0,0 +1,294 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi_ram_fifo_bist
+//
+// Description:
+//
+// Implements a built-in self test for the RAM FIFO. It can generate random
+// or sequential data that it outputs as quickly as possible. The output of
+// the RAM is verified to make sure that it matches what was input to the RAM.
+//
+// Parameters:
+//
+// DATA_W : The width of the data port to use for the AXI4-Stream interface
+//
+// COUNT_W : Width of internal counters. This must be wide enough so that
+// word, cycle, and and error counters don't overflow during a
+// test.
+//
+// CLK_RATE : The frequency of clk in Hz
+//
+// RAND : Set to 1 for random data, 0 for sequential data.
+//
+
+module axi_ram_fifo_bist #(
+ parameter DATA_W = 64,
+ parameter COUNT_W = 48,
+ parameter CLK_RATE = 200e6,
+ parameter RAND = 1
+) (
+ input clk,
+ input rst,
+
+ //--------------------------------------------------------------------------
+ // CTRL Port
+ //--------------------------------------------------------------------------
+
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ output wire s_ctrlport_resp_ack,
+ output wire [31:0] s_ctrlport_resp_data,
+
+ //--------------------------------------------------------------------------
+ // AXI-Stream Interface
+ //--------------------------------------------------------------------------
+
+ // Output to RAM FIFO
+ output wire [DATA_W-1:0] m_tdata,
+ output reg m_tvalid,
+ input wire m_tready,
+
+ // Input from RAM FIFO
+ input wire [DATA_W-1:0] s_tdata,
+ input wire s_tvalid,
+ output wire s_tready,
+
+ //---------------------------------------------------------------------------
+ // Status
+ //---------------------------------------------------------------------------
+
+ output reg running
+
+);
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Internal word size to use for data generation. The output word will be a
+ // multiple of this size.
+ localparam WORD_W = 32;
+
+ // Random number seed (must not be 0)
+ localparam [WORD_W-1:0] SEED = 'h012345678;
+
+ // Test data reset value
+ localparam [WORD_W-1:0] INIT = RAND ? SEED : 0;
+
+
+ //---------------------------------------------------------------------------
+ // Assertions
+ //---------------------------------------------------------------------------
+
+ if (DATA_W % WORD_W != 0) begin
+ DATA_W_must_be_a_multiple_of_WORD_W();
+ end
+
+ // LFSR only supports 8, 16, and 32 bits
+ if (WORD_W != 32 && WORD_W != 16 && WORD_W != 8) begin
+ WORD_W_not_supported();
+ end
+
+ //---------------------------------------------------------------------------
+ // Functions
+ //---------------------------------------------------------------------------
+
+ // Linear-feedback Shift Register for random number generation.
+ function [WORD_W-1:0] lfsr(input [WORD_W-1:0] din);
+ reg new_bit;
+ begin
+ case (WORD_W)
+ 8 : new_bit = din[7] ^ din[5] ^ din[4] ^ din[3];
+ 16 : new_bit = din[15] ^ din[14] ^ din[12] ^ din[3];
+ 32 : new_bit = din[31] ^ din[21] ^ din[1] ^ din[0];
+ endcase
+ lfsr = { din[WORD_W-2:0], new_bit };
+ end
+ endfunction
+
+ function [WORD_W-1:0] next(input [WORD_W-1:0] din);
+ next = RAND ? lfsr(din) : din + 1;
+ endfunction
+
+
+ //---------------------------------------------------------------------------
+ // Signal Declarations
+ //---------------------------------------------------------------------------
+
+ reg [COUNT_W-1:0] tx_count; // Number of words transmitted to FIFO
+ reg [COUNT_W-1:0] rx_count; // Number of words received back from FIFO
+ reg [COUNT_W-1:0] error_count; // Number of words that show errors
+
+ reg [WORD_W-1:0] tx_data = next(INIT); // Transmitted data word
+ reg [DATA_W-1:0] rx_data = INIT; // Received data words
+ reg [WORD_W-1:0] exp_data; // Expected data word
+ reg rx_valid; // Received word is value (strobe)
+
+ wire [COUNT_W-1:0] num_words; // Number of words to test
+ reg [COUNT_W-1:0] cycle_count; // Number of clock cycles test has been running for
+ wire start; // Start test
+ wire stop; // Stop test
+ wire clear; // Clear the counters
+ wire continuous; // Continuous test mode
+
+
+ //---------------------------------------------------------------------------
+ // Registers
+ //---------------------------------------------------------------------------
+
+ axi_ram_fifo_bist_regs #(
+ .DATA_W (DATA_W),
+ .COUNT_W (COUNT_W),
+ .CLK_RATE (CLK_RATE)
+ ) axi_ram_fifo_bist_regs_i (
+ .clk (clk),
+ .rst (rst),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr),
+ .s_ctrlport_req_data (s_ctrlport_req_data),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data),
+ .tx_count (tx_count),
+ .rx_count (rx_count),
+ .error_count (error_count),
+ .cycle_count (cycle_count),
+ .num_words (num_words),
+ .start (start),
+ .stop (stop),
+ .clear (clear),
+ .continuous (continuous),
+ .running (running)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // State Machine
+ //---------------------------------------------------------------------------
+
+ localparam ST_IDLE = 0;
+ localparam ST_ACTIVE = 1;
+ localparam ST_WAIT_DONE = 2;
+
+ reg [ 1:0] state;
+ reg [COUNT_W-1:0] num_words_m1;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ state <= ST_IDLE;
+ m_tvalid <= 0;
+ running <= 0;
+ end else begin
+ m_tvalid <= 0;
+
+ case (state)
+ ST_IDLE : begin
+ num_words_m1 <= num_words-1;
+ if (start) begin
+ running <= 1;
+ state <= ST_ACTIVE;
+ end
+ end
+
+ ST_ACTIVE : begin
+ if (stop || (tx_count == num_words_m1 && m_tvalid && m_tready && !continuous)) begin
+ m_tvalid <= 0;
+ state <= ST_WAIT_DONE;
+ end else begin
+ m_tvalid <= 1;
+ running <= 1;
+ end
+ end
+
+ ST_WAIT_DONE : begin
+ if (rx_count >= tx_count) begin
+ running <= 0;
+ state <= ST_IDLE;
+ end
+ end
+ endcase
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Data Generator
+ //---------------------------------------------------------------------------
+
+ reg count_en;
+
+ // Output data is the concatenation of our generated test word.
+ assign m_tdata = {(DATA_W/WORD_W){ tx_data }};
+
+ // We were born ready
+ assign s_tready = 1;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ tx_data <= next(INIT);
+ exp_data <= INIT;
+ rx_valid <= 0;
+ tx_count <= 0;
+ rx_count <= 0;
+ error_count <= 0;
+ cycle_count <= 0;
+ count_en <= 0;
+ end else begin
+ //
+ // Output Data generation
+ //
+ if (m_tvalid && m_tready) begin
+ tx_data <= next(tx_data);
+ tx_count <= tx_count + 1;
+ end
+
+ //
+ // Expected Data Generation
+ //
+ if (s_tvalid & s_tready) begin
+ rx_valid <= 1;
+ exp_data <= next(exp_data);
+ rx_count <= rx_count + 1;
+ rx_data <= s_tdata;
+ end else begin
+ rx_valid <= 0;
+ end
+
+ //
+ // Data checker
+ //
+ if (rx_valid) begin
+ if (rx_data !== {(DATA_W/WORD_W){exp_data}}) begin
+ error_count <= error_count + 1;
+ end
+ end
+
+ //
+ // Cycle Counter
+ //
+ // Start counting after get the first word back so that we measure
+ // throughput and not latency.
+ if (state == ST_IDLE) count_en <= 0;
+ else if (s_tvalid) count_en <= 1;
+
+ if (count_en) cycle_count <= cycle_count + 1;
+
+ //
+ // Clear counters upon request
+ //
+ if (clear) begin
+ tx_count <= 0;
+ rx_count <= 0;
+ error_count <= 0;
+ cycle_count <= 0;
+ end
+ end
+ end
+
+endmodule
+
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_bist_regs.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_bist_regs.v
new file mode 100644
index 000000000..c161c10f5
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_bist_regs.v
@@ -0,0 +1,206 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi_ram_fifo_bist_regs
+//
+// Description:
+//
+// Implements the registers for the RAM FIFO BIST logic.
+//
+// Parameters:
+//
+// DATA_W : The width of the data port to use for the AXI4-Stream
+// interface.
+//
+// COUNT_W : Width of internal counters. This must be wide enough so that
+// word, cycle, and and error counters don't overflow during a
+// test.
+//
+// CLK_RATE : The frequency of clk in Hz
+//
+
+module axi_ram_fifo_bist_regs #(
+ parameter DATA_W = 64,
+ parameter COUNT_W = 48,
+ parameter CLK_RATE = 200e6
+) (
+ input clk,
+ input rst,
+
+ //--------------------------------------------------------------------------
+ // CTRL Port
+ //--------------------------------------------------------------------------
+
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ output reg s_ctrlport_resp_ack,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ //--------------------------------------------------------------------------
+ // Control and Status
+ //--------------------------------------------------------------------------
+
+ input wire [COUNT_W-1:0] tx_count,
+ input wire [COUNT_W-1:0] rx_count,
+ input wire [COUNT_W-1:0] error_count,
+ input wire [COUNT_W-1:0] cycle_count,
+
+ output wire [COUNT_W-1:0] num_words,
+
+ output reg start,
+ output reg stop,
+ output reg clear,
+ output reg continuous,
+ input wire running
+);
+
+ `include "axi_ram_fifo_regs.vh"
+
+ localparam BYTES_PER_WORD = DATA_W/8;
+ localparam WORD_SHIFT = $clog2(BYTES_PER_WORD);
+
+ // Make sure DATA_W is a power of 2, or else the word/byte count conversion
+ // logic won't be correct.
+ if (2**$clog2(DATA_W) != DATA_W) begin
+ DATA_W_must_be_a_power_of_2();
+ end
+
+ // The register logic currently assumes that COUNT_W is at least 33 bits.
+ if (COUNT_W <= 32) begin
+ COUNT_W_must_be_larger_than_32();
+ end
+
+ wire [19:0] word_addr;
+ wire [63:0] tx_byte_count;
+ wire [63:0] rx_byte_count;
+ reg [63:0] num_bytes = 0;
+
+ reg [31:0] tx_byte_count_hi = 0;
+ reg [31:0] rx_byte_count_hi = 0;
+ reg [31:0] error_count_hi = 0;
+ reg [31:0] cycle_count_hi = 0;
+
+ // Only use the word address to simplify address decoding logic
+ assign word_addr = {s_ctrlport_req_addr[19:2], 2'b00 };
+
+ // Convert between words and bytes
+ assign tx_byte_count = tx_count << WORD_SHIFT;
+ assign rx_byte_count = rx_count << WORD_SHIFT;
+ assign num_words = num_bytes >> WORD_SHIFT;
+
+
+ always @(posedge clk) begin
+ if (rst) begin
+ s_ctrlport_resp_ack <= 0;
+ start <= 0;
+ stop <= 0;
+ continuous <= 0;
+ clear <= 0;
+ num_bytes <= 0;
+ end else begin
+ // Default values
+ s_ctrlport_resp_ack <= 0;
+ start <= 0;
+ stop <= 0;
+ clear <= 0;
+
+ //-----------------------------------------------------------------------
+ // Read Logic
+ //-----------------------------------------------------------------------
+
+ if (s_ctrlport_req_rd) begin
+ case (word_addr)
+ REG_BIST_CTRL : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data[REG_BIST_RUNNING_POS] <= running;
+ s_ctrlport_resp_data[REG_BIST_CONT_POS] <= continuous;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_CLK_RATE : begin
+ s_ctrlport_resp_data <= CLK_RATE;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_NUM_BYTES_LO : begin
+ s_ctrlport_resp_data <= num_bytes[31:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_NUM_BYTES_HI : begin
+ s_ctrlport_resp_data <= num_bytes[63:32];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_TX_BYTE_COUNT_LO : begin
+ s_ctrlport_resp_data <= tx_byte_count[31:0];
+ tx_byte_count_hi <= tx_byte_count[63:32];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_TX_BYTE_COUNT_HI : begin
+ s_ctrlport_resp_data <= tx_byte_count_hi;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_RX_BYTE_COUNT_LO : begin
+ s_ctrlport_resp_data <= rx_byte_count[31:0];
+ rx_byte_count_hi[COUNT_W-33:0] <= rx_byte_count[COUNT_W-1:32];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_RX_BYTE_COUNT_HI : begin
+ s_ctrlport_resp_data <= rx_byte_count_hi;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_ERROR_COUNT_LO : begin
+ s_ctrlport_resp_data <= error_count[31:0];
+ error_count_hi[COUNT_W-33:0] <= error_count[COUNT_W-1:32];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_ERROR_COUNT_HI : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data <= error_count_hi;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_CYCLE_COUNT_LO : begin
+ s_ctrlport_resp_data <= cycle_count[31:0];
+ cycle_count_hi[COUNT_W-33:0] <= cycle_count[COUNT_W-1:32];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_CYCLE_COUNT_HI : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data <= cycle_count_hi;
+ s_ctrlport_resp_ack <= 1;
+ end
+ endcase
+ end
+
+
+ //-----------------------------------------------------------------------
+ // Write Logic
+ //-----------------------------------------------------------------------
+
+ if (s_ctrlport_req_wr) begin
+ case (word_addr)
+ REG_BIST_CTRL : begin
+ start <= s_ctrlport_req_data[REG_BIST_START_POS];
+ stop <= s_ctrlport_req_data[REG_BIST_STOP_POS];
+ clear <= s_ctrlport_req_data[REG_BIST_CLEAR_POS];
+ continuous <= s_ctrlport_req_data[REG_BIST_CONT_POS];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_NUM_BYTES_LO : begin
+ // Update only the word-count portion
+ num_bytes[31:WORD_SHIFT] <= s_ctrlport_req_data[31:WORD_SHIFT];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_BIST_NUM_BYTES_HI : begin
+ num_bytes[COUNT_W-1:32] <= s_ctrlport_req_data[COUNT_W-33:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ endcase
+ end
+
+ end
+ end
+
+endmodule
+
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_regs.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_regs.v
new file mode 100644
index 000000000..4496d172d
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_regs.v
@@ -0,0 +1,207 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi_ram_fifo_regs
+//
+// Description:
+//
+// Implements the software-accessible registers for the axi_ram_fifo block.
+//
+
+
+module axi_ram_fifo_regs #(
+ parameter MEM_ADDR_W = 32,
+ parameter MEM_DATA_W = 64,
+ parameter [MEM_ADDR_W-1:0] FIFO_ADDR_BASE = 'h0,
+ parameter [MEM_ADDR_W-1:0] FIFO_ADDR_MASK = 'h0000FFFF,
+ parameter [MEM_ADDR_W-1:0] FIFO_ADDR_MASK_MIN = 'h00000FFF,
+ parameter BIST = 1,
+ parameter IN_FIFO_SIZE = 10,
+ parameter WORD_ADDR_W = 29,
+ parameter BURST_TIMEOUT = 128,
+ parameter TIMEOUT_W = 12
+) (
+
+ input wire clk,
+ input wire rst,
+
+ //--------------------------------------------------------------------------
+ // CTRL Port
+ //--------------------------------------------------------------------------
+
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ output reg s_ctrlport_resp_ack,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ //---------------------------------------------------------------------------
+ // Register Inputs and Outputs
+ //---------------------------------------------------------------------------
+
+ // Read-back Registers
+ input wire [ 31:0] rb_out_pkt_count,
+ input wire [WORD_ADDR_W:0] rb_occupied,
+
+ // Settings Registers
+ output reg [ 15:0] set_suppress_threshold,
+ output reg [ TIMEOUT_W-1:0] set_timeout,
+ output reg [MEM_ADDR_W-1:0] set_fifo_addr_base = FIFO_ADDR_BASE,
+ output reg [MEM_ADDR_W-1:0] set_fifo_addr_mask = FIFO_ADDR_MASK
+);
+
+ `include "axi_ram_fifo_regs.vh"
+
+ function automatic integer min(input integer a, b);
+ min = a < b ? a : b;
+ endfunction
+
+ function automatic integer max(input integer a, b);
+ max = a > b ? a : b;
+ endfunction
+
+ wire [19:0] word_addr;
+ wire [63:0] reg_fifo_fullness;
+ reg [31:0] reg_fifo_fullness_hi;
+
+ // Only use the word address to simplify address decoding logic
+ assign word_addr = {s_ctrlport_req_addr[19:2], 2'b00 };
+
+ // Convert the "occupied" word count to a 64-bit byte value
+ assign reg_fifo_fullness = {
+ {64-MEM_ADDR_W{1'b0}}, // Set unused upper bits to 0
+ rb_occupied,
+ {(MEM_ADDR_W-WORD_ADDR_W){1'b0}} // Set byte offset bits to 0
+ };
+
+ always @(posedge clk) begin
+ if (rst) begin
+ s_ctrlport_resp_ack <= 0;
+ set_suppress_threshold <= 0;
+ set_timeout <= BURST_TIMEOUT;
+ set_fifo_addr_base <= FIFO_ADDR_BASE;
+ set_fifo_addr_mask <= FIFO_ADDR_MASK;
+ end else begin
+ s_ctrlport_resp_ack <= 0;
+
+ //-----------------------------------------------------------------------
+ // Write Logic
+ //-----------------------------------------------------------------------
+
+ if (s_ctrlport_req_wr) begin
+ case (word_addr)
+ REG_FIFO_READ_SUPPRESS : begin
+ set_suppress_threshold <= s_ctrlport_req_data[REG_FIFO_SUPPRESS_THRESH_POS +: REG_FIFO_SUPPRESS_THRESH_W];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_TIMEOUT : begin
+ set_timeout[REG_TIMEOUT_W-1:0] <= s_ctrlport_req_data[REG_TIMEOUT_W-1:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_ADDR_BASE_LO : begin
+ set_fifo_addr_base[min(32, MEM_ADDR_W)-1:0] <= s_ctrlport_req_data[min(32, MEM_ADDR_W)-1:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_ADDR_BASE_HI : begin
+ if (MEM_ADDR_W > 32) begin
+ set_fifo_addr_base[max(32, MEM_ADDR_W-1):32] <= s_ctrlport_req_data[max(0, MEM_ADDR_W-33):0];
+ end
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_ADDR_MASK_LO : begin
+ // Coerce the lower bits so we are guaranteed to meet the minimum mask size requirement.
+ set_fifo_addr_mask[min(32, MEM_ADDR_W)-1:0] <=
+ s_ctrlport_req_data[min(32, MEM_ADDR_W)-1:0] | FIFO_ADDR_MASK_MIN;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_ADDR_MASK_HI : begin
+ if (MEM_ADDR_W > 32) begin
+ set_fifo_addr_mask[max(32, MEM_ADDR_W-1):32] <= s_ctrlport_req_data[max(0, MEM_ADDR_W-33):0];
+ end
+ s_ctrlport_resp_ack <= 1;
+ end
+ endcase
+ end
+
+
+ //-----------------------------------------------------------------------
+ // Read Logic
+ //-----------------------------------------------------------------------
+
+ if (s_ctrlport_req_rd) begin
+ case (word_addr)
+ REG_FIFO_INFO : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data[REG_FIFO_MAGIC_POS +: REG_FIFO_MAGIC_W] <= 16'hF1F0;
+ s_ctrlport_resp_data[REG_FIFO_BIST_PRSNT_POS] <= (BIST != 0);
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_READ_SUPPRESS : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data[REG_FIFO_IN_FIFO_SIZE_POS +: REG_FIFO_IN_FIFO_SIZE_W]
+ <= IN_FIFO_SIZE;
+ s_ctrlport_resp_data[REG_FIFO_SUPPRESS_THRESH_POS +: REG_FIFO_SUPPRESS_THRESH_W]
+ <= set_suppress_threshold;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_MEM_SIZE : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data[REG_FIFO_DATA_SIZE_POS +: REG_FIFO_DATA_SIZE_W]
+ <= MEM_DATA_W;
+ s_ctrlport_resp_data[REG_FIFO_ADDR_SIZE_POS +: REG_FIFO_ADDR_SIZE_W]
+ <= MEM_ADDR_W;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_TIMEOUT : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data[REG_TIMEOUT_W-1:0] <= set_timeout[REG_TIMEOUT_W-1:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_FULLNESS_LO : begin
+ s_ctrlport_resp_data <= reg_fifo_fullness[31:0];
+ reg_fifo_fullness_hi <= reg_fifo_fullness[63:32];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_FULLNESS_HI : begin
+ s_ctrlport_resp_data <= reg_fifo_fullness_hi;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_ADDR_BASE_LO : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data[min(32, MEM_ADDR_W)-1:0] <= set_fifo_addr_base[min(32, MEM_ADDR_W)-1:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_ADDR_BASE_HI : begin
+ s_ctrlport_resp_data <= 0;
+ if (MEM_ADDR_W > 32) begin
+ s_ctrlport_resp_data[max(0,MEM_ADDR_W-33):0] <= set_fifo_addr_base[max(32, MEM_ADDR_W-1):32];
+ end
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_ADDR_MASK_LO : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data[min(32, MEM_ADDR_W)-1:0] <= set_fifo_addr_mask[min(32, MEM_ADDR_W)-1:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_ADDR_MASK_HI : begin
+ s_ctrlport_resp_data <= 0;
+ if (MEM_ADDR_W > 32) begin
+ s_ctrlport_resp_data[max(0, MEM_ADDR_W-33):0] <= set_fifo_addr_mask[max(32, MEM_ADDR_W-1):32];
+ end
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_FIFO_PACKET_CNT : begin
+ s_ctrlport_resp_data <= 0;
+ s_ctrlport_resp_data <= rb_out_pkt_count;
+ s_ctrlport_resp_ack <= 1;
+ end
+ endcase
+ end
+
+ end
+ end
+
+endmodule \ No newline at end of file
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_regs.vh
new file mode 100644
index 000000000..ccb942552
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/axi_ram_fifo_regs.vh
@@ -0,0 +1,228 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axi_ram_fifo_regs (Header)
+//
+// Description: Header file for axi_ram_fifo_regs. All registers are 32-bit
+// words from software's perspective.
+//
+
+// Address space size, per FIFO. That is, each FIFO is separated in the CTRL
+// Port address space by 2^FIFO_ADDR_W bytes.
+localparam RAM_FIFO_ADDR_W = 7;
+
+
+// REG_FIFO_INFO (R|W)
+//
+// Contains info/control bits for the FIFO.
+//
+// [31:16] : Returns the magic number 0xF1F0 (read-only)
+// [0] : Indicates if BIST logic is present (read-only)
+//
+localparam REG_FIFO_INFO = 'h0;
+//
+localparam REG_FIFO_MAGIC_POS = 16;
+localparam REG_FIFO_BIST_PRSNT_POS = 0;
+//
+localparam REG_FIFO_MAGIC_W = 16;
+
+
+// REG_FIFO_READ_SUPPRESS (R|W)
+//
+// Controls the read suppression threshold. RAM reads will be disabled whenever
+// the amount of free space in the input buffer (in units of RAM words) falls
+// below this threshold. This is intended to prevent input buffer overflows
+// caused by the RAM being too busy with reads. To disable the read suppression
+// feature, set the threshold to 0. In general, the threshold should be set to
+// a small value relative to the input FIFO buffer size (the IN_FIFO_SIZE
+// field) so that it is only enabled when the input FIFO buffer is close to
+// overflowing.
+//
+
+// [31:16] : Address width of input buffer. In other words, the input buffer is
+// 2**REG_FIFO_IN_FIFO_SIZE RAM words deep. (read-only)
+// [15: 0] : Read suppression threshold, in RAM words (read/write)
+//
+localparam REG_FIFO_READ_SUPPRESS = 'h4;
+//
+localparam REG_FIFO_IN_FIFO_SIZE_POS = 16;
+localparam REG_FIFO_SUPPRESS_THRESH_POS = 0;
+//
+localparam REG_FIFO_IN_FIFO_SIZE_W = 16;
+localparam REG_FIFO_SUPPRESS_THRESH_W = 16;
+
+
+// REG_FIFO_MEM_SIZE (R)
+//
+// Returns information about the size of the attached memory. The address size
+// allows software to determine what mask and base address values are valid.
+//
+// [31:16] : Returns the bit width of the RAM word size.
+// [15: 0] : Returns the bit width of the RAM byte address size. That is, the
+// addressable portion of the attached memory is
+// 2**REG_FIFO_ADDR_SIZE bytes.
+//
+localparam REG_FIFO_MEM_SIZE = 'h8;
+//
+localparam REG_FIFO_DATA_SIZE_POS = 16;
+localparam REG_FIFO_ADDR_SIZE_POS = 0;
+//
+localparam REG_FIFO_DATA_SIZE_W = 16;
+localparam REG_FIFO_ADDR_SIZE_W = 16;
+
+
+// REG_FIFO_TIMEOUT (R/W)
+//
+// Programs the FIFO timeout, in memory interface clock cycles. For efficiency,
+// we want the memory to read and write full bursts. But we also don't want
+// smaller amounts of data to be stuck in the FIFO. This timeout determines how
+// long we wait for new data before we go ahead and perform a smaller
+// read/write. A longer timeout will make more efficient use of the memory, but
+// will increase latency. The default value is set by a module parameter.
+//
+// [31:12] : <Reserved>
+// [11: 0] : Timeout
+//
+localparam REG_FIFO_TIMEOUT = 'hC;
+//
+localparam REG_TIMEOUT_POS = 0;
+localparam REG_TIMEOUT_W = 12;
+
+
+// REG_FIFO_FULLNESS (R)
+//
+// Returns the fullness of the FIFO in bytes. This is is a 64-bit register in
+// which the least-significant 32-bit word must be read first.
+//
+localparam REG_FIFO_FULLNESS_LO = 'h10;
+localparam REG_FIFO_FULLNESS_HI = 'h14;
+
+
+// REG_FIFO_ADDR_BASE (R|W)
+//
+// Sets the base byte address to use for this FIFO. This should only be updated
+// when the FIFO is idle. This should be set to a multiple of
+// REG_FIFO_ADDR_MASK+1. Depending on the size of the memory connected, upper
+// bits might be ignored.
+//
+localparam REG_FIFO_ADDR_BASE_LO = 'h18;
+localparam REG_FIFO_ADDR_BASE_HI = 'h1C;
+
+
+// REG_FIFO_ADDR_MASK (R|W)
+//
+// The byte address mask that controls the portion of the memory address that
+// is allocated to this FIFO. For example, set to 0xFFFF for a 64 KiB memory.
+//
+// This should only be updated when the FIFO is idle. It must be equal to a
+// power-of-2 minus 1. It should be no smaller than FIFO_ADDR_MASK_MIN, defined
+// in axi_ram_fifo.v, otherwise it will be coerced up to that size.
+//
+// This is is a 64-bit register in which the least-significant 32-bit word must
+// be read/written first. Depending on the size of the memory connected, the
+// upper bits might be ignored.
+//
+localparam REG_FIFO_ADDR_MASK_LO = 'h20;
+localparam REG_FIFO_ADDR_MASK_HI = 'h24;
+
+
+// REG_FIFO_PACKET_CNT (R)
+//
+// Returns the number of packets transferred out of the FIFO block.
+//
+localparam REG_FIFO_PACKET_CNT = 'h28;
+
+
+//-----------------------------------------------------------------------------
+// BIST Registers
+//-----------------------------------------------------------------------------
+//
+// Only read these registers if the BIST component is included.
+//
+//-----------------------------------------------------------------------------
+
+// REG_BIST_CTRL (R|W)
+//
+// Control register for the BIST component.
+//
+// [4] : BIST is running. Changes to 1 after a test is started, then returns to
+// 0 when BIST is complete.
+//
+// [3] : Continuous mode (run until stopped). When set to 1, test will continue
+// to run until Stop bit is set.
+//
+// [2] : Clear the BIST counters (i.e., the TX, RX, cycle, and error counters)
+//
+// [1] : Stop BIST (strobe). Write a 1 to this bit to stop the test that is
+// currently running
+//
+// [0] : Start BIST (strobe). Write a 1 to this bit to start a test using the
+// configured NUM_BYTES and continuous mode setting.
+//
+localparam REG_BIST_CTRL = 'h30;
+//
+localparam REG_BIST_RUNNING_POS = 4;
+localparam REG_BIST_CONT_POS = 3;
+localparam REG_BIST_CLEAR_POS = 2; // Strobe
+localparam REG_BIST_STOP_POS = 1; // Strobe
+localparam REG_BIST_START_POS = 0; // Strobe
+
+
+// REG_BIST_CLOCK_RATE (R)
+//
+// Reports the clock rate of the BIST component in Hz. This can be used with
+// REG_BIST_CYCLE_COUNT to calculate throughput.
+//
+localparam REG_BIST_CLK_RATE = 'h34;
+
+
+// REG_BIST_NUM_BYTES (R|W)
+//
+// Number of bytes to generate for the next BIST run. THis is not used if the
+// REG_BIST_CONT_POS bit is set. This register should not be updated while the
+// BIST is running.
+//
+localparam REG_BIST_NUM_BYTES_LO = 'h38;
+localparam REG_BIST_NUM_BYTES_HI = 'h3C;
+
+
+// REG_BIST_TX_BYTE_COUNT (R)
+//
+// Reports the number of bytes transmitted by the BIST component. This should
+// always be read least-significant word first to ensure coherency. Once BIST
+// is complete, the TX count will equal the RX count.
+//
+localparam REG_BIST_TX_BYTE_COUNT_LO = 'h40;
+localparam REG_BIST_TX_BYTE_COUNT_HI = 'h44;
+
+
+// REG_BIST_RX_BYTE_COUNT (R)
+//
+// Reports the number of bytes received by the BIST component. This should
+// always be read least-significant word first to ensure coherency. Once BIST
+// is complete, the TX count will equal the RX count.
+//
+localparam REG_BIST_RX_BYTE_COUNT_LO = 'h48;
+localparam REG_BIST_RX_BYTE_COUNT_HI = 'h4C;
+
+
+// REG_BIST_ERROR_COUNT (R)
+//
+// Reports the number of words in which the BIST component detected errors.
+// This should always be read least-significant word first to ensure coherency.
+//
+localparam REG_BIST_ERROR_COUNT_LO = 'h50;
+localparam REG_BIST_ERROR_COUNT_HI = 'h54;
+
+
+// REG_BIST_CYCLE_COUNT (R)
+//
+// Reports the number of clock cycles that have elapsed while the BIST was
+// running. This can be used to calculate throughput. This should always be
+// read least-significant word first to ensure coherency.
+//
+localparam REG_BIST_CYCLE_COUNT_LO = 'h58;
+localparam REG_BIST_CYCLE_COUNT_HI = 'h5C;
+
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/noc_shell_axi_ram_fifo.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/noc_shell_axi_ram_fifo.v
new file mode 100644
index 000000000..fc353595d
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/noc_shell_axi_ram_fifo.v
@@ -0,0 +1,319 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: noc_shell_axi_ram_fifo
+//
+// Description: A NoC Shell for the RFNoC AXI RAM FIFO. This NoC Shell
+// implements the control port interface but does nothing to the
+// data path other than moving it to the requested clock domain.
+//
+
+`define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
+
+
+module noc_shell_axi_ram_fifo #(
+ parameter [31:0] NOC_ID = 32'h0,
+ parameter [ 9:0] THIS_PORTID = 10'd0,
+ parameter CHDR_W = 64,
+ parameter DATA_W = 64,
+ parameter [ 5:0] CTRL_FIFO_SIZE = 0,
+ parameter [ 0:0] CTRLPORT_MST_EN = 1,
+ parameter [ 0:0] CTRLPORT_SLV_EN = 1,
+ parameter [ 5:0] NUM_DATA_I = 1,
+ parameter [ 5:0] NUM_DATA_O = 1,
+ parameter [ 5:0] MTU = 10,
+ parameter SYNC_DATA_CLOCKS = 0
+) (
+ //---------------------------------------------------------------------------
+ // Framework Interface
+ //---------------------------------------------------------------------------
+
+ // RFNoC Framework Clocks and Resets
+ input wire rfnoc_chdr_clk,
+ output wire rfnoc_chdr_rst,
+ input wire rfnoc_ctrl_clk,
+ output wire rfnoc_ctrl_rst,
+ // RFNoC Backend Interface
+ input wire [ 511:0] rfnoc_core_config,
+ output wire [ 511:0] rfnoc_core_status,
+ // CHDR Input Ports (from framework)
+ input wire [(CHDR_W*NUM_DATA_I)-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tready,
+ // CHDR Output Ports (to framework)
+ output wire [(CHDR_W*NUM_DATA_O)-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tready,
+ // AXIS-Ctrl Input Port (from framework)
+ input wire [ 31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ // AXIS-Ctrl Output Port (to framework)
+ output wire [ 31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+
+ //---------------------------------------------------------------------------
+ // Client Control Port Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+ // Master
+ output wire m_ctrlport_req_wr,
+ output wire m_ctrlport_req_rd,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [31:0] m_ctrlport_req_data,
+ output wire [ 3:0] m_ctrlport_req_byte_en,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ input wire m_ctrlport_resp_ack,
+ input wire [ 1:0] m_ctrlport_resp_status,
+ input wire [31:0] m_ctrlport_resp_data,
+ // Slave
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [ 9:0] s_ctrlport_req_portid,
+ input wire [15:0] s_ctrlport_req_rem_epid,
+ input wire [ 9:0] s_ctrlport_req_rem_portid,
+ input wire [31:0] s_ctrlport_req_data,
+ input wire [ 3:0] s_ctrlport_req_byte_en,
+ input wire s_ctrlport_req_has_time,
+ input wire [63:0] s_ctrlport_req_time,
+ output wire s_ctrlport_resp_ack,
+ output wire [ 1:0] s_ctrlport_resp_status,
+ output wire [31:0] s_ctrlport_resp_data,
+
+ //---------------------------------------------------------------------------
+ // Client Data Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+
+ // Output data stream (to user logic)
+ output wire [ (NUM_DATA_I*DATA_W)-1:0] m_axis_tdata,
+ output wire [(NUM_DATA_I*`MAX(DATA_W/CHDR_W, 1))-1:0] m_axis_tkeep,
+ output wire [ NUM_DATA_I-1:0] m_axis_tlast,
+ output wire [ NUM_DATA_I-1:0] m_axis_tvalid,
+ input wire [ NUM_DATA_I-1:0] m_axis_tready,
+
+ // Input data stream (from user logic)
+ input wire [ (NUM_DATA_O*DATA_W)-1:0] s_axis_tdata,
+ input wire [(NUM_DATA_O*`MAX(DATA_W/CHDR_W, 1))-1:0] s_axis_tkeep,
+ input wire [ NUM_DATA_O-1:0] s_axis_tlast,
+ input wire [ NUM_DATA_O-1:0] s_axis_tvalid,
+ output wire [ NUM_DATA_O-1:0] s_axis_tready
+);
+
+ //---------------------------------------------------------------------------
+ // Backend Interface
+ //---------------------------------------------------------------------------
+ wire data_i_flush_en;
+ wire [31:0] data_i_flush_timeout;
+ wire [63:0] data_i_flush_active;
+ wire [63:0] data_i_flush_done;
+ wire data_o_flush_en;
+ wire [31:0] data_o_flush_timeout;
+ wire [63:0] data_o_flush_active;
+ wire [63:0] data_o_flush_done;
+
+ backend_iface #(
+ .NOC_ID (NOC_ID),
+ .NUM_DATA_I (NUM_DATA_I),
+ .NUM_DATA_O (NUM_DATA_O),
+ .CTRL_FIFOSIZE (CTRL_FIFO_SIZE),
+ .MTU (MTU)
+ ) backend_iface_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst),
+ .data_i_flush_en (data_i_flush_en),
+ .data_i_flush_timeout (data_i_flush_timeout),
+ .data_i_flush_active (data_i_flush_active),
+ .data_i_flush_done (data_i_flush_done),
+ .data_o_flush_en (data_o_flush_en),
+ .data_o_flush_timeout (data_o_flush_timeout),
+ .data_o_flush_active (data_o_flush_active),
+ .data_o_flush_done (data_o_flush_done)
+ );
+
+ //---------------------------------------------------------------------------
+ // Control Path
+ //---------------------------------------------------------------------------
+
+ ctrlport_endpoint #(
+ .THIS_PORTID (THIS_PORTID ),
+ .SYNC_CLKS (0 ),
+ .AXIS_CTRL_MST_EN (CTRLPORT_SLV_EN),
+ .AXIS_CTRL_SLV_EN (CTRLPORT_MST_EN),
+ .SLAVE_FIFO_SIZE (CTRL_FIFO_SIZE )
+ ) ctrlport_ep_i (
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk ),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst ),
+ .ctrlport_clk (ctrlport_clk ),
+ .ctrlport_rst (ctrlport_rst ),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata ),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast ),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid ),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready ),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata ),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast ),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid ),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready ),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr ),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd ),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr ),
+ .m_ctrlport_req_data (m_ctrlport_req_data ),
+ .m_ctrlport_req_byte_en (m_ctrlport_req_byte_en ),
+ .m_ctrlport_req_has_time (m_ctrlport_req_has_time ),
+ .m_ctrlport_req_time (m_ctrlport_req_time ),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack ),
+ .m_ctrlport_resp_status (m_ctrlport_resp_status ),
+ .m_ctrlport_resp_data (m_ctrlport_resp_data ),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr ),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd ),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr ),
+ .s_ctrlport_req_portid (s_ctrlport_req_portid ),
+ .s_ctrlport_req_rem_epid (s_ctrlport_req_rem_epid ),
+ .s_ctrlport_req_rem_portid(s_ctrlport_req_rem_portid),
+ .s_ctrlport_req_data (s_ctrlport_req_data ),
+ .s_ctrlport_req_byte_en (s_ctrlport_req_byte_en ),
+ .s_ctrlport_req_has_time (s_ctrlport_req_has_time ),
+ .s_ctrlport_req_time (s_ctrlport_req_time ),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack ),
+ .s_ctrlport_resp_status (s_ctrlport_resp_status ),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data )
+ );
+
+ //---------------------------------------------------------------------------
+ // Data Path
+ //---------------------------------------------------------------------------
+
+ // Set WORD_W to the smaller of DATA_W and CHDR_W. This will be our common
+ // word size between the CHDR and user data ports.
+ localparam WORD_W = DATA_W < CHDR_W ? DATA_W : CHDR_W;
+ localparam KEEP_W = `MAX(DATA_W/CHDR_W, 1);
+
+ genvar i;
+
+ for (i = 0; i < NUM_DATA_I; i = i + 1) begin : gen_in
+ wire [CHDR_W-1:0] temp_in_tdata;
+ wire temp_in_tlast;
+ wire temp_in_tvalid;
+ wire temp_in_tready;
+
+ axis_packet_flush #(
+ .WIDTH (CHDR_W),
+ .FLUSH_PARTIAL_PKTS (0),
+ .TIMEOUT_W (32),
+ .PIPELINE ("IN")
+ ) in_packet_flush_i (
+ .clk (rfnoc_chdr_clk),
+ .reset (rfnoc_chdr_rst),
+ .enable (data_i_flush_en),
+ .timeout (data_i_flush_timeout),
+ .flushing (data_i_flush_active[i]),
+ .done (data_i_flush_done[i]),
+ .s_axis_tdata (s_rfnoc_chdr_tdata[i*CHDR_W +: CHDR_W]),
+ .s_axis_tlast (s_rfnoc_chdr_tlast[i]),
+ .s_axis_tvalid (s_rfnoc_chdr_tvalid[i]),
+ .s_axis_tready (s_rfnoc_chdr_tready[i]),
+ .m_axis_tdata (temp_in_tdata),
+ .m_axis_tlast (temp_in_tlast),
+ .m_axis_tvalid (temp_in_tvalid),
+ .m_axis_tready (temp_in_tready)
+ );
+
+ axis_width_conv #(
+ .WORD_W (WORD_W),
+ .IN_WORDS (CHDR_W/WORD_W),
+ .OUT_WORDS (DATA_W/WORD_W),
+ .SYNC_CLKS (SYNC_DATA_CLOCKS),
+ .PIPELINE ("NONE")
+ ) in_width_conv_i (
+ .s_axis_aclk (rfnoc_chdr_clk),
+ .s_axis_rst (rfnoc_chdr_rst),
+ .s_axis_tdata (temp_in_tdata),
+ .s_axis_tkeep ({CHDR_W/WORD_W{1'b1}}),
+ .s_axis_tlast (temp_in_tlast),
+ .s_axis_tvalid (temp_in_tvalid),
+ .s_axis_tready (temp_in_tready),
+ .m_axis_aclk (axis_data_clk),
+ .m_axis_rst (axis_data_rst),
+ .m_axis_tdata (m_axis_tdata[i*DATA_W +: DATA_W]),
+ .m_axis_tkeep (m_axis_tkeep[i*KEEP_W +: KEEP_W]),
+ .m_axis_tlast (m_axis_tlast[i]),
+ .m_axis_tvalid (m_axis_tvalid[i]),
+ .m_axis_tready (m_axis_tready[i])
+ );
+ end
+
+
+ for (i = 0; i < NUM_DATA_O; i = i + 1) begin : gen_out
+ wire [ CHDR_W-1:0] temp_out_tdata;
+ wire [CHDR_W/WORD_W-1:0] temp_out_tkeep;
+ wire temp_out_tlast;
+ wire temp_out_tvalid;
+ wire temp_out_tready;
+
+ axis_width_conv #(
+ .WORD_W (WORD_W),
+ .IN_WORDS (DATA_W/WORD_W),
+ .OUT_WORDS (CHDR_W/WORD_W),
+ .SYNC_CLKS (SYNC_DATA_CLOCKS),
+ .PIPELINE ("NONE")
+ ) out_width_conv_i (
+ .s_axis_aclk (axis_data_clk),
+ .s_axis_rst (axis_data_rst),
+ .s_axis_tdata (s_axis_tdata[i*DATA_W +: DATA_W]),
+ .s_axis_tkeep (s_axis_tkeep[i*KEEP_W +: KEEP_W]),
+ .s_axis_tlast (s_axis_tlast[i]),
+ .s_axis_tvalid (s_axis_tvalid[i]),
+ .s_axis_tready (s_axis_tready[i]),
+ .m_axis_aclk (rfnoc_chdr_clk),
+ .m_axis_rst (rfnoc_chdr_rst),
+ .m_axis_tdata (temp_out_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (temp_out_tlast),
+ .m_axis_tvalid (temp_out_tvalid),
+ .m_axis_tready (temp_out_tready)
+ );
+
+ axis_packet_flush #(
+ .WIDTH (CHDR_W),
+ .FLUSH_PARTIAL_PKTS (0),
+ .TIMEOUT_W (32),
+ .PIPELINE ("OUT")
+ ) out_packet_flush_i (
+ .clk (rfnoc_chdr_clk),
+ .reset (rfnoc_chdr_rst),
+ .enable (data_o_flush_en),
+ .timeout (data_o_flush_timeout),
+ .flushing (data_o_flush_active[i]),
+ .done (data_o_flush_done[i]),
+ .s_axis_tdata (temp_out_tdata),
+ .s_axis_tlast (temp_out_tlast),
+ .s_axis_tvalid (temp_out_tvalid),
+ .s_axis_tready (temp_out_tready),
+ .m_axis_tdata (m_rfnoc_chdr_tdata[i*CHDR_W +: CHDR_W]),
+ .m_axis_tlast (m_rfnoc_chdr_tlast[i]),
+ .m_axis_tvalid (m_rfnoc_chdr_tvalid[i]),
+ .m_axis_tready (m_rfnoc_chdr_tready[i])
+ );
+
+ end
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo.v
new file mode 100644
index 000000000..04d942ce0
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo.v
@@ -0,0 +1,485 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_axi_ram_fifo
+//
+// Description:
+//
+// Implements a FIFO using an AXI memory-mapped interface to an external
+// memory.
+//
+// Parameters:
+//
+// THIS_PORTID : Control crossbar port to which this block is connected
+//
+// CHDR_W : CHDR AXI-Stream data bus width
+//
+// NUM_PORTS : Number of independent FIFOs to support, all sharing the
+// same memory.
+//
+// MTU : Maximum transfer unit (maximum packet size) to support,
+// in CHDR_W-sized words.
+//
+// MEM_DATA_W : Width of the data bus to use for the AXI memory-mapped
+// interface. This must be no bigger than CHDR_W and it must
+// evenly divide CHDR_W.
+//
+// MEM_ADDR_W : Width of the byte address to use for RAM addressing. This
+// effectively sets the maximum combined size of all FIFOs.
+// This must be less than or equal to AWIDTH.
+//
+// AWIDTH : Width of the address bus for the AXI memory-mapped
+// interface. This must be at least as big as MEM_DATA_W.
+//
+// FIFO_ADDR_BASE : Default base byte address of each FIFO. When NUM_PORTS >
+// 1, this should be the concatenation of all the FIFO base
+// addresses. These values can be reconfigured by software.
+//
+// FIFO_ADDR_MASK : Default byte address mask used by each FIFO. It must be
+// all ones. The size of the FIFO in bytes will be this
+// minus one. These values can be reconfigured by software.
+//
+// BURST_TIMEOUT : Default number of memory clock cycles to wait for new
+// data before performing a short, sub-optimal burst. One
+// value per FIFO.
+//
+// IN_FIFO_SIZE : Size of the input buffer. This is used to mitigate the
+// effects of memory write latency, which can be significant
+// when the external memory is DRAM.
+//
+// OUT_FIFO_SIZE : Size of the output buffer. This is used to mitigate the
+// effects of memory read latency, which can be significant
+// when the external memory is DRAM.
+//
+// BIST : Includes BIST logic when true.
+//
+// MEM_CLK_RATE : Frequency of mem_clk in Hz. This is used by BIST for
+// throughput calculation.
+//
+
+module rfnoc_block_axi_ram_fifo #(
+ parameter THIS_PORTID = 0,
+ parameter CHDR_W = 64,
+ parameter NUM_PORTS = 1,
+ parameter MTU = 10,
+ parameter MEM_DATA_W = CHDR_W,
+ parameter MEM_ADDR_W = 32,
+ parameter AWIDTH = 32,
+ parameter [NUM_PORTS*MEM_ADDR_W-1:0] FIFO_ADDR_BASE = {NUM_PORTS{ {MEM_ADDR_W{1'b0}} }},
+ parameter [NUM_PORTS*MEM_ADDR_W-1:0] FIFO_ADDR_MASK = {NUM_PORTS{ {(MEM_ADDR_W-$clog2(NUM_PORTS)){1'b1}} }},
+ parameter [ NUM_PORTS*32-1:0] BURST_TIMEOUT = {NUM_PORTS{ 32'd256 }},
+ parameter IN_FIFO_SIZE = 11,
+ parameter OUT_FIFO_SIZE = 11,
+ parameter BIST = 1,
+ parameter MEM_CLK_RATE = 200e6
+) (
+ //---------------------------------------------------------------------------
+ // AXIS CHDR Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_chdr_clk,
+
+ // CHDR inputs from framework
+ input wire [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tready,
+
+ // CHDR outputs to framework
+ output wire [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
+
+ // Backend interface
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+
+
+ //---------------------------------------------------------------------------
+ // AXIS CTRL Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_ctrl_clk,
+
+ // CTRL port requests from framework
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+
+ // CTRL port requests to framework
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+
+
+ //---------------------------------------------------------------------------
+ // AXI Memory Mapped Interface
+ //---------------------------------------------------------------------------
+
+ // AXI Interface Clock and Reset
+ input wire mem_clk,
+ input wire axi_rst,
+
+ // AXI Write Address Channel
+ output wire [ NUM_PORTS*1-1:0] m_axi_awid, // Write address ID. This signal is the identification tag for the write address signals
+ output wire [ NUM_PORTS*AWIDTH-1:0] m_axi_awaddr, // Write address. The write address gives the address of the first transfer in a write burst
+ output wire [ NUM_PORTS*8-1:0] m_axi_awlen, // Burst length. The burst length gives the exact number of transfers in a burst.
+ output wire [ NUM_PORTS*3-1:0] m_axi_awsize, // Burst size. This signal indicates the size of each transfer in the burst.
+ output wire [ NUM_PORTS*2-1:0] m_axi_awburst, // Burst type. The burst type and the size information, determine how the address is calculated
+ output wire [ NUM_PORTS*1-1:0] m_axi_awlock, // Lock type. Provides additional information about the atomic characteristics of the transfer.
+ output wire [ NUM_PORTS*4-1:0] m_axi_awcache, // Memory type. This signal indicates how transactions are required to progress
+ output wire [ NUM_PORTS*3-1:0] m_axi_awprot, // Protection type. This signal indicates the privilege and security level of the transaction
+ output wire [ NUM_PORTS*4-1:0] m_axi_awqos, // Quality of Service, QoS. The QoS identifier sent for each write transaction
+ output wire [ NUM_PORTS*4-1:0] m_axi_awregion, // Region identifier. Permits a single physical interface on a slave to be re-used.
+ output wire [ NUM_PORTS*1-1:0] m_axi_awuser, // User signal. Optional User-defined signal in the write address channel.
+ output wire [ NUM_PORTS*1-1:0] m_axi_awvalid, // Write address valid. This signal indicates that the channel is signaling valid write addr
+ input wire [ NUM_PORTS*1-1:0] m_axi_awready, // Write address ready. This signal indicates that the slave is ready to accept an address
+
+ // AXI Write Data Channel
+ output wire [ NUM_PORTS*MEM_DATA_W-1:0] m_axi_wdata, // Write data
+ output wire [NUM_PORTS*MEM_DATA_W/8-1:0] m_axi_wstrb, // Write strobes. This signal indicates which byte lanes hold valid data.
+ output wire [ NUM_PORTS*1-1:0] m_axi_wlast, // Write last. This signal indicates the last transfer in a write burst
+ output wire [ NUM_PORTS*1-1:0] m_axi_wuser, // User signal. Optional User-defined signal in the write data channel.
+ output wire [ NUM_PORTS*1-1:0] m_axi_wvalid, // Write valid. This signal indicates that valid write data and strobes are available.
+ input wire [ NUM_PORTS*1-1:0] m_axi_wready, // Write ready. This signal indicates that the slave can accept the write data.
+
+ // AXI Write Response Channel
+ input wire [ NUM_PORTS*1-1:0] m_axi_bid, // Response ID tag. This signal is the ID tag of the write response.
+ input wire [ NUM_PORTS*2-1:0] m_axi_bresp, // Write response. This signal indicates the status of the write transaction.
+ input wire [ NUM_PORTS*1-1:0] m_axi_buser, // User signal. Optional User-defined signal in the write response channel.
+ input wire [ NUM_PORTS*1-1:0] m_axi_bvalid, // Write response valid. This signal indicates that the channel is signaling a valid response
+ output wire [ NUM_PORTS*1-1:0] m_axi_bready, // Response ready. This signal indicates that the master can accept a write response
+
+ // AXI Read Address Channel
+ output wire [ NUM_PORTS*1-1:0] m_axi_arid, // Read address ID. This signal is the identification tag for the read address group of signals
+ output wire [ NUM_PORTS*AWIDTH-1:0] m_axi_araddr, // Read address. The read address gives the address of the first transfer in a read burst
+ output wire [ NUM_PORTS*8-1:0] m_axi_arlen, // Burst length. This signal indicates the exact number of transfers in a burst.
+ output wire [ NUM_PORTS*3-1:0] m_axi_arsize, // Burst size. This signal indicates the size of each transfer in the burst.
+ output wire [ NUM_PORTS*2-1:0] m_axi_arburst, // Burst type. The burst type and the size information determine how the address for each transfer
+ output wire [ NUM_PORTS*1-1:0] m_axi_arlock, // Lock type. This signal provides additional information about the atomic characteristics
+ output wire [ NUM_PORTS*4-1:0] m_axi_arcache, // Memory type. This signal indicates how transactions are required to progress
+ output wire [ NUM_PORTS*3-1:0] m_axi_arprot, // Protection type. This signal indicates the privilege and security level of the transaction
+ output wire [ NUM_PORTS*4-1:0] m_axi_arqos, // Quality of Service, QoS. QoS identifier sent for each read transaction.
+ output wire [ NUM_PORTS*4-1:0] m_axi_arregion, // Region identifier. Permits a single physical interface on a slave to be re-used
+ output wire [ NUM_PORTS*1-1:0] m_axi_aruser, // User signal. Optional User-defined signal in the read address channel.
+ output wire [ NUM_PORTS*1-1:0] m_axi_arvalid, // Read address valid. This signal indicates that the channel is signaling valid read addr
+ input wire [ NUM_PORTS*1-1:0] m_axi_arready, // Read address ready. This signal indicates that the slave is ready to accept an address
+
+ // AXI Read Data Channel
+ input wire [ NUM_PORTS*1-1:0] m_axi_rid, // Read ID tag. This signal is the identification tag for the read data group of signals
+ input wire [NUM_PORTS*MEM_DATA_W-1:0] m_axi_rdata, // Read data.
+ input wire [ NUM_PORTS*2-1:0] m_axi_rresp, // Read response. This signal indicates the status of the read transfer
+ input wire [ NUM_PORTS*1-1:0] m_axi_rlast, // Read last. This signal indicates the last transfer in a read burst.
+ input wire [ NUM_PORTS*1-1:0] m_axi_ruser, // User signal. Optional User-defined signal in the read data channel.
+ input wire [ NUM_PORTS*1-1:0] m_axi_rvalid, // Read valid. This signal indicates that the channel is signaling the required read data.
+ output wire [ NUM_PORTS*1-1:0] m_axi_rready // Read ready. This signal indicates that the master can accept the read data and response
+);
+
+ `include "axi_ram_fifo_regs.vh"
+
+ localparam NOC_ID = 'hF1F0_0000;
+
+ // If the memory width is larger than the CHDR width, then we need to use
+ // tkeep to track which CHDR words are valid as they go through the FIFO.
+ // Calculate the TKEEP width here. Set to 1 if it's not needed.
+ localparam KEEP_W = (MEM_DATA_W/CHDR_W) > 1 ? (MEM_DATA_W/CHDR_W) : 1;
+
+
+ //---------------------------------------------------------------------------
+ // Parameter Checks
+ //---------------------------------------------------------------------------
+
+ if (CHDR_W % MEM_DATA_W != 0 && MEM_DATA_W % CHDR_W != 0)
+ CHDR_W_must_be_a_multiple_of_MEM_DATA_W_or_vice_versa();
+
+ if (MEM_ADDR_W > AWIDTH)
+ MEM_ADDR_W_must_be_greater_than_AWIDTH();
+
+
+ //---------------------------------------------------------------------------
+ // NoC Shell
+ //---------------------------------------------------------------------------
+
+ wire rfnoc_chdr_rst;
+
+ wire ctrlport_req_wr;
+ wire ctrlport_req_rd;
+ wire [19:0] ctrlport_req_addr;
+ wire [31:0] ctrlport_req_data;
+ wire ctrlport_resp_ack;
+ wire [31:0] ctrlport_resp_data;
+
+ wire [NUM_PORTS*MEM_DATA_W-1:0] m_axis_data_tdata;
+ wire [ NUM_PORTS*KEEP_W-1:0] m_axis_data_tkeep;
+ wire [ NUM_PORTS-1:0] m_axis_data_tlast;
+ wire [ NUM_PORTS-1:0] m_axis_data_tvalid;
+ wire [ NUM_PORTS-1:0] m_axis_data_tready;
+
+ wire [NUM_PORTS*MEM_DATA_W-1:0] s_axis_data_tdata;
+ wire [ NUM_PORTS*KEEP_W-1:0] s_axis_data_tkeep;
+ wire [ NUM_PORTS-1:0] s_axis_data_tlast;
+ wire [ NUM_PORTS-1:0] s_axis_data_tvalid;
+ wire [ NUM_PORTS-1:0] s_axis_data_tready;
+
+ noc_shell_axi_ram_fifo #(
+ .NOC_ID (NOC_ID),
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .DATA_W (MEM_DATA_W),
+ .CTRL_FIFO_SIZE (5),
+ .CTRLPORT_MST_EN (1),
+ .CTRLPORT_SLV_EN (0),
+ .NUM_DATA_I (NUM_PORTS),
+ .NUM_DATA_O (NUM_PORTS),
+ .MTU (MTU),
+ .SYNC_DATA_CLOCKS (0)
+ ) noc_shell_axi_ram_fifo_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_ctrl_rst (),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
+ .ctrlport_clk (mem_clk),
+ .ctrlport_rst (axi_rst),
+ .m_ctrlport_req_wr (ctrlport_req_wr),
+ .m_ctrlport_req_rd (ctrlport_req_rd),
+ .m_ctrlport_req_addr (ctrlport_req_addr),
+ .m_ctrlport_req_data (ctrlport_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (),
+ .m_ctrlport_req_time (),
+ .m_ctrlport_resp_ack (ctrlport_resp_ack),
+ .m_ctrlport_resp_status (2'b0),
+ .m_ctrlport_resp_data (ctrlport_resp_data),
+ .s_ctrlport_req_wr (1'b0),
+ .s_ctrlport_req_rd (1'b0),
+ .s_ctrlport_req_addr (20'b0),
+ .s_ctrlport_req_portid (10'b0),
+ .s_ctrlport_req_rem_epid (16'b0),
+ .s_ctrlport_req_rem_portid (10'b0),
+ .s_ctrlport_req_data (32'b0),
+ .s_ctrlport_req_byte_en (4'b0),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (),
+ .axis_data_clk (mem_clk),
+ .axis_data_rst (axi_rst),
+ .m_axis_tdata (m_axis_data_tdata),
+ .m_axis_tkeep (m_axis_data_tkeep),
+ .m_axis_tlast (m_axis_data_tlast),
+ .m_axis_tvalid (m_axis_data_tvalid),
+ .m_axis_tready (m_axis_data_tready),
+ .s_axis_tdata (s_axis_data_tdata),
+ .s_axis_tkeep (s_axis_data_tkeep),
+ .s_axis_tlast (s_axis_data_tlast),
+ .s_axis_tvalid (s_axis_data_tvalid),
+ .s_axis_tready (s_axis_data_tready)
+ );
+
+ wire rfnoc_chdr_rst_mem_clk;
+ reg mem_rst_block;
+
+ // Cross the CHDR reset to the mem_clk domain
+ pulse_synchronizer #(
+ .MODE ("POSEDGE")
+ ) ctrl_rst_sync_i (
+ .clk_a (rfnoc_chdr_clk),
+ .rst_a (1'b0),
+ .pulse_a (rfnoc_chdr_rst),
+ .busy_a (),
+ .clk_b (mem_clk),
+ .pulse_b (rfnoc_chdr_rst_mem_clk)
+ );
+
+ // Combine the resets in a glitch-free manner
+ always @(posedge mem_clk) begin
+ mem_rst_block <= axi_rst | rfnoc_chdr_rst_mem_clk;
+ end
+
+
+ //---------------------------------------------------------------------------
+ // CTRL Port Splitter
+ //---------------------------------------------------------------------------
+
+ wire [ NUM_PORTS-1:0] m_ctrlport_req_wr;
+ wire [ NUM_PORTS-1:0] m_ctrlport_req_rd;
+ wire [20*NUM_PORTS-1:0] m_ctrlport_req_addr;
+ wire [32*NUM_PORTS-1:0] m_ctrlport_req_data;
+ wire [ NUM_PORTS-1:0] m_ctrlport_resp_ack;
+ wire [32*NUM_PORTS-1:0] m_ctrlport_resp_data;
+
+ ctrlport_decoder #(
+ .NUM_SLAVES (NUM_PORTS),
+ .BASE_ADDR (0),
+ .SLAVE_ADDR_W (RAM_FIFO_ADDR_W)
+ ) ctrlport_splitter_i (
+ .ctrlport_clk (mem_clk),
+ .ctrlport_rst (mem_rst_block),
+ .s_ctrlport_req_wr (ctrlport_req_wr),
+ .s_ctrlport_req_rd (ctrlport_req_rd),
+ .s_ctrlport_req_addr (ctrlport_req_addr),
+ .s_ctrlport_req_data (ctrlport_req_data),
+ .s_ctrlport_req_byte_en (4'b1111),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (ctrlport_resp_ack),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (ctrlport_resp_data),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr),
+ .m_ctrlport_req_data (m_ctrlport_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (),
+ .m_ctrlport_req_time (),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack),
+ .m_ctrlport_resp_status ({NUM_PORTS*2{1'b0}}),
+ .m_ctrlport_resp_data (m_ctrlport_resp_data)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // FIFO Instances
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_ram_fifos
+
+ wire [MEM_ADDR_W-1:0] m_axi_awaddr_int;
+ wire [MEM_ADDR_W-1:0] m_axi_araddr_int;
+
+ // Resize the addresses from MEM_ADDR_W to AWIDTH
+ assign m_axi_awaddr[(AWIDTH*(i+1))-1:AWIDTH*i] = m_axi_awaddr_int;
+ assign m_axi_araddr[(AWIDTH*(i+1))-1:AWIDTH*i] = m_axi_araddr_int;
+
+ axi_ram_fifo #(
+ .MEM_ADDR_W (MEM_ADDR_W),
+ .MEM_DATA_W (MEM_DATA_W),
+ .KEEP_W (KEEP_W),
+ .FIFO_ADDR_BASE (FIFO_ADDR_BASE[MEM_ADDR_W*i +: MEM_ADDR_W]),
+ .FIFO_ADDR_MASK (FIFO_ADDR_MASK[MEM_ADDR_W*i +: MEM_ADDR_W]),
+ .BURST_TIMEOUT (BURST_TIMEOUT[32*i +: 32]),
+ .BIST (BIST),
+ .CLK_RATE (MEM_CLK_RATE),
+ .IN_FIFO_SIZE (IN_FIFO_SIZE),
+ .OUT_FIFO_SIZE (OUT_FIFO_SIZE)
+ ) axi_ram_fifo_i (
+
+ .clk(mem_clk),
+ .rst(mem_rst_block),
+
+ //-----------------------------------------------------------------------
+ // Control Port
+ //-----------------------------------------------------------------------
+
+ .s_ctrlport_req_wr (m_ctrlport_req_wr[i]),
+ .s_ctrlport_req_rd (m_ctrlport_req_rd[i]),
+ .s_ctrlport_req_addr (m_ctrlport_req_addr[20*i +: 20]),
+ .s_ctrlport_req_data (m_ctrlport_req_data[32*i +: 32]),
+ .s_ctrlport_resp_ack (m_ctrlport_resp_ack[i]),
+ .s_ctrlport_resp_data (m_ctrlport_resp_data[32*i +: 32]),
+
+ //-----------------------------------------------------------------------
+ // AXI-Stream FIFO Interface
+ //-----------------------------------------------------------------------
+
+ // AXI-Stream Input
+ .s_tdata (m_axis_data_tdata[MEM_DATA_W*i +: MEM_DATA_W]),
+ .s_tkeep (m_axis_data_tkeep[KEEP_W*i +: KEEP_W]),
+ .s_tlast (m_axis_data_tlast[i]),
+ .s_tvalid (m_axis_data_tvalid[i]),
+ .s_tready (m_axis_data_tready[i]),
+ //
+ // AXI-Stream Output
+ .m_tdata (s_axis_data_tdata[MEM_DATA_W*i +: MEM_DATA_W]),
+ .m_tkeep (s_axis_data_tkeep[KEEP_W*i +: KEEP_W]),
+ .m_tlast (s_axis_data_tlast[i]),
+ .m_tvalid (s_axis_data_tvalid[i]),
+ .m_tready (s_axis_data_tready[i]),
+
+ //-----------------------------------------------------------------------
+ // AXI4 Memory Interface
+ //-----------------------------------------------------------------------
+
+ // AXI Write address channel
+ .m_axi_awid (m_axi_awid[i]),
+ .m_axi_awaddr (m_axi_awaddr_int),
+ .m_axi_awlen (m_axi_awlen[(8*(i+1))-1:8*i]),
+ .m_axi_awsize (m_axi_awsize[(3*(i+1))-1:3*i]),
+ .m_axi_awburst (m_axi_awburst[(2*(i+1))-1:2*i]),
+ .m_axi_awlock (m_axi_awlock[i]),
+ .m_axi_awcache (m_axi_awcache[(4*(i+1))-1:4*i]),
+ .m_axi_awprot (m_axi_awprot[(3*(i+1))-1:3*i]),
+ .m_axi_awqos (m_axi_awqos[(4*(i+1))-1:4*i]),
+ .m_axi_awregion (m_axi_awregion[(4*(i+1))-1:4*i]),
+ .m_axi_awuser (m_axi_awuser[i]),
+ .m_axi_awvalid (m_axi_awvalid[i]),
+ .m_axi_awready (m_axi_awready[i]),
+ //
+ // AXI Write data channel.
+ .m_axi_wdata (m_axi_wdata[(MEM_DATA_W*(i+1))-1:MEM_DATA_W*i]),
+ .m_axi_wstrb (m_axi_wstrb[((MEM_DATA_W/8)*(i+1))-1:(MEM_DATA_W/8)*i]),
+ .m_axi_wlast (m_axi_wlast[i]),
+ .m_axi_wuser (m_axi_wuser[i]),
+ .m_axi_wvalid (m_axi_wvalid[i]),
+ .m_axi_wready (m_axi_wready[i]),
+ //
+ // AXI Write response channel signals
+ .m_axi_bid (m_axi_bid[i]),
+ .m_axi_bresp (m_axi_bresp[(2*(i+1))-1:2*i]),
+ .m_axi_buser (m_axi_buser[i]),
+ .m_axi_bvalid (m_axi_bvalid[i]),
+ .m_axi_bready (m_axi_bready[i]),
+ //
+ // AXI Read address channel
+ .m_axi_arid (m_axi_arid[i]),
+ .m_axi_araddr (m_axi_araddr_int),
+ .m_axi_arlen (m_axi_arlen[(8*(i+1))-1:8*i]),
+ .m_axi_arsize (m_axi_arsize[(3*(i+1))-1:3*i]),
+ .m_axi_arburst (m_axi_arburst[(2*(i+1))-1:2*i]),
+ .m_axi_arlock (m_axi_arlock[i]),
+ .m_axi_arcache (m_axi_arcache[(4*(i+1))-1:4*i]),
+ .m_axi_arprot (m_axi_arprot[(3*(i+1))-1:3*i]),
+ .m_axi_arqos (m_axi_arqos[(4*(i+1))-1:4*i]),
+ .m_axi_arregion (m_axi_arregion[(4*(i+1))-1:4*i]),
+ .m_axi_aruser (m_axi_aruser[i]),
+ .m_axi_arvalid (m_axi_arvalid[i]),
+ .m_axi_arready (m_axi_arready[i]),
+ //
+ // AXI Read data channel
+ .m_axi_rid (m_axi_rid[i]),
+ .m_axi_rdata (m_axi_rdata[(MEM_DATA_W*(i+1))-1:MEM_DATA_W*i]),
+ .m_axi_rresp (m_axi_rresp[(2*(i+1))-1:2*i]),
+ .m_axi_rlast (m_axi_rlast[i]),
+ .m_axi_ruser (m_axi_ruser[i]),
+ .m_axi_rvalid (m_axi_rvalid[i]),
+ .m_axi_rready (m_axi_rready[i])
+ );
+
+ end
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_all_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_all_tb.sv
new file mode 100644
index 000000000..575c600f9
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_all_tb.sv
@@ -0,0 +1,70 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_axi_ram_fifo_all_tb
+//
+// Description:
+//
+// This is the testbench for rfnoc_block_axi_ram_fifo that instantiates
+// several variations of rfnoc_block_axi_ram_fifo_tb to test different
+// configurations.
+//
+
+
+module rfnoc_block_axi_ram_fifo_all_tb;
+
+ timeunit 1ns;
+ timeprecision 1ps;
+
+ import PkgTestExec::*;
+
+
+ //---------------------------------------------------------------------------
+ // Test Definitions
+ //---------------------------------------------------------------------------
+
+ typedef struct {
+ int CHDR_W;
+ int NUM_PORTS;
+ int MEM_DATA_W;
+ int MEM_ADDR_W;
+ int FIFO_ADDR_W;
+ int IN_FIFO_SIZE;
+ int OUT_FIFO_SIZE;
+ bit OVERFLOW;
+ bit BIST;
+ } test_config_t;
+
+ localparam NUM_TESTS = 4;
+
+ localparam test_config_t test[NUM_TESTS] = '{
+ '{CHDR_W: 64, NUM_PORTS: 2, MEM_DATA_W: 64, MEM_ADDR_W: 13, FIFO_ADDR_W: 12, IN_FIFO_SIZE: 9, OUT_FIFO_SIZE: 9, OVERFLOW: 1, BIST: 1 },
+ '{CHDR_W: 64, NUM_PORTS: 1, MEM_DATA_W: 128, MEM_ADDR_W: 14, FIFO_ADDR_W: 13, IN_FIFO_SIZE: 9, OUT_FIFO_SIZE: 9, OVERFLOW: 1, BIST: 1 },
+ '{CHDR_W: 128, NUM_PORTS: 1, MEM_DATA_W: 64, MEM_ADDR_W: 13, FIFO_ADDR_W: 12, IN_FIFO_SIZE: 9, OUT_FIFO_SIZE: 10, OVERFLOW: 0, BIST: 1 },
+ '{CHDR_W: 128, NUM_PORTS: 1, MEM_DATA_W: 128, MEM_ADDR_W: 16, FIFO_ADDR_W: 14, IN_FIFO_SIZE: 12, OUT_FIFO_SIZE: 12, OVERFLOW: 0, BIST: 0 }
+ };
+
+
+ //---------------------------------------------------------------------------
+ // DUT Instances
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ for (i = 0; i < NUM_TESTS; i++) begin : gen_test_config
+ rfnoc_block_axi_ram_fifo_tb #(
+ .CHDR_W (test[i].CHDR_W),
+ .NUM_PORTS (test[i].NUM_PORTS),
+ .MEM_DATA_W (test[i].MEM_DATA_W),
+ .MEM_ADDR_W (test[i].MEM_ADDR_W),
+ .FIFO_ADDR_W (test[i].FIFO_ADDR_W),
+ .IN_FIFO_SIZE (test[i].IN_FIFO_SIZE),
+ .OUT_FIFO_SIZE (test[i].OUT_FIFO_SIZE),
+ .OVERFLOW (test[i].OVERFLOW),
+ .BIST (test[i].BIST)
+ ) rfnoc_block_radio_tb_i ();
+ end : gen_test_config
+
+
+endmodule : rfnoc_block_axi_ram_fifo_all_tb \ No newline at end of file
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_tb.sv
new file mode 100644
index 000000000..49e184ce0
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_tb.sv
@@ -0,0 +1,1114 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_axi_ram_fifo_tb
+//
+// Description: Testbench for rfnoc_block_axi_ram_fifo
+//
+
+
+module rfnoc_block_axi_ram_fifo_tb #(
+ parameter int CHDR_W = 64,
+ parameter int NUM_PORTS = 2,
+ parameter int MEM_DATA_W = 64,
+ parameter int MEM_ADDR_W = 13,
+ parameter int FIFO_ADDR_W = 12,
+ parameter int IN_FIFO_SIZE = 9,
+ parameter int OUT_FIFO_SIZE = 9,
+ parameter bit OVERFLOW = 1,
+ parameter bit BIST = 1
+);
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+
+ `include "axi_ram_fifo_regs.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Simulation parameters
+ localparam int USE_RANDOM = 1; // Use random simulation data (not sequential)
+ localparam real CHDR_CLK_PER = 5.333333; // CHDR clock rate
+ localparam real CTRL_CLK_PER = 10.0; // CTRL clock rate
+ localparam real MEM_CLK_PER = 3.333333; // Memory clock rate
+ localparam int SPP = 256; // Samples per packet
+ localparam int PKT_SIZE_BYTES = SPP*4; // Bytes per packet
+ localparam int STALL_PROB = 25; // BFM stall probability
+
+ // Block configuration
+ localparam int THIS_PORTID = 'h123;
+ localparam int MTU = 12;
+ localparam int NUM_HB = 3;
+ localparam int CIC_MAX_DECIM = 255;
+ localparam int BURST_TIMEOUT = 64;
+ localparam int MEM_CLK_RATE = int'(1.0e9/MEM_CLK_PER); // Frequency in Hz
+ localparam int AWIDTH = MEM_ADDR_W+1;
+
+ // Put FIFO 0 at the bottom of the memory and FIFO 1 immediately above it.
+ localparam bit [MEM_ADDR_W-1:0] FIFO_ADDR_BASE_0 = 0;
+ localparam bit [MEM_ADDR_W-1:0] FIFO_ADDR_BASE_1 = 2**FIFO_ADDR_W;
+ localparam bit [MEM_ADDR_W-1:0] FIFO_ADDR_MASK = 2**FIFO_ADDR_W-1;
+
+
+ //---------------------------------------------------------------------------
+ // Clocks
+ //---------------------------------------------------------------------------
+
+ bit rfnoc_chdr_clk;
+ bit rfnoc_ctrl_clk;
+ bit mem_clk, mem_rst;
+
+ // Don't start the clocks automatically (AUTOSTART=0), since we expect
+ // multiple instances of this testbench to run in sequence. They will be
+ // started before the first test.
+ sim_clock_gen #(.PERIOD(CHDR_CLK_PER), .AUTOSTART(0))
+ rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
+ sim_clock_gen #(.PERIOD(CTRL_CLK_PER), .AUTOSTART(0))
+ rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst());
+ sim_clock_gen #(.PERIOD(MEM_CLK_PER), .AUOSTART(0))
+ mem_clk_gen (.clk(mem_clk), .rst(mem_rst));
+
+
+ //---------------------------------------------------------------------------
+ // Bus Functional Models
+ //---------------------------------------------------------------------------
+
+ RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk);
+ AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+
+ // Bus functional model for a software block controller
+ RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
+ new(backend, m_ctrl, s_ctrl);
+
+ // Connect block controller to BFMs
+ for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
+ initial begin
+ blk_ctrl.connect_master_data_port(i, m_chdr[i], PKT_SIZE_BYTES);
+ blk_ctrl.connect_slave_data_port(i, s_chdr[i]);
+ blk_ctrl.set_master_stall_prob(i, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(i, STALL_PROB);
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // AXI Memory Model
+ //---------------------------------------------------------------------------
+
+ // AXI Write Address Channel
+ wire [ NUM_PORTS*1-1:0] m_axi_awid;
+ wire [ NUM_PORTS*AWIDTH-1:0] m_axi_awaddr;
+ wire [ NUM_PORTS*8-1:0] m_axi_awlen;
+ wire [ NUM_PORTS*3-1:0] m_axi_awsize;
+ wire [ NUM_PORTS*2-1:0] m_axi_awburst;
+ wire [ NUM_PORTS*1-1:0] m_axi_awlock; // Unused master output
+ wire [ NUM_PORTS*4-1:0] m_axi_awcache; // Unused master output
+ wire [ NUM_PORTS*3-1:0] m_axi_awprot; // Unused master output
+ wire [ NUM_PORTS*4-1:0] m_axi_awqos; // Unused master output
+ wire [ NUM_PORTS*4-1:0] m_axi_awregion; // Unused master output
+ wire [ NUM_PORTS*1-1:0] m_axi_awuser; // Unused master output
+ wire [ NUM_PORTS*1-1:0] m_axi_awvalid;
+ wire [ NUM_PORTS*1-1:0] m_axi_awready;
+ // AXI Write Data Channel
+ wire [ NUM_PORTS*MEM_DATA_W-1:0] m_axi_wdata;
+ wire [NUM_PORTS*MEM_DATA_W/8-1:0] m_axi_wstrb;
+ wire [ NUM_PORTS*1-1:0] m_axi_wlast;
+ wire [ NUM_PORTS*1-1:0] m_axi_wuser; // Unused master output
+ wire [ NUM_PORTS*1-1:0] m_axi_wvalid;
+ wire [ NUM_PORTS*1-1:0] m_axi_wready;
+ // AXI Write Response Channel
+ wire [ NUM_PORTS*1-1:0] m_axi_bid;
+ wire [ NUM_PORTS*2-1:0] m_axi_bresp;
+ wire [ NUM_PORTS*1-1:0] m_axi_buser; // Unused master input
+ wire [ NUM_PORTS*1-1:0] m_axi_bvalid;
+ wire [ NUM_PORTS*1-1:0] m_axi_bready;
+ // AXI Read Address Channel
+ wire [ NUM_PORTS*1-1:0] m_axi_arid;
+ wire [ NUM_PORTS*AWIDTH-1:0] m_axi_araddr;
+ wire [ NUM_PORTS*8-1:0] m_axi_arlen;
+ wire [ NUM_PORTS*3-1:0] m_axi_arsize;
+ wire [ NUM_PORTS*2-1:0] m_axi_arburst;
+ wire [ NUM_PORTS*1-1:0] m_axi_arlock; // Unused master output
+ wire [ NUM_PORTS*4-1:0] m_axi_arcache; // Unused master output
+ wire [ NUM_PORTS*3-1:0] m_axi_arprot; // Unused master output
+ wire [ NUM_PORTS*4-1:0] m_axi_arqos; // Unused master output
+ wire [ NUM_PORTS*4-1:0] m_axi_arregion; // Unused master output
+ wire [ NUM_PORTS*1-1:0] m_axi_aruser; // Unused master output
+ wire [ NUM_PORTS*1-1:0] m_axi_arvalid;
+ wire [ NUM_PORTS*1-1:0] m_axi_arready;
+ // AXI Read Data Channel
+ wire [ NUM_PORTS*1-1:0] m_axi_rid;
+ wire [NUM_PORTS*MEM_DATA_W-1:0] m_axi_rdata;
+ wire [ NUM_PORTS*2-1:0] m_axi_rresp;
+ wire [ NUM_PORTS*1-1:0] m_axi_rlast;
+ wire [ NUM_PORTS*1-1:0] m_axi_ruser; // Unused master input
+ wire [ NUM_PORTS*1-1:0] m_axi_rvalid;
+ wire [ NUM_PORTS*1-1:0] m_axi_rready;
+
+ // Unused master input signals
+ assign m_axi_buser = {NUM_PORTS{1'b0}};
+ assign m_axi_ruser = {NUM_PORTS{1'b0}};
+
+ for (genvar i = 0; i < NUM_PORTS; i = i+1) begin : gen_sim_axi_ram
+ sim_axi_ram #(
+ .AWIDTH (AWIDTH),
+ .DWIDTH (MEM_DATA_W),
+ .IDWIDTH (1),
+ .BIG_ENDIAN (0),
+ .STALL_PROB (STALL_PROB)
+ ) sim_axi_ram_i (
+ .s_aclk (mem_clk),
+ .s_aresetn (~mem_rst),
+ .s_axi_awid (m_axi_awid[i]),
+ .s_axi_awaddr (m_axi_awaddr[i*AWIDTH +: AWIDTH]),
+ .s_axi_awlen (m_axi_awlen[i*8 +: 8]),
+ .s_axi_awsize (m_axi_awsize[i*3 +: 3]),
+ .s_axi_awburst (m_axi_awburst[i*2 +: 2]),
+ .s_axi_awvalid (m_axi_awvalid[i]),
+ .s_axi_awready (m_axi_awready[i]),
+ .s_axi_wdata (m_axi_wdata[i*MEM_DATA_W +: MEM_DATA_W]),
+ .s_axi_wstrb (m_axi_wstrb[i*(MEM_DATA_W/8) +: (MEM_DATA_W/8)]),
+ .s_axi_wlast (m_axi_wlast[i]),
+ .s_axi_wvalid (m_axi_wvalid[i]),
+ .s_axi_wready (m_axi_wready[i]),
+ .s_axi_bid (m_axi_bid[i]),
+ .s_axi_bresp (m_axi_bresp[i*2 +: 2]),
+ .s_axi_bvalid (m_axi_bvalid[i]),
+ .s_axi_bready (m_axi_bready[i]),
+ .s_axi_arid (m_axi_arid[i]),
+ .s_axi_araddr (m_axi_araddr[i*AWIDTH +: AWIDTH]),
+ .s_axi_arlen (m_axi_arlen[i*8 +: 8]),
+ .s_axi_arsize (m_axi_arsize[i*3 +: 3]),
+ .s_axi_arburst (m_axi_arburst[i*2 +: 2]),
+ .s_axi_arvalid (m_axi_arvalid[i]),
+ .s_axi_arready (m_axi_arready[i]),
+ .s_axi_rid (m_axi_rid[i]),
+ .s_axi_rdata (m_axi_rdata[i*MEM_DATA_W +: MEM_DATA_W]),
+ .s_axi_rresp (m_axi_rresp[i*2 +: 2]),
+ .s_axi_rlast (m_axi_rlast[i]),
+ .s_axi_rvalid (m_axi_rvalid[i]),
+ .s_axi_rready (m_axi_rready[i])
+ );
+ end
+
+
+ //---------------------------------------------------------------------------
+ // DUT
+ //---------------------------------------------------------------------------
+
+ logic [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tready;
+
+ logic [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tready;
+
+ // Map the array of BFMs to a flat vector for the DUT
+ for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_dut_connections
+ // Connect BFM master to DUT slave port
+ assign s_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata;
+ assign s_rfnoc_chdr_tlast[i] = m_chdr[i].tlast;
+ assign s_rfnoc_chdr_tvalid[i] = m_chdr[i].tvalid;
+ assign m_chdr[i].tready = s_rfnoc_chdr_tready[i];
+
+ // Connect BFM slave to DUT master port
+ assign s_chdr[i].tdata = m_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W];
+ assign s_chdr[i].tlast = m_rfnoc_chdr_tlast[i];
+ assign s_chdr[i].tvalid = m_rfnoc_chdr_tvalid[i];
+ assign m_rfnoc_chdr_tready[i] = s_chdr[i].tready;
+ end
+
+ rfnoc_block_axi_ram_fifo #(
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .NUM_PORTS (NUM_PORTS),
+ .MTU (MTU),
+ .MEM_DATA_W (MEM_DATA_W),
+ .MEM_ADDR_W (MEM_ADDR_W),
+ .AWIDTH (AWIDTH),
+ .FIFO_ADDR_BASE ({ FIFO_ADDR_BASE_1, FIFO_ADDR_BASE_0 }),
+ .FIFO_ADDR_MASK ({NUM_PORTS{FIFO_ADDR_MASK}}),
+ .BURST_TIMEOUT ({NUM_PORTS{BURST_TIMEOUT}}),
+ .IN_FIFO_SIZE (IN_FIFO_SIZE),
+ .OUT_FIFO_SIZE (OUT_FIFO_SIZE),
+ .BIST (BIST),
+ .MEM_CLK_RATE (MEM_CLK_RATE)
+ ) rfnoc_block_axi_ram_fifo_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .rfnoc_core_config (backend.cfg),
+ .rfnoc_core_status (backend.sts),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .s_rfnoc_ctrl_tdata (m_ctrl.tdata),
+ .s_rfnoc_ctrl_tlast (m_ctrl.tlast),
+ .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid),
+ .s_rfnoc_ctrl_tready (m_ctrl.tready),
+ .m_rfnoc_ctrl_tdata (s_ctrl.tdata),
+ .m_rfnoc_ctrl_tlast (s_ctrl.tlast),
+ .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid),
+ .m_rfnoc_ctrl_tready (s_ctrl.tready),
+ .mem_clk (mem_clk),
+ .axi_rst (mem_rst),
+ .m_axi_awid (m_axi_awid),
+ .m_axi_awaddr (m_axi_awaddr),
+ .m_axi_awlen (m_axi_awlen),
+ .m_axi_awsize (m_axi_awsize),
+ .m_axi_awburst (m_axi_awburst),
+ .m_axi_awlock (m_axi_awlock),
+ .m_axi_awcache (m_axi_awcache),
+ .m_axi_awprot (m_axi_awprot),
+ .m_axi_awqos (m_axi_awqos),
+ .m_axi_awregion (m_axi_awregion),
+ .m_axi_awuser (m_axi_awuser),
+ .m_axi_awvalid (m_axi_awvalid),
+ .m_axi_awready (m_axi_awready),
+ .m_axi_wdata (m_axi_wdata),
+ .m_axi_wstrb (m_axi_wstrb),
+ .m_axi_wlast (m_axi_wlast),
+ .m_axi_wuser (m_axi_wuser),
+ .m_axi_wvalid (m_axi_wvalid),
+ .m_axi_wready (m_axi_wready),
+ .m_axi_bid (m_axi_bid),
+ .m_axi_bresp (m_axi_bresp),
+ .m_axi_buser (m_axi_buser),
+ .m_axi_bvalid (m_axi_bvalid),
+ .m_axi_bready (m_axi_bready),
+ .m_axi_arid (m_axi_arid),
+ .m_axi_araddr (m_axi_araddr),
+ .m_axi_arlen (m_axi_arlen),
+ .m_axi_arsize (m_axi_arsize),
+ .m_axi_arburst (m_axi_arburst),
+ .m_axi_arlock (m_axi_arlock),
+ .m_axi_arcache (m_axi_arcache),
+ .m_axi_arprot (m_axi_arprot),
+ .m_axi_arqos (m_axi_arqos),
+ .m_axi_arregion (m_axi_arregion),
+ .m_axi_aruser (m_axi_aruser),
+ .m_axi_arvalid (m_axi_arvalid),
+ .m_axi_arready (m_axi_arready),
+ .m_axi_rid (m_axi_rid),
+ .m_axi_rdata (m_axi_rdata),
+ .m_axi_rresp (m_axi_rresp),
+ .m_axi_rlast (m_axi_rlast),
+ .m_axi_ruser (m_axi_ruser),
+ .m_axi_rvalid (m_axi_rvalid),
+ .m_axi_rready (m_axi_rready)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Helper Tasks
+ //---------------------------------------------------------------------------
+
+ task automatic write_reg(int port, bit [19:0] addr, bit [31:0] value);
+ blk_ctrl.reg_write((2**RAM_FIFO_ADDR_W)*port + addr, value);
+ endtask : write_reg
+
+ task automatic write_reg_64(int port, bit [19:0] addr, bit [63:0] value);
+ blk_ctrl.reg_write((2**RAM_FIFO_ADDR_W)*port + addr + 0, value[31: 0]);
+ blk_ctrl.reg_write((2**RAM_FIFO_ADDR_W)*port + addr + 4, value[63:32]);
+ endtask : write_reg_64
+
+ task automatic read_reg(int port, bit [19:0] addr, output logic [63:0] value);
+ blk_ctrl.reg_read((2**RAM_FIFO_ADDR_W)*port + addr, value[31: 0]);
+ endtask : read_reg
+
+ task automatic read_reg_64(int port, bit [19:0] addr, output logic [63:0] value);
+ blk_ctrl.reg_read((2**RAM_FIFO_ADDR_W)*port + addr + 0, value[31: 0]);
+ blk_ctrl.reg_read((2**RAM_FIFO_ADDR_W)*port + addr + 4, value[63:32]);
+ endtask : read_reg_64
+
+
+ // Generate a unique sequence of incrementing numbers
+ task automatic gen_test_data(int num_bytes, output chdr_word_t queue[$]);
+ int num_chdr_words;
+ chdr_word_t val64;
+
+ // Calculate the number of chdr_word_t size words
+ num_chdr_words = int'($ceil(real'(num_bytes) / ($bits(chdr_word_t) / 8)));
+
+ for (int i = 0; i < num_chdr_words; i++) begin
+ if (USE_RANDOM) begin
+ val64 = { $urandom(), $urandom() }; // Random data, for more rigorous testing
+ end else begin
+ val64 = i; // Sequential data, for easier debugging
+ end
+ queue.push_back(val64);
+ end
+ endtask : gen_test_data
+
+
+ //---------------------------------------------------------------------------
+ // Reset
+ //---------------------------------------------------------------------------
+
+ task test_reset();
+ test.start_test("Wait for Reset", 10us);
+ mem_clk_gen.reset();
+ blk_ctrl.flush_and_reset();
+ wait(!mem_rst);
+ test.end_test();
+ endtask : test_reset
+
+
+ //---------------------------------------------------------------------------
+ // Check NoC ID and Block Info
+ //---------------------------------------------------------------------------
+
+ task test_block_info();
+ test.start_test("Verify Block Info", 2us);
+ `ASSERT_ERROR(blk_ctrl.get_noc_id() == rfnoc_block_axi_ram_fifo_i.NOC_ID, "Incorrect NOC_ID Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect NUM_DATA_I Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect NUM_DATA_O Value");
+ `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value");
+ test.end_test();
+ endtask : test_block_info
+
+
+ //---------------------------------------------------------------------------
+ // Check Unused Signals
+ //---------------------------------------------------------------------------
+
+ task test_unused();
+ test.start_test("Check unused/static signals");
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ `ASSERT_ERROR(m_axi_awlock [port*1 +: 1] == 1'b0, "m_axi_awlock value unexpected");
+ `ASSERT_ERROR(m_axi_awcache [port*4 +: 4] == 4'hF, "m_axi_awcache value unexpected");
+ `ASSERT_ERROR(m_axi_awprot [port*3 +: 3] == 3'h2, "m_axi_awprot value unexpected");
+ `ASSERT_ERROR(m_axi_awqos [port*4 +: 4] == 4'h0, "m_axi_awqos value unexpected");
+ `ASSERT_ERROR(m_axi_awregion [port*4 +: 4] == 4'h0, "m_axi_awregion value unexpected");
+ `ASSERT_ERROR(m_axi_awuser [port*1 +: 1] == 1'b0, "m_axi_awuser value unexpected");
+ //
+ `ASSERT_ERROR(m_axi_wuser [port*1 +: 1] == 1'b0, "m_axi_wuser value unexpected");
+ //
+ `ASSERT_ERROR(m_axi_arlock [port*1 +: 1] == 1'b0, "m_axi_arlock value unexpected");
+ `ASSERT_ERROR(m_axi_arcache [port*4 +: 4] == 4'hF, "m_axi_arcache value unexpected");
+ `ASSERT_ERROR(m_axi_arprot [port*3 +: 3] == 3'h2, "m_axi_arprot value unexpected");
+ `ASSERT_ERROR(m_axi_arqos [port*4 +: 4] == 4'h0, "m_axi_arqos value unexpected");
+ `ASSERT_ERROR(m_axi_arregion [port*4 +: 4] == 4'h0, "m_axi_arregion value unexpected");
+ `ASSERT_ERROR(m_axi_aruser [port*1 +: 1] == 1'b0, "m_axi_aruser value unexpected");
+ end
+ test.end_test();
+ endtask : test_unused
+
+
+ //---------------------------------------------------------------------------
+ // Test Registers
+ //---------------------------------------------------------------------------
+
+ task test_registers();
+ logic [63:0] val64, expected64, temp64;
+ logic [31:0] val32, expected32, temp32;
+
+ test.start_test("Test registers", 50us);
+
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ //
+ // REG_FIFO_INFO
+ //
+ expected32 = 0;
+ expected32[REG_FIFO_MAGIC_POS +: REG_FIFO_MAGIC_W] = 16'hF1F0;
+ expected32[REG_FIFO_BIST_PRSNT_POS] = (BIST != 0);
+ read_reg(port, REG_FIFO_INFO, val32);
+ `ASSERT_ERROR(val32 == expected32, "Initial value for REG_FIFO_INFO is not correct");
+
+ //
+ // REG_FIFO_READ_SUPPRESS
+ //
+ expected32 = 0;
+ expected32[REG_FIFO_IN_FIFO_SIZE_POS+:REG_FIFO_IN_FIFO_SIZE_W] = IN_FIFO_SIZE;
+ expected32[REG_FIFO_SUPPRESS_THRESH_POS+:REG_FIFO_SUPPRESS_THRESH_W] = 0;
+ read_reg(port, REG_FIFO_READ_SUPPRESS, val32);
+ `ASSERT_ERROR(val32 == expected32, "Initial value for REG_FIFO_READ_SUPPRESS is not correct");
+
+ temp32 = expected32;
+ expected32[REG_FIFO_SUPPRESS_THRESH_POS+:REG_FIFO_SUPPRESS_THRESH_W] =
+ ~val32[REG_FIFO_SUPPRESS_THRESH_POS+:REG_FIFO_SUPPRESS_THRESH_W];
+ write_reg(port, REG_FIFO_READ_SUPPRESS, expected32);
+ read_reg(port, REG_FIFO_READ_SUPPRESS, val32);
+ `ASSERT_ERROR(val32 == expected32, "REG_FIFO_READ_SUPPRESS did not update");
+
+ expected32 = temp32;
+ write_reg(port, REG_FIFO_READ_SUPPRESS, expected32);
+ read_reg(port, REG_FIFO_READ_SUPPRESS, val32);
+ `ASSERT_ERROR(val32 == expected32, "REG_FIFO_READ_SUPPRESS did not reset");
+
+ //
+ // REG_FIFO_MEM_SIZE
+ //
+ expected32 = 0;
+ expected32[REG_FIFO_DATA_SIZE_POS +: REG_FIFO_DATA_SIZE_W] = MEM_DATA_W;
+ expected32[REG_FIFO_ADDR_SIZE_POS +: REG_FIFO_ADDR_SIZE_W] = MEM_ADDR_W;
+ read_reg(port, REG_FIFO_MEM_SIZE, val32);
+ `ASSERT_ERROR(val32 == expected32, "Incorrect REG_FIFO_MEM_SIZE value!");
+
+ //
+ // REG_FIFO_TIMEOUT
+ //
+ expected32 = BURST_TIMEOUT;
+ read_reg(port, REG_FIFO_TIMEOUT, val32);
+ `ASSERT_ERROR(val32 == expected32, "Initial value for REG_FIFO_TIMEOUT is not correct");
+
+ write_reg(port, REG_FIFO_TIMEOUT, {REG_TIMEOUT_W{1'b1}});
+ read_reg(port, REG_FIFO_TIMEOUT, val32);
+ `ASSERT_ERROR(val32 == {REG_TIMEOUT_W{1'b1}}, "REG_FIFO_TIMEOUT did not update");
+
+ write_reg(port, REG_FIFO_TIMEOUT, expected32);
+ read_reg(port, REG_FIFO_TIMEOUT, val32);
+ `ASSERT_ERROR(val32 == expected32, "REG_FIFO_TIMEOUT did not reset");
+
+ //
+ // REG_FIFO_FULLNESS
+ //
+ read_reg_64(port, REG_FIFO_FULLNESS_LO, val64);
+ `ASSERT_ERROR(val64 == 0, "Incorrect REG_FIFO_FULLNESS value!");
+
+ //
+ // REG_FIFO_ADDR_BASE
+ //
+ expected64 = port * 2**FIFO_ADDR_W;
+ read_reg_64(port, REG_FIFO_ADDR_BASE_LO, val64);
+ `ASSERT_ERROR(val64 == expected64, "Initial value for REG_FIFO_ADDR_BASE is not correct");
+
+ write_reg_64(port, REG_FIFO_ADDR_BASE_LO, {MEM_ADDR_W{1'b1}});
+ read_reg_64(port, REG_FIFO_ADDR_BASE_LO, val64);
+ `ASSERT_ERROR(val64 == {MEM_ADDR_W{1'b1}}, "REG_FIFO_ADDR_BASE did not update");
+
+ write_reg_64(port, REG_FIFO_ADDR_BASE_LO, expected64);
+ read_reg_64(port, REG_FIFO_ADDR_BASE_LO, val64);
+ `ASSERT_ERROR(val64 == expected64, "REG_FIFO_ADDR_BASE did not reset");
+
+ //
+ // REG_FIFO_ADDR_MASK
+ //
+ expected64 = {FIFO_ADDR_W{1'b1}};
+ read_reg_64(port, REG_FIFO_ADDR_MASK_LO, val64);
+ `ASSERT_ERROR(val64 == expected64, "Initial value for REG_FIFO_ADDR_MASK_LO is not correct");
+
+ // Set to the max value
+ write_reg_64(port, REG_FIFO_ADDR_MASK_LO, {MEM_ADDR_W{1'b1}});
+ read_reg_64(port, REG_FIFO_ADDR_MASK_LO, val64);
+ `ASSERT_ERROR(val64 == {MEM_ADDR_W{1'b1}}, "REG_FIFO_ADDR_MASK_LO did not update");
+
+ // Set to the min value
+ write_reg_64(port, REG_FIFO_ADDR_MASK_LO, 0);
+ read_reg_64(port, REG_FIFO_ADDR_MASK_LO, val64);
+ // Coerce to the minimum allowed value
+ temp64 = rfnoc_block_axi_ram_fifo_i.gen_ram_fifos[0].axi_ram_fifo_i.FIFO_ADDR_MASK_MIN;
+ `ASSERT_ERROR(val64 == temp64, "REG_FIFO_ADDR_MASK_LO did not update");
+
+ write_reg_64(port, REG_FIFO_ADDR_MASK_LO, expected64);
+ read_reg_64(port, REG_FIFO_ADDR_MASK_LO, val64);
+ `ASSERT_ERROR(val64 == expected64, "REG_FIFO_ADDR_MASK_LO did not reset");
+
+ //
+ // REG_FIFO_PACKET_CNT
+ //
+ read_reg(port, REG_FIFO_PACKET_CNT, val32);
+ `ASSERT_ERROR(val32 == 0, "Incorrect REG_FIFO_PACKET_CNT value!");
+
+ if (BIST) begin
+ read_reg(port, REG_BIST_CTRL, val32);
+ `ASSERT_ERROR(val32 == 0, "Initial value for REG_BIST_CTRL is not correct");
+ read_reg(port, REG_BIST_CLK_RATE, val32);
+ `ASSERT_ERROR(val32 == MEM_CLK_RATE, "Initial value for REG_BIST_CLK_RATE is not correct");
+ read_reg_64(port, REG_BIST_NUM_BYTES_LO, val64);
+ `ASSERT_ERROR(val64 == 0, "Initial value for REG_BIST_NUM_BYTES is not correct");
+ read_reg_64(port, REG_BIST_TX_BYTE_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == 0, "Initial value for REG_BIST_TX_BYTE_COUNT is not correct");
+ read_reg_64(port, REG_BIST_RX_BYTE_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == 0, "Initial value for REG_BIST_RX_BYTE_COUNT is not correct");
+ read_reg_64(port, REG_BIST_ERROR_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == 0, "Initial value for REG_BIST_ERROR_COUNT is not correct");
+ read_reg_64(port, REG_BIST_CYCLE_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == 0, "Initial value for REG_BIST_CYCLE_COUNT is not correct");
+ end
+ end
+
+ test.end_test();
+ endtask : test_registers
+
+
+ //---------------------------------------------------------------------------
+ // Basic Test
+ //---------------------------------------------------------------------------
+ //
+ // Push a few packets through each FIFO.
+ //
+ //---------------------------------------------------------------------------
+
+ task test_basic();
+ logic [31:0] val32;
+
+ test.start_test("Basic test", NUM_PORTS*20us);
+
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ chdr_word_t test_data[$];
+ logic [63:0] val64;
+ timeout_t timeout;
+
+ // Generate the test data to send
+ gen_test_data(PKT_SIZE_BYTES*3, test_data);
+
+ // Queue up the packets to send
+ blk_ctrl.send_packets(port, test_data);
+
+ // Make sure fullness increases
+ test.start_timeout(timeout, 4us,
+ $sformatf("Waiting for fullness to increase on port %0d", port));
+ forever begin
+ read_reg_64(port, REG_FIFO_FULLNESS_LO, val64);
+ if (val64 != 0) break;
+ end
+ test.end_timeout(timeout);
+
+ // Verify the data, one packet at a time
+ for (int count = 0; count < test_data.size(); ) begin
+ chdr_word_t recv_data[$];
+ int data_bytes;
+ blk_ctrl.recv(port, recv_data, data_bytes);
+
+ `ASSERT_ERROR(
+ data_bytes == PKT_SIZE_BYTES,
+ "Length didn't match expected value"
+ );
+
+ for (int i = 0; i < recv_data.size(); i++, count++) begin
+ if (recv_data[i] != test_data[count]) begin
+ $display("Expected %X, received %X on port %0d", test_data[count], recv_data[i], port);
+ end
+ `ASSERT_ERROR(
+ recv_data[i] == test_data[count],
+ "Received data doesn't match expected value"
+ );
+ end
+ end
+
+ // Make sure the packet count updated
+ read_reg(port, REG_FIFO_PACKET_CNT, val32);
+ `ASSERT_ERROR(val32 > 0, "REG_FIFO_PACKET_CNT didn't update");
+ end
+
+ test.end_test();
+ endtask : test_basic
+
+
+ //---------------------------------------------------------------------------
+ // Single Byte Test
+ //---------------------------------------------------------------------------
+
+ task test_single_byte();
+ test.start_test("Single byte test", 20us);
+
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ chdr_word_t test_data[$];
+ chdr_word_t recv_data[$];
+ int data_bytes;
+
+ gen_test_data(1, test_data);
+
+ blk_ctrl.send(port, test_data, 1);
+ blk_ctrl.recv(port, recv_data, data_bytes);
+
+ `ASSERT_ERROR(
+ data_bytes == 1 && recv_data.size() == CHDR_W/$bits(chdr_word_t),
+ "Length didn't match expected value"
+ );
+ `ASSERT_ERROR(
+ recv_data[0][7:0] == test_data[0][7:0],
+ "Received data doesn't match expected value"
+ );
+ end
+
+ test.end_test();
+ endtask : test_single_byte
+
+
+ //---------------------------------------------------------------------------
+ // Test Overflow
+ //---------------------------------------------------------------------------
+ //
+ // Fill the FIFO on both ports to make sure if fills correctly and flow
+ // control works correct at the limits.
+ //
+ //---------------------------------------------------------------------------
+
+ task test_overflow();
+ chdr_word_t test_data[NUM_PORTS][$];
+ int num_bytes, num_words;
+ bit [NUM_PORTS-1:0] full_bits;
+ logic [63:0] val64;
+ timeout_t timeout;
+ realtime start_time;
+
+ if (!OVERFLOW) return;
+
+ num_bytes = (MEM_DATA_W/8) * (2**IN_FIFO_SIZE + 2**OUT_FIFO_SIZE) + 2**MEM_ADDR_W;
+ num_bytes = num_bytes * 3 / 2;
+ num_words = num_bytes / (CHDR_W/8);
+
+ test.start_test("Overflow test", 10 * num_words * CHDR_CLK_PER);
+
+ // Stall the output of each FIFO, allow unrestricted input
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ blk_ctrl.set_master_stall_prob(port, 0);
+ blk_ctrl.set_slave_stall_prob(port, 100);
+ end
+
+ // Input more packets into each FIFO than they can fit
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ gen_test_data(num_bytes, test_data[port]);
+ blk_ctrl.send_packets(port, test_data[port]);
+ end
+
+ // Wait for both inputs to stall
+ test.start_timeout(timeout, (4 * num_words + 1000) * CHDR_CLK_PER,
+ $sformatf("Waiting for input to stall"));
+ full_bits = 0;
+ forever begin
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ full_bits[port] = ~s_rfnoc_chdr_tready[port];
+ if (!full_bits[port]) start_time = $realtime;
+ end
+
+ // Break as soon as all FIFOs have been stalled for 1000 clock cycles
+ if (full_bits == {NUM_PORTS{1'b1}} && $realtime-start_time > 1000 * CHDR_CLK_PER) break;
+ #(CHDR_CLK_PER*100);
+ end
+ test.end_timeout(timeout);
+
+ // Make sure all the FIFOs filled up
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ read_reg_64(port, REG_FIFO_FULLNESS_LO, val64);
+ // FIFO is full once it comes within 256 words of being full
+ `ASSERT_ERROR(val64 >= (2**FIFO_ADDR_W / (MEM_DATA_W/8)) - 256, "FIFO not reading full");
+ end
+
+ // Restore the input/output rates
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ blk_ctrl.set_master_stall_prob(port, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(port, STALL_PROB);
+ end
+
+ // Read out and verify the data
+ for (int port = 0; port < NUM_PORTS; port++) begin
+ for (int count = 0; count < test_data[port].size(); ) begin
+ chdr_word_t recv_data[$];
+ int data_bytes;
+ int expected_length;
+ blk_ctrl.recv(port, recv_data, data_bytes);
+
+ if (count*($bits(chdr_word_t)/8) + PKT_SIZE_BYTES <= num_bytes) begin
+ // Should be a full packet
+ expected_length = PKT_SIZE_BYTES;
+ end else begin
+ // Should be a partial packet
+ expected_length = num_bytes % PKT_SIZE_BYTES;
+ end
+
+ // Check the length
+ `ASSERT_ERROR(
+ data_bytes == expected_length,
+ "Length didn't match expected value"
+ );
+
+ for (int i = 0; i < recv_data.size(); i++, count++) begin
+ `ASSERT_ERROR(
+ recv_data[i] == test_data[port][count],
+ "Received data doesn't match expected value"
+ );
+ end
+ end
+ end
+
+ test.end_test();
+ endtask : test_overflow
+
+
+ //---------------------------------------------------------------------------
+ // Test Read Suppression
+ //---------------------------------------------------------------------------
+
+ task test_read_suppression();
+ chdr_word_t test_data[$];
+ logic [31:0] val32, save32;
+ int port;
+
+ test.start_test("Read suppression test", 100us);
+
+ port = 0; // Only test one port
+
+ // Turn on read suppression with the max threshold to cause it to
+ // suppress everything.
+ read_reg(port, REG_FIFO_READ_SUPPRESS, save32);
+ val32 = save32;
+ val32[REG_FIFO_SUPPRESS_THRESH_POS +: REG_FIFO_SUPPRESS_THRESH_W] = {REG_FIFO_SUPPRESS_THRESH_W{1'b1}};
+ write_reg(port, REG_FIFO_READ_SUPPRESS, val32);
+
+ // Generate the test data to send (send 8 RAM bursts)
+ gen_test_data(MEM_DATA_W/8 * 256 * 8, test_data);
+
+ // Start sending packets then wait for the input to stall, either because
+ // we've filled the FIFO or we've input everything.
+ blk_ctrl.set_master_stall_prob(port, 0);
+ blk_ctrl.send_packets(port, test_data);
+ wait (s_rfnoc_chdr_tvalid && s_rfnoc_chdr_tready);
+ wait (!s_rfnoc_chdr_tvalid || !s_rfnoc_chdr_tready);
+
+ // Make sure nothing made it through
+ `ASSERT_ERROR(blk_ctrl.num_received(port) == 0, "Read suppression failed");
+
+ // Turn down the threshold
+ val32[REG_FIFO_SUPPRESS_THRESH_POS +: REG_FIFO_SUPPRESS_THRESH_W] = {REG_FIFO_SUPPRESS_THRESH_W{1'b0}};
+ write_reg(port, REG_FIFO_READ_SUPPRESS, val32);
+
+ blk_ctrl.set_master_stall_prob(port, STALL_PROB);
+
+ // Verify the data, one packet at a time
+ for (int count = 0; count < test_data.size(); ) begin
+ chdr_word_t recv_data[$];
+ int data_bytes;
+ blk_ctrl.recv(port, recv_data, data_bytes);
+
+ for (int i = 0; i < recv_data.size(); i++, count++) begin
+ if (recv_data[i] != test_data[count]) begin
+ $display("Expected %X, received %X on port %0d", test_data[count], recv_data[i], port);
+ end
+ `ASSERT_ERROR(
+ recv_data[i] == test_data[count],
+ "Received data doesn't match expected value"
+ );
+ end
+ end
+
+ // Restore suppression settings
+ write_reg(port, REG_FIFO_READ_SUPPRESS, save32);
+
+ test.end_test();
+ endtask : test_read_suppression
+
+
+ //---------------------------------------------------------------------------
+ // Random Tests
+ //---------------------------------------------------------------------------
+ //
+ // Perform a series of random tests with different read/write probabilities
+ // test unexpected conditions.
+ //
+ //---------------------------------------------------------------------------
+
+ class RandTrans;
+ chdr_word_t data[$];
+ int num_bytes;
+ endclass;
+
+ task test_random();
+ localparam NUM_PACKETS = 256;
+
+ mailbox #(RandTrans) data_queue;
+ int port;
+
+ test.start_test("Random test", NUM_PACKETS * 2us);
+
+ data_queue = new();
+ port = 0; // Just check one port for this test
+
+ // Queue up a bunch of random packets
+ begin : data_gen
+ RandTrans trans;
+ $display("Generating %0d random packets...", NUM_PACKETS);
+
+ for (int packet_count = 0; packet_count < NUM_PACKETS; packet_count++) begin
+ trans = new();
+ trans.num_bytes = $urandom_range(1, PKT_SIZE_BYTES);
+ gen_test_data(trans.num_bytes, trans.data);
+ blk_ctrl.send(port, trans.data, trans.num_bytes);
+ data_queue.put(trans);
+ end
+ end
+
+ // Receive and check all the packets
+ fork
+ begin : stall_update
+ // Split the packets into four groups and use different stall
+ // behavior for each.
+ //
+ // 1. Start filling up the FIFO
+ // 2. Let it run for a while
+ // 3. Start emptying the FIFO
+ // 4. Let it run until all the data gets through
+ //
+ for (int i = 0; i < 4; i++) begin
+ case (i)
+ 0 : begin
+ $display("Test fast writer, slow reader");
+ blk_ctrl.set_master_stall_prob(port, 10);
+ blk_ctrl.set_slave_stall_prob(port, 80);
+ end
+ 1 : begin
+ $display("Test matched reader/writer");
+ blk_ctrl.set_master_stall_prob(port, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(port, STALL_PROB);
+ end
+ 2 : begin
+ $display("Test slow writer, fast reader");
+ blk_ctrl.set_master_stall_prob(port, 90);
+ blk_ctrl.set_slave_stall_prob(port, 10);
+ end
+ 3 : begin
+ $display("Test matched reader/writer");
+ blk_ctrl.set_master_stall_prob(port, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(port, STALL_PROB);
+ end
+ endcase
+
+ // Wait for a quarter of the packets to be accepted by the RAM FIFO
+ blk_ctrl.wait_complete(port, NUM_PACKETS/4);
+ end
+ end
+ begin : data_check
+ RandTrans trans;
+ chdr_word_t recv_data[$];
+ int num_bytes;
+ int num_words;
+
+
+ for (int packet_count = 0; packet_count < NUM_PACKETS; packet_count++) begin
+ //$display("Checking packet %0d/%0d...", packet_count, NUM_PACKETS);
+
+ blk_ctrl.recv(port, recv_data, num_bytes);
+ data_queue.get(trans);
+
+ // Check the length
+ `ASSERT_ERROR(
+ num_bytes == trans.num_bytes,
+ "Length didn't match expected value"
+ );
+
+ // If the generated data was an odd number of chdr_word_t words, we
+ // will get back an extra 0 word at the end. Calculate the correct
+ // number of words so that we ignore any extra at the end.
+ num_words = int'($ceil(real'(num_bytes)/($bits(chdr_word_t)/8)));
+ for (int i = 0; i < num_words; i++) begin
+ `ASSERT_ERROR(
+ recv_data[i] == trans.data[i],
+ "Received data doesn't match expected value"
+ );
+ end
+ end
+ end
+ join
+
+ test.end_test();
+ endtask : test_random
+
+
+ //---------------------------------------------------------------------------
+ // Test Clearing FIFO Block
+ //---------------------------------------------------------------------------
+
+ task test_clear();
+ test.start_test("FIFO clear test", 100us);
+
+ // TODO:
+ $warning("Need to write a test flushing and resetting the block!");
+
+ test.end_test();
+ endtask : test_clear
+
+
+ //---------------------------------------------------------------------------
+ // Test BIST
+ //---------------------------------------------------------------------------
+
+ task test_bist();
+ logic [31:0] val32;
+ logic [63:0] val64;
+ int port;
+ int num_bytes;
+
+ if (!BIST) return;
+
+ test.start_test("BIST test", 100us);
+
+ port = 0; // Test the first port
+ num_bytes = 2048;
+
+ // Start a test
+ write_reg(port, REG_BIST_CTRL, 1 << REG_BIST_CLEAR_POS);
+ write_reg(port, REG_BIST_NUM_BYTES_LO, num_bytes);
+ write_reg(port, REG_BIST_CTRL, 1 << REG_BIST_START_POS);
+
+ // Make sure running bit gets set
+ read_reg(port, REG_BIST_CTRL, val32);
+ `ASSERT_ERROR(val32[REG_BIST_RUNNING_POS] == 1'b1, "RUNNING bit not set");
+
+ // Wait for the test to complete
+ do begin
+ read_reg(port, REG_BIST_CTRL, val32);
+ end while(val32[REG_BIST_RUNNING_POS]);
+
+ // Check the results
+ read_reg_64(port, REG_BIST_TX_BYTE_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == num_bytes, "TX_BYTE_COUNT is not correct");
+ read_reg_64(port, REG_BIST_RX_BYTE_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == num_bytes, "RX_BYTE_COUNT is not correct");
+ read_reg_64(port, REG_BIST_ERROR_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == 0, "ERROR_COUNT is not zero");
+ read_reg_64(port, REG_BIST_CYCLE_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 > 0, "CYCLE_COUNT did not update");
+
+ // TODO:
+ $warning("BIST Continuous mode is NOT being tested");
+ $warning("BIST error insertion is NOT being tested (errors might be ignored)");
+
+ test.end_test();
+ endtask : test_bist
+
+
+ //---------------------------------------------------------------------------
+ // BIST Throughput Test
+ //---------------------------------------------------------------------------
+ //
+ // This test sanity-checks the values returned by the BIST. If run with the
+ // other BIST test, it also tests clearing the BIST counters.
+ //
+ //---------------------------------------------------------------------------
+
+ task test_bist_throughput();
+ localparam port = 0; // Test the first port
+ logic [31:0] val32;
+ logic [63:0] val64;
+ int num_bytes;
+ longint rx_byte_count;
+ longint cycle_count;
+ real throughput;
+ real efficiency;
+
+ if (!BIST) return;
+
+ test.start_test("BIST throughput test", 100us);
+
+ num_bytes = 64*1024;
+
+ // Reset the memory probability
+ gen_sim_axi_ram[port].sim_axi_ram_i.set_stall_prob(0);
+
+ // Start a test
+ write_reg(port, REG_BIST_CTRL, 1 << REG_BIST_CLEAR_POS);
+ write_reg(port, REG_BIST_NUM_BYTES_LO, num_bytes);
+ write_reg(port, REG_BIST_CTRL, 1 << REG_BIST_START_POS);
+
+ // Make sure running bit gets set
+ read_reg(port, REG_BIST_CTRL, val32);
+ `ASSERT_ERROR(val32[REG_BIST_RUNNING_POS] == 1'b1, "RUNNING bit not set");
+
+ // Wait for the test to complete
+ do begin
+ read_reg(port, REG_BIST_CTRL, val32);
+ end while(val32[REG_BIST_RUNNING_POS]);
+
+ // Check the results
+ read_reg_64(port, REG_BIST_TX_BYTE_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == num_bytes, "TX_BYTE_COUNT is not correct");
+ read_reg_64(port, REG_BIST_RX_BYTE_COUNT_LO, rx_byte_count);
+ `ASSERT_ERROR(rx_byte_count == num_bytes, "RX_BYTE_COUNT is not correct");
+ read_reg_64(port, REG_BIST_ERROR_COUNT_LO, val64);
+ `ASSERT_ERROR(val64 == 0, "ERROR_COUNT is not zero");
+ read_reg_64(port, REG_BIST_CYCLE_COUNT_LO, cycle_count);
+ `ASSERT_ERROR(cycle_count > 0, "CYCLE_COUNT did not update");
+
+ // Throughput = num_bytes / time = num_bytes / (num_cyles * period)
+ throughput = real'(rx_byte_count) / (real'(cycle_count) / real'(MEM_CLK_RATE));
+
+ // Efficiency is the actual throughput divided by the theoretical max. We
+ // use 0.5x in the calculation because we assume that the memory is a
+ // half-duplex read/write memory running at MEM_CLK_RATE, but we're
+ // measuring the full-duplex throughput.
+ efficiency = throughput / (0.5 * real'(MEM_CLK_RATE) * (MEM_DATA_W/8));
+
+ $display("BIST Throughput: %0.1f MB/s", throughput / 1.0e6);
+ $display("BIST Efficiency: %0.1f %%", efficiency * 100.0 );
+
+ `ASSERT_ERROR(efficiency > 0.95, "BIST efficiency was lower than expected");
+
+ // Restore the memory stall probability
+ gen_sim_axi_ram[port].sim_axi_ram_i.set_stall_prob(STALL_PROB);
+
+ test.end_test();
+ endtask;
+
+
+ //---------------------------------------------------------------------------
+ // Main Test Process
+ //---------------------------------------------------------------------------
+
+ initial begin : tb_main
+ const int port = 0;
+ string tb_name;
+
+ // Generate a string for the name of this instance of the testbench
+ tb_name = $sformatf(
+ "rfnoc_block_axi_ram_fifo_tb\nCHDR_W = %0D, NUM_PORTS = %0D, MEM_DATA_W = %0D, MEM_ADDR_W = %0D, FIFO_ADDR_W = %0D, IN_FIFO_SIZE = %0D, OUT_FIFO_SIZE = %0D, OVERFLOW = %0D, BIST = %0D",
+ CHDR_W, NUM_PORTS, MEM_DATA_W, MEM_ADDR_W, FIFO_ADDR_W, IN_FIFO_SIZE, OUT_FIFO_SIZE, OVERFLOW, BIST
+ );
+ test.start_tb(tb_name);
+
+ // Don't start the clocks until after start_tb() returns. This ensures that
+ // the clocks aren't toggling while other instances of this testbench are
+ // running, which speeds up simulation time.
+ rfnoc_chdr_clk_gen.start();
+ rfnoc_ctrl_clk_gen.start();
+ mem_clk_gen.start();
+
+ // Start the BFMs running
+ blk_ctrl.run();
+
+ //
+ // Run test procedures
+ //
+ test_reset();
+ test_block_info();
+ test_unused();
+ test_registers();
+ test_basic();
+ test_single_byte();
+ test_overflow();
+ test_read_suppression();
+ test_random();
+ test_clear();
+ test_bist();
+ test_bist_throughput();
+
+ // End the TB, but don't $finish, since we don't want to kill other
+ // instances of this testbench that may be running.
+ test.end_tb(0);
+
+ // Kill the clocks to end this instance of the testbench
+ rfnoc_chdr_clk_gen.kill();
+ rfnoc_ctrl_clk_gen.kill();
+ mem_clk_gen.kill();
+ end
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/sim_axi_ram.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/sim_axi_ram.sv
new file mode 100644
index 000000000..ee7ff5df8
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/sim_axi_ram.sv
@@ -0,0 +1,637 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: sim_axi_ram
+//
+// Description:
+//
+// Simulation model for a basic AXI4 memory mapped memory. A few notes on its
+// behavior:
+//
+// - This model does not reorder requests (regardless of WID/RID). All
+// requests are evaluated strictly in order.
+// - The only supported response is OKAY
+// - This model supports misaligned memory accesses, which cause a
+// simulation warning.
+// - A reset does not clear the memory contents
+// - The memory itself is implemented using an associative array (sparse
+// matrix) so that large memories can be supported.
+// - This model is half duplex, meaning read and write data transfers won't
+// happen at the same time. A new data transfer won't begin until the
+// previous one has completed.
+//
+
+module sim_axi_ram #(
+ parameter AWIDTH = 32,
+ parameter DWIDTH = 64,
+ parameter IDWIDTH = 2,
+ parameter BIG_ENDIAN = 0,
+ parameter STALL_PROB = 25
+) (
+ input logic s_aclk,
+ input logic s_aresetn,
+
+ // Write Address Channel
+ input logic [IDWIDTH-1:0] s_axi_awid,
+ input logic [ AWIDTH-1:0] s_axi_awaddr,
+ input logic [ 7:0] s_axi_awlen,
+ input logic [ 2:0] s_axi_awsize,
+ input logic [ 1:0] s_axi_awburst,
+ input logic s_axi_awvalid,
+ output logic s_axi_awready,
+
+ // Write Data Channel
+ input logic [ DWIDTH-1:0] s_axi_wdata,
+ input logic [DWIDTH/8-1:0] s_axi_wstrb,
+ input logic s_axi_wlast,
+ input logic s_axi_wvalid,
+ output logic s_axi_wready,
+
+ // Write Response Channel
+ output logic [IDWIDTH-1:0] s_axi_bid,
+ output logic [ 1:0] s_axi_bresp,
+ output logic s_axi_bvalid,
+ input logic s_axi_bready,
+
+ // Read Address Channel
+ input logic [IDWIDTH-1:0] s_axi_arid,
+ input logic [ AWIDTH-1:0] s_axi_araddr,
+ input logic [ 7:0] s_axi_arlen,
+ input logic [ 2:0] s_axi_arsize,
+ input logic [ 1:0] s_axi_arburst,
+ input logic s_axi_arvalid,
+ output logic s_axi_arready,
+
+ // Read Data Channel
+ output logic [ 0:0] s_axi_rid,
+ output logic [DWIDTH-1:0] s_axi_rdata,
+ output logic [ 1:0] s_axi_rresp,
+ output logic s_axi_rlast,
+ output logic s_axi_rvalid,
+ input logic s_axi_rready
+);
+
+ localparam DEBUG = 0;
+
+ //---------------------------------------------------------------------------
+ // Data Types
+ //---------------------------------------------------------------------------
+
+ typedef enum logic [1:0] { FIXED, INCR, WRAP } burst_t;
+ typedef enum logic [1:0] { OKAY, EXOKAY, SLVERR, DECERR } resp_t;
+
+ typedef struct packed {
+ longint count; // Number of requests to wait for before executing
+ logic [IDWIDTH-1:0] id;
+ logic [AWIDTH-1:0] addr;
+ logic [8:0] len; // Add an extra bit, since actual true length is +1
+ logic [7:0] size; // Add extra bits to store size in bytes, instead of clog2(size)
+ burst_t burst;
+ } req_t;
+
+ // Make the address type an extra bit wide so that we can detect
+ // out-of-bounds accesses easily.
+ typedef bit [AWIDTH:0] addr_t;
+
+ // Data word type
+ typedef logic [DWIDTH-1:0] data_t;
+
+ // Mask to indicate which bits should be written.
+ typedef bit [DWIDTH/8-1:0] mask_t;
+
+
+ //---------------------------------------------------------------------------
+ // Data Structures
+ //---------------------------------------------------------------------------
+
+ byte memory [addr_t]; // Byte addressable memory
+ mailbox #(req_t) read_req = new(); // Read request queue
+ mailbox #(req_t) write_req = new(); // Write request queue
+ mailbox #(req_t) write_resp = new(); // Write response queue
+
+ longint req_count; // Number of requests received
+ longint compl_count; // Number of requests completed
+
+
+ //---------------------------------------------------------------------------
+ // External Configuration Interface
+ //---------------------------------------------------------------------------
+
+ int waddr_stall_prob = STALL_PROB;
+ int wdata_stall_prob = STALL_PROB;
+ int wresp_stall_prob = STALL_PROB;
+ int raddr_stall_prob = STALL_PROB;
+ int rdata_stall_prob = STALL_PROB;
+
+ // Set ALL stall probabilities to the same value
+ function void set_stall_prob(int probability);
+ assert(probability >= 0 && probability <= 100) else begin
+ $error("Probability must be from 0 to 100");
+ end
+ waddr_stall_prob = probability;
+ wdata_stall_prob = probability;
+ wresp_stall_prob = probability;
+ raddr_stall_prob = probability;
+ rdata_stall_prob = probability;
+ endfunction : set_stall_prob
+
+ // Set WRITE stall probabilities to the same value
+ function void set_write_stall_prob(int probability);
+ assert(probability >= 0 && probability <= 100) else begin
+ $error("Probability must be from 0 to 100");
+ end
+ waddr_stall_prob = probability;
+ wdata_stall_prob = probability;
+ wresp_stall_prob = probability;
+ endfunction : set_write_stall_prob
+
+ // Set READ stall probabilities to the same value
+ function void set_read_stall_prob(int probability);
+ assert(probability >= 0 && probability <= 100) else begin
+ $error("Probability must be from 0 to 100");
+ end
+ raddr_stall_prob = probability;
+ rdata_stall_prob = probability;
+ endfunction : set_read_stall_prob
+
+ // Set Write Address Channel stall probability
+ function void set_waddr_stall_prob(int probability);
+ assert(probability >= 0 && probability <= 100) else begin
+ $error("Probability must be from 0 to 100");
+ end
+ waddr_stall_prob = probability;
+ endfunction : set_waddr_stall_prob
+
+ // Set Write Data Channel stall probability
+ function void set_wdata_stall_prob(int probability);
+ assert(probability >= 0 && probability <= 100) else begin
+ $error("Probability must be from 0 to 100");
+ end
+ wdata_stall_prob = probability;
+ endfunction : set_wdata_stall_prob
+
+ // Set Write Response Channel stall probability
+ function void set_wresp_stall_prob(int probability);
+ assert(probability >= 0 && probability <= 100) else begin
+ $error("Probability must be from 0 to 100");
+ end
+ wresp_stall_prob = probability;
+ endfunction : set_wresp_stall_prob
+
+ // Set Read Address Channel stall probability
+ function void set_raddr_stall_prob(int probability);
+ assert(probability >= 0 && probability <= 100) else begin
+ $error("Probability must be from 0 to 100");
+ end
+ raddr_stall_prob = probability;
+ endfunction : set_raddr_stall_prob
+
+ // Set Read Data Channel stall probability
+ function void set_rdata_stall_prob(int probability);
+ assert(probability >= 0 && probability <= 100) else begin
+ $error("Probability must be from 0 to 100");
+ end
+ rdata_stall_prob = probability;
+ endfunction : set_rdata_stall_prob
+
+ // Get Write Address Channel stall probability
+ function int get_waddr_stall_prob();
+ return waddr_stall_prob;
+ endfunction : get_waddr_stall_prob
+
+ // Get Write Data Channel stall probability
+ function int get_wdata_stall_prob();
+ return wdata_stall_prob;
+ endfunction : get_wdata_stall_prob
+
+ // Get Write Response Channel stall probability
+ function int get_wresp_stall_prob();
+ return wresp_stall_prob;
+ endfunction : get_wresp_stall_prob
+
+ // Get Read Address Channel stall probability
+ function int get_raddr_stall_prob();
+ return raddr_stall_prob;
+ endfunction : get_raddr_stall_prob
+
+ // Get Read Data Channel stall probability
+ function int get_rdata_stall_prob();
+ return rdata_stall_prob;
+ endfunction : get_rdata_stall_prob
+
+
+
+ //---------------------------------------------------------------------------
+ // Helper Functions
+ //---------------------------------------------------------------------------
+
+ function data_t read_mem(addr_t byte_addr, int num_bytes);
+ data_t data;
+ addr_t incr;
+
+ if (BIG_ENDIAN) begin
+ byte_addr = byte_addr + num_bytes-1;
+ incr = -1;
+ end else begin
+ incr = 1;
+ end
+
+ for (int i = 0; i < num_bytes; i++) begin
+ if (byte_addr >= 2**AWIDTH) begin
+ $fatal(1, "Read extends beyond memory range");
+ end
+ if (memory.exists(byte_addr)) data[i*8 +: 8] = memory[byte_addr];
+ else data[i*8 +: 8] = 'X;
+ byte_addr += incr;
+ end
+
+ return data;
+ endfunction : read_mem
+
+
+ function void write_mem(addr_t byte_addr, int num_bytes, data_t data, mask_t mask);
+ addr_t incr;
+
+ if (BIG_ENDIAN) begin
+ byte_addr = byte_addr + num_bytes-1;
+ incr = -1;
+ end else begin
+ incr = 1;
+ end
+
+ for (int i = 0; i < num_bytes; i++) begin
+ if (mask[i]) begin
+ if (byte_addr >= 2**AWIDTH) begin
+ $fatal(1, "Write extends beyond memory range");
+ end
+ memory[byte_addr] = data[i*8 +: 8];
+ end
+ byte_addr += incr;
+ end
+ endfunction : write_mem
+
+
+ //---------------------------------------------------------------------------
+ // Write Requests
+ //---------------------------------------------------------------------------
+
+ initial begin : write_req_proc
+ req_t req;
+ burst_t burst;
+
+ s_axi_awready <= 0;
+
+ forever begin
+ @(posedge s_aclk);
+ if (!s_aresetn) continue;
+
+ if (s_axi_awvalid) begin
+ if (s_axi_awready) begin
+ req.count = req_count;
+ req.id = s_axi_awid;
+ req.addr = s_axi_awaddr;
+ req.len = s_axi_awlen + 1; // Per AXI4 spec, Burst_length = AxLEN[7:0] + 1
+ req.size = 2**s_axi_awsize; // Store as true size in bytes, not clog2(size)
+ req.burst = burst_t'(s_axi_awburst);
+
+ // Check that the request is valid
+ assert (!$isunknown(req)) else begin
+ $fatal(1, "Write request signals are unknown");
+ end
+ assert (s_axi_araddr % (DWIDTH/8) == 0) else begin
+ $warning("Unaligned memory write");
+ end
+ assert (2**s_axi_awsize <= DWIDTH/8) else begin
+ $fatal(1, "AWSIZE must not be larger than DWIDTH");
+ end
+ assert ($cast(burst, s_axi_awburst)) else begin
+ $fatal(1, "Invalid AWBURST value");
+ end
+
+ if (DEBUG) begin
+ $display("WRITE REQ: id=%X, addr=%X, len=%X, size=%X, burst=%s, %t, %m",
+ req.id, req.addr, req.len, req.size, req.burst.name, $realtime);
+ end
+
+ req_count++;
+ write_req.put(req);
+ end
+
+ // Randomly deassert ready
+ s_axi_awready <= $urandom_range(99) < waddr_stall_prob ? 0 : 1;
+ end
+ end
+ end : write_req_proc
+
+
+ //---------------------------------------------------------------------------
+ // Read Requests
+ //---------------------------------------------------------------------------
+
+ initial begin : read_req_proc
+ req_t req;
+ burst_t burst;
+
+ s_axi_arready <= 0;
+
+ forever begin
+ @(posedge s_aclk);
+ if (!s_aresetn) continue;
+
+ if (s_axi_arvalid) begin
+ if (s_axi_arready) begin
+ req.count = req_count;
+ req.id = s_axi_arid;
+ req.addr = s_axi_araddr;
+ req.len = s_axi_arlen + 1; // Per AXI4 spec, Burst_length = AxLEN[7:0] + 1
+ req.size = 2**s_axi_arsize; // Store as true size in bytes, not clog2(size)
+ req.burst = burst_t'(s_axi_arburst);
+
+ // Check that the request is valid
+ assert(!$isunknown(req)) else begin
+ $fatal(1, "Read request signals are unknown");
+ end
+ assert(s_axi_araddr % (DWIDTH/8) == 0) else begin
+ $warning("Unaligned memory read");
+ end
+ assert(2**s_axi_arsize <= DWIDTH/8) else begin
+ $fatal(1, "ARSIZE must not be larger than DWIDTH");
+ end
+ assert ($cast(burst, s_axi_awburst)) else begin
+ $fatal(1, "Invalid ARBURST value");
+ end
+
+ if (DEBUG) begin
+ $display("READ REQ: id=%X, addr=%X, len=%X, size=%X, burst=%s, %t, %m",
+ req.id, req.addr, req.len, req.size, req.burst.name, $realtime);
+ end
+
+ req_count++;
+ read_req.put(req);
+ end
+
+ // Randomly deassert ready to cause a stall
+ s_axi_arready <= $urandom_range(99) < raddr_stall_prob ? 0 : 1;
+ end
+ end
+ end : read_req_proc
+
+
+ //---------------------------------------------------------------------------
+ // Write Data
+ //---------------------------------------------------------------------------
+
+ initial begin : write_data_proc
+ req_t req;
+ bit [AWIDTH-1:0] addr;
+
+ forever begin
+ // Wait for the next write request
+ s_axi_wready <= 0;
+ write_req.get(req);
+
+ // Wait for previous requests to complete
+ while (compl_count < req.count) begin
+ @(posedge s_aclk);
+ if (!s_aresetn) break;
+ end
+
+ // If reset was asserted, clear the request queue and start over
+ if (!s_aresetn) begin
+ while(write_req.try_get(req));
+ continue;
+ end
+
+ // Iterate over the number of words in the request
+ for (int i = 0; i < req.len; ) begin
+ @(posedge s_aclk);
+ if (!s_aresetn) break;
+
+ // Check if we have a new data word
+ if (s_axi_wvalid) begin
+ if (s_axi_wready) begin
+ // Check the inputs
+ if ($isunknown(s_axi_wstrb)) begin
+ $fatal(1, "WSTRB is unknown");
+ end
+ if ($isunknown(s_axi_wdata)) begin
+ $warning(1, "WDATA is unknown; data will be changed to zero");
+ end
+
+ case (req.burst)
+ FIXED : begin
+ addr = req.addr;
+ end
+ INCR : begin
+ // If the address rolls over, we've reached the end of the
+ // memory and we should stop here.
+ addr = req.addr + i*req.size;
+ if (addr < req.addr) break;
+ end
+ WRAP : begin
+ // Allow roll-over
+ addr = req.addr + i*req.size;
+ end
+ endcase
+
+ write_mem(addr, req.size, s_axi_wdata, s_axi_wstrb);
+
+ if (DEBUG) begin
+ $display("WRITE: count=%3X, ADDR=%X, DATA=%X, SIZE=%X, STRB=%X, %t, %m",
+ i, addr, s_axi_wdata, req.size, s_axi_wstrb, $realtime);
+ end
+
+ i++;
+ end
+
+ // Randomly deassert ready to cause a stall
+ s_axi_wready <= $urandom_range(99) < wdata_stall_prob ? 0 : 1;
+ end
+ end // for
+
+ // If reset was asserted, clear the request queue and start over
+ if (!s_aresetn) begin
+ while(write_req.try_get(req));
+ continue;
+ end
+
+ compl_count++;
+
+ // Enqueue write response
+ write_resp.put(req);
+
+ // Make sure WLAST asserted for the last word. If not we report an error.
+ // Per the AXI4 standard, "a slave is not required to use the WLAST
+ // signal" because "a slave can calculate the last write data transfer
+ // from the burst length AWLEN".
+ if (s_axi_wlast != 1'b1) begin
+ $error("WLAST not asserted on last word of burst");
+ end
+
+ end // forever
+ end : write_data_proc
+
+
+ //---------------------------------------------------------------------------
+ // Write Response
+ //---------------------------------------------------------------------------
+
+ initial begin : write_resp_proc
+ req_t resp;
+ bit [AWIDTH-1:0] addr;
+
+ forever begin
+ s_axi_bid <= 'X;
+ s_axi_bresp <= 'X;
+ s_axi_bvalid <= 0;
+
+ // Wait for the next write response
+ write_resp.get(resp);
+ @(posedge s_aclk);
+
+ // If there's a reset, clear the response queue and start over
+ if (!s_aresetn) begin
+ while(write_resp.try_get(resp));
+ continue;
+ end
+
+ // Randomly keep bvalid deasserted for next word to cause a stall
+ if ($urandom_range(99) < wresp_stall_prob) begin
+ do begin
+ @(posedge s_aclk);
+ if (!s_aresetn) break;
+ end while ($urandom_range(99) < wresp_stall_prob);
+
+ // If reset was asserted, clear the response queue and start over
+ if (!s_aresetn) begin
+ while(write_resp.try_get(resp));
+ continue;
+ end
+ end
+
+ // Output the next response
+ s_axi_bid <= resp.id;
+ s_axi_bresp <= OKAY;
+ s_axi_bvalid <= 1;
+
+ if (DEBUG) begin
+ $display("WRITE RESP: ID=%X, %t, %m", resp.id, $realtime);
+ end
+
+ // Wait for the response to be accepted
+ do begin
+ @(posedge s_aclk);
+ if (!s_aresetn) break;
+ end while (!s_axi_bready);
+
+ // Output the next response
+ s_axi_bid <= 'X;
+ s_axi_bresp <= 'X;
+ s_axi_bvalid <= 0;
+
+ // If reset was asserted, clear the response queue and start over
+ if (!s_aresetn) begin
+ while(write_resp.try_get(resp));
+ continue;
+ end
+ end // forever
+ end : write_resp_proc
+
+
+ //---------------------------------------------------------------------------
+ // Read Data
+ //---------------------------------------------------------------------------
+
+ initial begin : read_data_proc
+ req_t req;
+ bit [AWIDTH-1:0] addr;
+ logic [DWIDTH-1:0] data;
+
+ forever begin
+ s_axi_rid <= 'X;
+ s_axi_rdata <= 'X;
+ s_axi_rresp <= 'X;
+ s_axi_rlast <= 'X;
+ s_axi_rvalid <= 0;
+
+ // Wait for the next read request
+ read_req.get(req);
+
+ // Wait for previous requests to complete
+ do begin
+ @(posedge s_aclk);
+ if (!s_aresetn) break;
+ end while (compl_count < req.count);
+
+ // If reset was asserted, clear the request queue and start over
+ if (!s_aresetn) begin
+ while(read_req.try_get(req));
+ continue;
+ end
+
+ for (int i = 0; i < req.len; i++) begin
+ // Randomly keep rvalid deasserted for next word to cause a stall
+ if ($urandom_range(99) < rdata_stall_prob) begin
+ do begin
+ @(posedge s_aclk);
+ if (!s_aresetn) break;
+ end while ($urandom_range(99) < rdata_stall_prob);
+ if (!s_aresetn) break;
+ end
+
+ case (req.burst)
+ FIXED : begin
+ addr = req.addr;
+ end
+ INCR : begin
+ // If the address rolls over, we've reached the end of the memory
+ // and we should stop here.
+ addr = req.addr + i*req.size;
+ if (addr < req.addr) break;
+ end
+ WRAP : begin
+ // Allow roll-over
+ addr = req.addr + i*req.size;
+ end
+ endcase
+
+ // Read the memory
+ data = read_mem(addr, req.size);
+
+ // Output the next word
+ s_axi_rid <= req.id;
+ s_axi_rdata <= data;
+ s_axi_rresp <= OKAY;
+ s_axi_rlast <= (i == req.len-1);
+ s_axi_rvalid <= 1;
+
+ if (DEBUG) begin
+ $display("READ: count=%3X, ADDR=%X, DATA=%X, SIZE=%X, %t, %m", i, addr, data, req.size, $realtime);
+ end
+
+ // Wait for the word to be captured
+ do begin
+ @(posedge s_aclk);
+ if (!s_aresetn) break;
+ end while (!s_axi_rready);
+
+ s_axi_rid <= 'X;
+ s_axi_rdata <= 'X;
+ s_axi_rresp <= 'X;
+ s_axi_rlast <= 'X;
+ s_axi_rvalid <= 0;
+ end // for
+
+ // If reset was asserted, clear the request queue and start over
+ if (!s_aresetn) begin
+ while(read_req.try_get(req));
+ end
+
+ compl_count++;
+
+ end // forever
+ end : read_data_proc
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/Makefile
new file mode 100644
index 000000000..d574c9a01
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/Makefile
@@ -0,0 +1,68 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../../../top)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# IP Specific
+#-------------------------------------------------
+# If simulation contains IP, define the IP_DIR and point
+# it to the base level IP directory
+LIB_IP_DIR = $(BASE_DIR)/../lib/ip
+
+# Include makefiles and sources for all IP components
+# *after* defining the LIB_IP_DIR
+#include $(LIB_IP_DIR)/axi_fft/Makefile.inc
+#include $(LIB_IP_DIR)/complex_to_magphase/Makefile.inc
+include $(LIB_IP_DIR)/complex_multiplier_dds/Makefile.inc
+include $(LIB_IP_DIR)/dds_sin_cos_lut_only/Makefile.inc
+include $(BASE_DIR)/x300/coregen_dsp/Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(LIB_IP_COMPLEX_MULTIPLIER_DDS_SRCS) \
+$(LIB_IP_DDS_SIN_COS_LUT_ONLY_SRCS) \
+$(COREGEN_DSP_SRCS) \
+)
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
+include Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_BLOCK_DDC_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = rfnoc_block_ddc_tb
+
+# Add test bench, user design under test, and
+# additional user created files
+SIM_SRCS = \
+$(COREGEN_DSP_SRCS) \
+$(abspath rfnoc_block_ddc_tb.sv)
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+# Include all simulator specific makefiles here
+# Each should define a unique target to simulate
+# e.g. xsim, vsim, etc and a common "clean" target
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/Makefile.srcs
new file mode 100644
index 000000000..28663f03c
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/Makefile.srcs
@@ -0,0 +1,11 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+RFNOC_BLOCK_DDC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_ddc/, \
+noc_shell_ddc.v \
+rfnoc_block_ddc_regs.vh \
+rfnoc_block_ddc.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/noc_shell_ddc.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/noc_shell_ddc.v
new file mode 100644
index 000000000..56a13ee0a
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/noc_shell_ddc.v
@@ -0,0 +1,291 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: noc_shell_ddc
+//
+// Description: A NoC Shell for RFNoC. This should eventually be replaced
+// by an auto-generated NoC Shell.
+//
+
+module noc_shell_ddc #(
+ parameter [31:0] NOC_ID = 32'h0,
+ parameter [ 9:0] THIS_PORTID = 10'd0,
+ parameter CHDR_W = 64,
+ parameter [ 0:0] CTRLPORT_SLV_EN = 1,
+ parameter [ 0:0] CTRLPORT_MST_EN = 1,
+ parameter [ 5:0] CTRL_FIFO_SIZE = 6,
+ parameter [ 5:0] NUM_DATA_I = 1,
+ parameter [ 5:0] NUM_DATA_O = 1,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter PYLD_FIFO_SIZE = 10,
+ parameter MTU = 10
+)(
+ //---------------------------------------------------------------------------
+ // Framework Interface
+ //---------------------------------------------------------------------------
+
+ // RFNoC Framework Clocks and Resets
+ input wire rfnoc_chdr_clk,
+ output wire rfnoc_chdr_rst,
+ input wire rfnoc_ctrl_clk,
+ output wire rfnoc_ctrl_rst,
+ // RFNoC Backend Interface
+ input wire [ 511:0] rfnoc_core_config,
+ output wire [ 511:0] rfnoc_core_status,
+ // CHDR Input Ports (from framework)
+ input wire [(CHDR_W*NUM_DATA_I)-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tready,
+ // CHDR Output Ports (to framework)
+ output wire [(CHDR_W*NUM_DATA_O)-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tready,
+ // AXIS-Ctrl Input Port (from framework)
+ input wire [ 31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ // AXIS-Ctrl Output Port (to framework)
+ output wire [ 31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+
+ //---------------------------------------------------------------------------
+ // Client Control Port Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+ // Master
+ output wire m_ctrlport_req_wr,
+ output wire m_ctrlport_req_rd,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [31:0] m_ctrlport_req_data,
+ output wire [ 3:0] m_ctrlport_req_byte_en,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ input wire m_ctrlport_resp_ack,
+ input wire [ 1:0] m_ctrlport_resp_status,
+ input wire [31:0] m_ctrlport_resp_data,
+ // Slave
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [ 9:0] s_ctrlport_req_portid,
+ input wire [15:0] s_ctrlport_req_rem_epid,
+ input wire [ 9:0] s_ctrlport_req_rem_portid,
+ input wire [31:0] s_ctrlport_req_data,
+ input wire [ 3:0] s_ctrlport_req_byte_en,
+ input wire s_ctrlport_req_has_time,
+ input wire [63:0] s_ctrlport_req_time,
+ output wire s_ctrlport_resp_ack,
+ output wire [ 1:0] s_ctrlport_resp_status,
+ output wire [31:0] s_ctrlport_resp_data,
+
+ //---------------------------------------------------------------------------
+ // Client Data Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+
+ // Output data stream (to user logic)
+ output wire [(NUM_DATA_I*ITEM_W*NIPC)-1:0] m_axis_tdata,
+ output wire [ (NUM_DATA_I*NIPC)-1:0] m_axis_tkeep,
+ output wire [ NUM_DATA_I-1:0] m_axis_tlast,
+ output wire [ NUM_DATA_I-1:0] m_axis_tvalid,
+ input wire [ NUM_DATA_I-1:0] m_axis_tready,
+ // Sideband information
+ output wire [ (NUM_DATA_I*64)-1:0] m_axis_ttimestamp,
+ output wire [ NUM_DATA_I-1:0] m_axis_thas_time,
+ output wire [ (NUM_DATA_I*16)-1:0] m_axis_tlength,
+ output wire [ NUM_DATA_I-1:0] m_axis_teov,
+ output wire [ NUM_DATA_I-1:0] m_axis_teob,
+
+ // Input data stream (from user logic)
+ input wire [(NUM_DATA_O*ITEM_W*NIPC)-1:0] s_axis_tdata,
+ input wire [ (NUM_DATA_O*NIPC)-1:0] s_axis_tkeep,
+ input wire [ NUM_DATA_O-1:0] s_axis_tlast,
+ input wire [ NUM_DATA_O-1:0] s_axis_tvalid,
+ output wire [ NUM_DATA_O-1:0] s_axis_tready,
+ // Sideband info (sampled on the first cycle of the packet)
+ input wire [ (NUM_DATA_O*64)-1:0] s_axis_ttimestamp,
+ input wire [ NUM_DATA_O-1:0] s_axis_thas_time,
+ input wire [ NUM_DATA_O-1:0] s_axis_teov,
+ input wire [ NUM_DATA_O-1:0] s_axis_teob
+);
+
+ localparam SNK_INFO_FIFO_SIZE = 4;
+ localparam SNK_PYLD_FIFO_SIZE = PYLD_FIFO_SIZE;
+ localparam SRC_INFO_FIFO_SIZE = 4;
+ localparam SRC_PYLD_FIFO_SIZE = (MTU > PYLD_FIFO_SIZE) ? MTU : PYLD_FIFO_SIZE;
+
+ //---------------------------------------------------------------------------
+ // Backend Interface
+ //---------------------------------------------------------------------------
+
+ wire data_i_flush_en;
+ wire [31:0] data_i_flush_timeout;
+ wire [63:0] data_i_flush_active;
+ wire [63:0] data_i_flush_done;
+ wire data_o_flush_en;
+ wire [31:0] data_o_flush_timeout;
+ wire [63:0] data_o_flush_active;
+ wire [63:0] data_o_flush_done;
+
+ backend_iface #(
+ .NOC_ID (NOC_ID),
+ .NUM_DATA_I (NUM_DATA_I),
+ .NUM_DATA_O (NUM_DATA_O),
+ .CTRL_FIFOSIZE (CTRL_FIFO_SIZE),
+ .MTU (MTU)
+ ) backend_iface_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst),
+ .data_i_flush_en (data_i_flush_en),
+ .data_i_flush_timeout (data_i_flush_timeout),
+ .data_i_flush_active (data_i_flush_active),
+ .data_i_flush_done (data_i_flush_done),
+ .data_o_flush_en (data_o_flush_en),
+ .data_o_flush_timeout (data_o_flush_timeout),
+ .data_o_flush_active (data_o_flush_active),
+ .data_o_flush_done (data_o_flush_done)
+ );
+
+ //---------------------------------------------------------------------------
+ // Control Path
+ //---------------------------------------------------------------------------
+
+ ctrlport_endpoint #(
+ .THIS_PORTID (THIS_PORTID ),
+ .SYNC_CLKS (0 ),
+ .AXIS_CTRL_MST_EN (CTRLPORT_SLV_EN),
+ .AXIS_CTRL_SLV_EN (CTRLPORT_MST_EN),
+ .SLAVE_FIFO_SIZE (CTRL_FIFO_SIZE )
+ ) ctrlport_ep_i (
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk ),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst ),
+ .ctrlport_clk (ctrlport_clk ),
+ .ctrlport_rst (ctrlport_rst ),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata ),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast ),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid ),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready ),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata ),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast ),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid ),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready ),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr ),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd ),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr ),
+ .m_ctrlport_req_data (m_ctrlport_req_data ),
+ .m_ctrlport_req_byte_en (m_ctrlport_req_byte_en ),
+ .m_ctrlport_req_has_time (m_ctrlport_req_has_time ),
+ .m_ctrlport_req_time (m_ctrlport_req_time ),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack ),
+ .m_ctrlport_resp_status (m_ctrlport_resp_status ),
+ .m_ctrlport_resp_data (m_ctrlport_resp_data ),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr ),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd ),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr ),
+ .s_ctrlport_req_portid (s_ctrlport_req_portid ),
+ .s_ctrlport_req_rem_epid (s_ctrlport_req_rem_epid ),
+ .s_ctrlport_req_rem_portid(s_ctrlport_req_rem_portid),
+ .s_ctrlport_req_data (s_ctrlport_req_data ),
+ .s_ctrlport_req_byte_en (s_ctrlport_req_byte_en ),
+ .s_ctrlport_req_has_time (s_ctrlport_req_has_time ),
+ .s_ctrlport_req_time (s_ctrlport_req_time ),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack ),
+ .s_ctrlport_resp_status (s_ctrlport_resp_status ),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data )
+ );
+
+ //---------------------------------------------------------------------------
+ // Data Path
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ generate
+
+ for (i = 0; i < NUM_DATA_I; i = i + 1) begin: chdr_to_data
+ chdr_to_axis_data #(
+ .CHDR_W (CHDR_W),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .SYNC_CLKS (0),
+ .INFO_FIFO_SIZE (SNK_INFO_FIFO_SIZE),
+ .PYLD_FIFO_SIZE (SNK_PYLD_FIFO_SIZE)
+ ) chdr_to_axis_data_i (
+ .axis_chdr_clk (rfnoc_chdr_clk),
+ .axis_chdr_rst (rfnoc_chdr_rst),
+ .axis_data_clk (axis_data_clk),
+ .axis_data_rst (axis_data_rst),
+ .s_axis_chdr_tdata (s_rfnoc_chdr_tdata [(i*CHDR_W)+:CHDR_W]),
+ .s_axis_chdr_tlast (s_rfnoc_chdr_tlast [i]),
+ .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid [i]),
+ .s_axis_chdr_tready (s_rfnoc_chdr_tready [i]),
+ .m_axis_tdata (m_axis_tdata [i*ITEM_W*NIPC +: ITEM_W*NIPC]),
+ .m_axis_tkeep (m_axis_tkeep [i*NIPC +: NIPC]),
+ .m_axis_tlast (m_axis_tlast [i]),
+ .m_axis_tvalid (m_axis_tvalid [i]),
+ .m_axis_tready (m_axis_tready [i]),
+ .m_axis_ttimestamp (m_axis_ttimestamp [i*64 +: 64]),
+ .m_axis_thas_time (m_axis_thas_time [i]),
+ .m_axis_tlength (m_axis_tlength [i*16 +: 16]),
+ .m_axis_teov (m_axis_teov [i]),
+ .m_axis_teob (m_axis_teob [i]),
+ .flush_en (data_i_flush_en),
+ .flush_timeout (data_i_flush_timeout),
+ .flush_active (data_i_flush_active [i]),
+ .flush_done (data_i_flush_done [i])
+ );
+ end
+
+ for (i = 0; i < NUM_DATA_O; i = i + 1) begin: data_to_chdr
+ axis_data_to_chdr #(
+ .CHDR_W (CHDR_W),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .SYNC_CLKS (0),
+ .INFO_FIFO_SIZE (4),
+ .PYLD_FIFO_SIZE (SRC_INFO_FIFO_SIZE),
+ .MTU (SRC_PYLD_FIFO_SIZE)
+ ) axis_data_to_chdr_i (
+ .axis_chdr_clk (rfnoc_chdr_clk),
+ .axis_chdr_rst (rfnoc_chdr_rst),
+ .axis_data_clk (axis_data_clk),
+ .axis_data_rst (axis_data_rst),
+ .m_axis_chdr_tdata (m_rfnoc_chdr_tdata [i*CHDR_W +: CHDR_W]),
+ .m_axis_chdr_tlast (m_rfnoc_chdr_tlast [i]),
+ .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid [i]),
+ .m_axis_chdr_tready (m_rfnoc_chdr_tready [i]),
+ .s_axis_tdata (s_axis_tdata [i*ITEM_W*NIPC +: ITEM_W*NIPC]),
+ .s_axis_tkeep (s_axis_tkeep [i*NIPC +: NIPC]),
+ .s_axis_tlast (s_axis_tlast [i]),
+ .s_axis_tvalid (s_axis_tvalid [i]),
+ .s_axis_tready (s_axis_tready [i]),
+ .s_axis_ttimestamp (s_axis_ttimestamp [i*64 +: 64]),
+ .s_axis_thas_time (s_axis_thas_time [i]),
+ .s_axis_teov (s_axis_teov [i]),
+ .s_axis_teob (s_axis_teob [i]),
+ .flush_en (data_o_flush_en),
+ .flush_timeout (data_o_flush_timeout),
+ .flush_active (data_o_flush_active [i]),
+ .flush_done (data_o_flush_done [i])
+ );
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc.v
new file mode 100644
index 000000000..3162743b6
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc.v
@@ -0,0 +1,420 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_ddc
+//
+// Description: An digital down-converter block for RFNoC.
+//
+// Parameters:
+//
+// THIS_PORTID : Control crossbar port to which this block is connected
+// CHDR_W : AXIS CHDR interface data width
+// NUM_PORTS : Number of DDCs to instantiate
+// MTU : Maximum transmission unit (i.e., maximum packet size) in
+// CHDR words is 2**MTU.
+// CTRL_FIFO_SIZE : Size of the Control Port slave FIFO. This affects the
+// number of outstanding commands that can be pending.
+// NUM_HB : Number of half-band decimation blocks to include (0-3)
+// CIC_MAX_DECIM : Maximum decimation to support in the CIC filter
+//
+
+module rfnoc_block_ddc #(
+ parameter THIS_PORTID = 0,
+ parameter CHDR_W = 64,
+ parameter NUM_PORTS = 2,
+ parameter MTU = 10,
+ parameter CTRL_FIFO_SIZE = 6,
+ parameter NUM_HB = 3,
+ parameter CIC_MAX_DECIM = 255
+) (
+ //---------------------------------------------------------------------------
+ // AXIS CHDR Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_chdr_clk,
+ input wire ce_clk,
+
+ // CHDR inputs from framework
+ input wire [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tready,
+
+ // CHDR outputs to framework
+ output wire [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
+
+ // Backend interface
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+
+ //---------------------------------------------------------------------------
+ // AXIS CTRL Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_ctrl_clk,
+
+ // CTRL port requests from framework
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+
+ // CTRL port requests to framework
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready
+);
+
+ // These are the only supported values for now
+ localparam ITEM_W = 32;
+ localparam NIPC = 1;
+
+ localparam NOC_ID = 'hDDC0_0000;
+
+ localparam COMPAT_MAJOR = 16'h0;
+ localparam COMPAT_MINOR = 16'h0;
+
+ `include "rfnoc_block_ddc_regs.vh"
+ `include "../../core/rfnoc_axis_ctrl_utils.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Signal Declarations
+ //---------------------------------------------------------------------------
+
+ wire rfnoc_chdr_rst;
+
+ wire ctrlport_req_wr;
+ wire ctrlport_req_rd;
+ wire [19:0] ctrlport_req_addr;
+ wire [31:0] ctrlport_req_data;
+ wire ctrlport_req_has_time;
+ wire [63:0] ctrlport_req_time;
+ wire ctrlport_resp_ack;
+ wire [31:0] ctrlport_resp_data;
+
+ wire [NUM_PORTS*ITEM_W-1:0] m_axis_data_tdata;
+ wire [ NUM_PORTS-1:0] m_axis_data_tlast;
+ wire [ NUM_PORTS-1:0] m_axis_data_tvalid;
+ wire [ NUM_PORTS-1:0] m_axis_data_tready;
+ wire [ NUM_PORTS*64-1:0] m_axis_data_ttimestamp;
+ wire [ NUM_PORTS-1:0] m_axis_data_thas_time;
+ wire [ 16*NUM_PORTS-1:0] m_axis_data_tlength;
+ wire [ NUM_PORTS-1:0] m_axis_data_teob;
+ wire [ NUM_PORTS*128-1:0] m_axis_data_tuser;
+
+ wire [NUM_PORTS*ITEM_W-1:0] s_axis_data_tdata;
+ wire [ NUM_PORTS-1:0] s_axis_data_tlast;
+ wire [ NUM_PORTS-1:0] s_axis_data_tvalid;
+ wire [ NUM_PORTS-1:0] s_axis_data_tready;
+ wire [ NUM_PORTS*128-1:0] s_axis_data_tuser;
+ wire [ NUM_PORTS-1:0] s_axis_data_teob;
+ wire [ NUM_PORTS*64-1:0] s_axis_data_ttimestamp;
+ wire [ NUM_PORTS-1:0] s_axis_data_thas_time;
+
+ wire ddc_rst;
+
+ // Cross the CHDR reset to the ce_clk domain
+ synchronizer ddc_rst_sync_i (
+ .clk (ce_clk),
+ .rst (1'b0),
+ .in (rfnoc_chdr_rst),
+ .out (ddc_rst)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // NoC Shell
+ //---------------------------------------------------------------------------
+
+ noc_shell_ddc #(
+ .NOC_ID (NOC_ID),
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .CTRLPORT_SLV_EN (0),
+ .CTRLPORT_MST_EN (1),
+ .CTRL_FIFO_SIZE (CTRL_FIFO_SIZE),
+ .NUM_DATA_I (NUM_PORTS),
+ .NUM_DATA_O (NUM_PORTS),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .PYLD_FIFO_SIZE (MTU),
+ .MTU (MTU)
+ ) noc_shell_ddc_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_ctrl_rst (),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (ddc_rst),
+ .m_ctrlport_req_wr (ctrlport_req_wr),
+ .m_ctrlport_req_rd (ctrlport_req_rd),
+ .m_ctrlport_req_addr (ctrlport_req_addr),
+ .m_ctrlport_req_data (ctrlport_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (ctrlport_req_has_time),
+ .m_ctrlport_req_time (ctrlport_req_time),
+ .m_ctrlport_resp_ack (ctrlport_resp_ack),
+ .m_ctrlport_resp_status (AXIS_CTRL_STS_OKAY),
+ .m_ctrlport_resp_data (ctrlport_resp_data),
+ .s_ctrlport_req_wr (1'b0),
+ .s_ctrlport_req_rd (1'b0),
+ .s_ctrlport_req_addr (20'b0),
+ .s_ctrlport_req_portid (10'b0),
+ .s_ctrlport_req_rem_epid (16'b0),
+ .s_ctrlport_req_rem_portid (10'b0),
+ .s_ctrlport_req_data (32'b0),
+ .s_ctrlport_req_byte_en (4'b0),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (),
+ .axis_data_clk (ce_clk),
+ .axis_data_rst (ddc_rst),
+ .m_axis_tdata (m_axis_data_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (m_axis_data_tlast),
+ .m_axis_tvalid (m_axis_data_tvalid),
+ .m_axis_tready (m_axis_data_tready),
+ .m_axis_ttimestamp (m_axis_data_ttimestamp),
+ .m_axis_thas_time (m_axis_data_thas_time),
+ .m_axis_tlength (m_axis_data_tlength),
+ .m_axis_teov (),
+ .m_axis_teob (m_axis_data_teob),
+ .s_axis_tdata (s_axis_data_tdata),
+ .s_axis_tkeep ({NUM_PORTS*NIPC{1'b1}}),
+ .s_axis_tlast (s_axis_data_tlast),
+ .s_axis_tvalid (s_axis_data_tvalid),
+ .s_axis_tready (s_axis_data_tready),
+ .s_axis_ttimestamp (s_axis_data_ttimestamp),
+ .s_axis_thas_time (s_axis_data_thas_time),
+ .s_axis_teov ({NUM_PORTS{1'b0}}),
+ .s_axis_teob (s_axis_data_teob)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Register Translation
+ //---------------------------------------------------------------------------
+ //
+ // Each DDC block is allocated an address spaces. This block translates CTRL
+ // port transactions in that space to settings bus.
+ //
+ //---------------------------------------------------------------------------
+
+ wire [ 8*NUM_PORTS-1:0] set_addr;
+ wire [32*NUM_PORTS-1:0] set_data;
+ wire [ NUM_PORTS-1:0] set_has_time;
+ wire [ NUM_PORTS-1:0] set_stb;
+ wire [64*NUM_PORTS-1:0] set_time;
+ wire [ 8*NUM_PORTS-1:0] rb_addr;
+ reg [64*NUM_PORTS-1:0] rb_data;
+ wire [ NUM_PORTS-1:0] rb_stb;
+
+ ctrlport_to_settings_bus # (
+ .NUM_PORTS (NUM_PORTS)
+ ) ctrlport_to_settings_bus_i (
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (ddc_rst),
+ .s_ctrlport_req_wr (ctrlport_req_wr),
+ .s_ctrlport_req_rd (ctrlport_req_rd),
+ .s_ctrlport_req_addr (ctrlport_req_addr),
+ .s_ctrlport_req_data (ctrlport_req_data),
+ .s_ctrlport_req_has_time (ctrlport_req_has_time),
+ .s_ctrlport_req_time (ctrlport_req_time),
+ .s_ctrlport_resp_ack (ctrlport_resp_ack),
+ .s_ctrlport_resp_data (ctrlport_resp_data),
+ .set_data (set_data),
+ .set_addr (set_addr),
+ .set_stb (set_stb),
+ .set_time (set_time),
+ .set_has_time (set_has_time),
+ .rb_stb (rb_stb),
+ .rb_addr (rb_addr),
+ .rb_data (rb_data));
+
+
+ //---------------------------------------------------------------------------
+ // DDC Implementation
+ //---------------------------------------------------------------------------
+
+ // Unused signals
+ wire [ NUM_PORTS-1:0] clear_tx_seqnum = 0;
+ wire [16*NUM_PORTS-1:0] src_sid = 0;
+ wire [16*NUM_PORTS-1:0] next_dst_sid = 0;
+
+ localparam MAX_N = CIC_MAX_DECIM * 2 << (NUM_HB-1);
+
+ genvar i;
+ generate
+ for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_ddc_chains
+ wire set_stb_int = set_stb[i];
+ wire [7:0] set_addr_int = set_addr[8*i+7:8*i];
+ wire [31:0] set_data_int = set_data[32*i+31:32*i];
+ wire [63:0] set_time_int = set_time[64*i+63:64*i];
+ wire set_has_time_int = set_has_time[i];
+
+ // Build the expected tuser CHDR header
+ cvita_hdr_encoder cvita_hdr_encoder_i (
+ .pkt_type (2'b0),
+ .eob (m_axis_data_teob[i]),
+ .has_time (m_axis_data_thas_time[i]),
+ .seqnum (12'b0),
+ .payload_length (m_axis_data_tlength[16*i +: 16]),
+ .src_sid (16'b0),
+ .dst_sid (16'b0),
+ .vita_time (m_axis_data_ttimestamp[64*i +: 64]),
+ .header (m_axis_data_tuser[128*i+:128])
+ );
+
+ // Extract bit fields from outgoing tuser CHDR header
+ assign s_axis_data_teob[i] = s_axis_data_tuser[128*i+124 +: 1];
+ assign s_axis_data_thas_time[i] = s_axis_data_tuser[128*i+125 +: 1];
+ assign s_axis_data_ttimestamp[64*i+:64] = s_axis_data_tuser[128*i+ 0 +: 64];
+
+ // TODO: Read-back register for number of FIR filter taps
+ always @(*) begin
+ case(rb_addr[8*i+7:8*i])
+ RB_COMPAT_NUM : rb_data[64*i+63:64*i] <= {COMPAT_MAJOR, COMPAT_MINOR};
+ RB_NUM_HB : rb_data[64*i+63:64*i] <= NUM_HB;
+ RB_CIC_MAX_DECIM : rb_data[64*i+63:64*i] <= CIC_MAX_DECIM;
+ default : rb_data[64*i+63:64*i] <= 64'h0BADC0DE0BADC0DE;
+ endcase
+ end
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Timed Commands
+ //
+ ////////////////////////////////////////////////////////////
+ wire [31:0] m_axis_tagged_tdata;
+ wire m_axis_tagged_tlast;
+ wire m_axis_tagged_tvalid;
+ wire m_axis_tagged_tready;
+ wire [127:0] m_axis_tagged_tuser;
+ wire m_axis_tagged_tag;
+
+ wire out_set_stb;
+ wire [7:0] out_set_addr;
+ wire [31:0] out_set_data;
+ wire timed_set_stb;
+ wire [7:0] timed_set_addr;
+ wire [31:0] timed_set_data;
+
+ wire timed_cmd_fifo_full;
+
+ axi_tag_time #(
+ .NUM_TAGS(1),
+ .SR_TAG_ADDRS(SR_FREQ_ADDR))
+ axi_tag_time (
+ .clk(ce_clk),
+ .reset(ddc_rst),
+ .clear(clear_tx_seqnum[i]),
+ .tick_rate(16'd1),
+ .timed_cmd_fifo_full(timed_cmd_fifo_full),
+ .s_axis_data_tdata(m_axis_data_tdata[i*ITEM_W+:ITEM_W]), .s_axis_data_tlast(m_axis_data_tlast[i]),
+ .s_axis_data_tvalid(m_axis_data_tvalid[i]), .s_axis_data_tready(m_axis_data_tready[i]),
+ .s_axis_data_tuser(m_axis_data_tuser[128*i+:128]),
+ .m_axis_data_tdata(m_axis_tagged_tdata), .m_axis_data_tlast(m_axis_tagged_tlast),
+ .m_axis_data_tvalid(m_axis_tagged_tvalid), .m_axis_data_tready(m_axis_tagged_tready),
+ .m_axis_data_tuser(m_axis_tagged_tuser), .m_axis_data_tag(m_axis_tagged_tag),
+ .in_set_stb(set_stb_int), .in_set_addr(set_addr_int), .in_set_data(set_data_int),
+ .in_set_time(set_time_int), .in_set_has_time(set_has_time_int),
+ .out_set_stb(out_set_stb), .out_set_addr(out_set_addr), .out_set_data(out_set_data),
+ .timed_set_stb(timed_set_stb), .timed_set_addr(timed_set_addr), .timed_set_data(timed_set_data));
+
+ // Hold off reading additional commands if internal FIFO is full
+ assign rb_stb[i] = ~timed_cmd_fifo_full;
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Reduce Rate
+ //
+ ////////////////////////////////////////////////////////////
+ wire [31:0] sample_in_tdata, sample_out_tdata;
+ wire sample_in_tuser, sample_in_eob;
+ wire sample_in_tvalid, sample_in_tready, sample_in_tlast;
+ wire sample_out_tvalid, sample_out_tready;
+ wire clear_user;
+ wire nc;
+ axi_rate_change #(
+ .WIDTH(33),
+ .MAX_N(MAX_N),
+ .MAX_M(1),
+ .SR_N_ADDR(SR_N_ADDR),
+ .SR_M_ADDR(SR_M_ADDR),
+ .SR_CONFIG_ADDR(SR_CONFIG_ADDR))
+ axi_rate_change (
+ .clk(ce_clk), .reset(ddc_rst), .clear(clear_tx_seqnum[i]), .clear_user(clear_user),
+ .src_sid(src_sid[16*i+15:16*i]), .dst_sid(next_dst_sid[16*i+15:16*i]),
+ .set_stb(out_set_stb), .set_addr(out_set_addr), .set_data(out_set_data),
+ .i_tdata({m_axis_tagged_tag,m_axis_tagged_tdata}), .i_tlast(m_axis_tagged_tlast),
+ .i_tvalid(m_axis_tagged_tvalid), .i_tready(m_axis_tagged_tready),
+ .i_tuser(m_axis_tagged_tuser),
+ .o_tdata({nc,s_axis_data_tdata[i*ITEM_W+:ITEM_W]}), .o_tlast(s_axis_data_tlast[i]), .o_tvalid(s_axis_data_tvalid[i]),
+ .o_tready(s_axis_data_tready[i]), .o_tuser(s_axis_data_tuser[128*i+:128]),
+ .m_axis_data_tdata({sample_in_tuser,sample_in_tdata}), .m_axis_data_tlast(sample_in_tlast),
+ .m_axis_data_tvalid(sample_in_tvalid), .m_axis_data_tready(sample_in_tready),
+ .s_axis_data_tdata({1'b0,sample_out_tdata}), .s_axis_data_tlast(1'b0),
+ .s_axis_data_tvalid(sample_out_tvalid), .s_axis_data_tready(sample_out_tready),
+ .warning_long_throttle(),
+ .error_extra_outputs(),
+ .error_drop_pkt_lockup());
+
+ assign sample_in_eob = m_axis_tagged_tuser[124]; //this should align with last packet output from axi_rate_change
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Digital Down Converter
+ //
+ ////////////////////////////////////////////////////////////
+
+ ddc #(
+ .SR_FREQ_ADDR(SR_FREQ_ADDR),
+ .SR_SCALE_IQ_ADDR(SR_SCALE_IQ_ADDR),
+ .SR_DECIM_ADDR(SR_DECIM_ADDR),
+ .SR_MUX_ADDR(SR_MUX_ADDR),
+ .SR_COEFFS_ADDR(SR_COEFFS_ADDR),
+ .NUM_HB(NUM_HB),
+ .CIC_MAX_DECIM(CIC_MAX_DECIM))
+ ddc (
+ .clk(ce_clk), .reset(ddc_rst),
+ .clear(clear_user | clear_tx_seqnum[i]), // Use AXI Rate Change's clear user to reset block to initial state after EOB
+ .set_stb(out_set_stb), .set_addr(out_set_addr), .set_data(out_set_data),
+ .timed_set_stb(timed_set_stb), .timed_set_addr(timed_set_addr), .timed_set_data(timed_set_data),
+ .sample_in_tdata(sample_in_tdata), .sample_in_tlast(sample_in_tlast),
+ .sample_in_tvalid(sample_in_tvalid), .sample_in_tready(sample_in_tready),
+ .sample_in_tuser(sample_in_tuser), .sample_in_eob(sample_in_eob),
+ .sample_out_tdata(sample_out_tdata), .sample_out_tlast(),
+ .sample_out_tvalid(sample_out_tvalid), .sample_out_tready(sample_out_tready)
+ );
+
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_regs.vh
new file mode 100644
index 000000000..bc1bf4c46
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_regs.vh
@@ -0,0 +1,27 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_ddc_regs (Header)
+//
+// Description: Header file for RFNoC DDC functionality. This includes
+// register offsets, bitfields and constants for the radio components.
+//
+
+// For now, these offsets match the original DDC
+localparam DDC_BASE_ADDR = 'h00;
+localparam DDC_ADDR_W = 8;
+
+localparam RB_COMPAT_NUM = 0;
+localparam RB_NUM_HB = 1;
+localparam RB_CIC_MAX_DECIM = 2;
+localparam SR_N_ADDR = 128;
+localparam SR_M_ADDR = 129;
+localparam SR_CONFIG_ADDR = 130;
+localparam SR_FREQ_ADDR = 132;
+localparam SR_SCALE_IQ_ADDR = 133;
+localparam SR_DECIM_ADDR = 134;
+localparam SR_MUX_ADDR = 135;
+localparam SR_COEFFS_ADDR = 136;
+
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_tb.sv
new file mode 100644
index 000000000..8b0790909
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_tb.sv
@@ -0,0 +1,386 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_ddc_tb
+//
+// Description: Testbench for rfnoc_block_ddc
+//
+
+
+module rfnoc_block_ddc_tb();
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+
+ `include "rfnoc_block_ddc_regs.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Simulation parameters
+ localparam real CHDR_CLK_PER = 5.0; // CHDR clock rate
+ localparam real DDC_CLK_PER = 4.0; // DUC IP clock rate
+ localparam int EXTENDED_TEST = 0; // Perform a longer test
+ localparam int SPP = 256; // Samples per packet
+ localparam int PKT_SIZE_BYTES = SPP*4; // Bytes per packet
+ localparam int STALL_PROB = 25; // BFM stall probability
+
+ // Block configuration
+ localparam int CHDR_W = 64;
+ localparam int THIS_PORTID = 'h123;
+ localparam int MTU = 8;
+ localparam int NUM_PORTS = 1;
+ localparam int NUM_HB = 3;
+ localparam int CIC_MAX_DECIM = 255;
+
+
+ //---------------------------------------------------------------------------
+ // Clocks
+ //---------------------------------------------------------------------------
+
+ bit rfnoc_chdr_clk;
+ bit rfnoc_ctrl_clk;
+ bit ce_clk;
+
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst());
+ sim_clock_gen #(DDC_CLK_PER) ddc_clk_gen (.clk(ce_clk), .rst());
+
+
+ //---------------------------------------------------------------------------
+ // Bus Functional Models
+ //---------------------------------------------------------------------------
+
+ RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk);
+ AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+
+ // Bus functional model for a software block controller
+ RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
+ new(backend, m_ctrl, s_ctrl);
+
+ // Connect block controller to BFMs
+ for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
+ initial begin
+ blk_ctrl.connect_master_data_port(i, m_chdr[i], PKT_SIZE_BYTES);
+ blk_ctrl.connect_slave_data_port(i, s_chdr[i]);
+ blk_ctrl.set_master_stall_prob(i, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(i, STALL_PROB);
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // DUT
+ //---------------------------------------------------------------------------
+
+ logic [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tready;
+
+ logic [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tready;
+
+ // Map the array of BFMs to a flat vector for the DUT
+ genvar i;
+ for (i = 0; i < NUM_PORTS; i++) begin : gen_dut_connections
+ // Connect BFM master to DUT slave port
+ assign s_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata;
+ assign s_rfnoc_chdr_tlast[i] = m_chdr[i].tlast;
+ assign s_rfnoc_chdr_tvalid[i] = m_chdr[i].tvalid;
+ assign m_chdr[i].tready = s_rfnoc_chdr_tready[i];
+
+ // Connect BFM slave to DUT master port
+ assign s_chdr[i].tdata = m_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W];
+ assign s_chdr[i].tlast = m_rfnoc_chdr_tlast[i];
+ assign s_chdr[i].tvalid = m_rfnoc_chdr_tvalid[i];
+ assign m_rfnoc_chdr_tready[i] = s_chdr[i].tready;
+ end
+
+ rfnoc_block_ddc #(
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .NUM_PORTS (NUM_PORTS),
+ .MTU (MTU),
+ .NUM_HB (NUM_HB),
+ .CIC_MAX_DECIM (CIC_MAX_DECIM)
+ ) rfnoc_block_ddc_i (
+ .rfnoc_chdr_clk (backend.chdr_clk),
+ .ce_clk (ce_clk),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .rfnoc_core_config (backend.cfg),
+ .rfnoc_core_status (backend.sts),
+ .rfnoc_ctrl_clk (backend.ctrl_clk),
+ .s_rfnoc_ctrl_tdata (m_ctrl.tdata),
+ .s_rfnoc_ctrl_tlast (m_ctrl.tlast),
+ .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid),
+ .s_rfnoc_ctrl_tready (m_ctrl.tready),
+ .m_rfnoc_ctrl_tdata (s_ctrl.tdata),
+ .m_rfnoc_ctrl_tlast (s_ctrl.tlast),
+ .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid),
+ .m_rfnoc_ctrl_tready (s_ctrl.tready)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Helper Tasks
+ //---------------------------------------------------------------------------
+
+ // Translate the desired register access to a ctrlport write request.
+ task automatic write_reg(int port, byte addr, bit [31:0] value);
+ blk_ctrl.reg_write(256*8*port + addr*8, value);
+ endtask : write_reg
+
+
+ // Translate the desired register access to a ctrlport read request.
+ task automatic read_user_reg(int port, byte addr, output logic [63:0] value);
+ blk_ctrl.reg_read(256*8*port + addr*8 + 0, value[31: 0]);
+ blk_ctrl.reg_read(256*8*port + addr*8 + 4, value[63:32]);
+ endtask : read_user_reg
+
+
+ task automatic set_decim_rate(int port, input int decim_rate);
+ logic [7:0] cic_rate;
+ logic [1:0] hb_enables;
+ int _decim_rate;
+
+ cic_rate = 8'd0;
+ hb_enables = 2'b0;
+ _decim_rate = decim_rate;
+
+ // Calculate which half bands to enable and whatever is left over set the CIC
+ while ((_decim_rate[0] == 0) && (hb_enables < NUM_HB)) begin
+ hb_enables += 1'b1;
+ _decim_rate = _decim_rate >> 1;
+ end
+ // CIC rate cannot be set to 0
+ cic_rate = (_decim_rate[7:0] == 8'd0) ? 8'd1 : _decim_rate[7:0];
+ `ASSERT_ERROR(
+ hb_enables <= NUM_HB,
+ "Enabled halfbands may not exceed total number of half bands."
+ );
+ `ASSERT_ERROR(
+ cic_rate > 0 && cic_rate <= CIC_MAX_DECIM,
+ "CIC Decimation rate must be positive, not exceed the max cic decimation rate, and cannot equal 0!"
+ );
+
+ // Setup DDC
+ $display("Set decimation to %0d", decim_rate);
+ $display("- Number of enabled HBs: %0d", hb_enables);
+ $display("- CIC Rate: %0d", cic_rate);
+ write_reg(port, SR_N_ADDR, decim_rate); // Set decimation rate in AXI rate change
+ write_reg(port, SR_DECIM_ADDR, {hb_enables,cic_rate}); // Enable HBs, set CIC rate
+ endtask
+
+
+ task automatic send_ramp (
+ input int unsigned port,
+ input int unsigned decim_rate,
+ // (Optional) For testing passing through partial packets
+ input logic drop_partial_packet = 1'b0,
+ input int unsigned extra_samples = 0
+ );
+ set_decim_rate(port, decim_rate);
+
+ // Setup DDC
+ write_reg(port, SR_CONFIG_ADDR, 32'd1); // Enable clear EOB
+ write_reg(port, SR_FREQ_ADDR, 32'd0); // Phase increment
+ write_reg(port, SR_SCALE_IQ_ADDR, (1 << 14)); // Scaling, set to 1
+
+ // Send a short ramp, should pass through unchanged
+ fork
+ begin
+ chdr_word_t send_payload[$];
+ packet_info_t pkt_info;
+
+ pkt_info = 0;
+ for (int i = 0; i < decim_rate*(PKT_SIZE_BYTES/8 + extra_samples); i++) begin
+ send_payload.push_back({16'(2*i/decim_rate), 16'(2*i/decim_rate), 16'((2*i+1)/decim_rate), 16'((2*i+1)/decim_rate)});
+ end
+ $display("Send ramp (%0d words)", send_payload.size());
+ pkt_info.eob = 1;
+ blk_ctrl.send_packets(port, send_payload, /*data_bytes*/, /*metadata*/, pkt_info);
+ blk_ctrl.wait_complete(port);
+ $display("Send ramp complete");
+ end
+ begin
+ string s;
+ logic [63:0] samples, samples_old;
+ chdr_word_t recv_payload[$], temp_payload[$];
+ chdr_word_t metadata[$];
+ int data_bytes;
+ packet_info_t pkt_info;
+
+ $display("Check ramp");
+ if (~drop_partial_packet && (extra_samples > 0)) begin
+ blk_ctrl.recv_adv(port, temp_payload, data_bytes, metadata, pkt_info);
+ $sformat(s, "Invalid EOB state! Expected %b, Received: %b", 1'b0, pkt_info.eob);
+ `ASSERT_ERROR(pkt_info.eob == 1'b0, s);
+ end
+ $display("Receiving packet");
+ blk_ctrl.recv_adv(port, recv_payload, data_bytes, metadata, pkt_info);
+ $display("Received!");
+ $sformat(s, "Invalid EOB state! Expected %b, Received: %b", 1'b1, pkt_info.eob);
+ `ASSERT_ERROR(pkt_info.eob == 1'b1, s);
+ recv_payload = {temp_payload, recv_payload};
+ if (drop_partial_packet) begin
+ $sformat(s, "Incorrect packet size! Expected: %0d, Actual: %0d", PKT_SIZE_BYTES/8, recv_payload.size());
+ `ASSERT_ERROR(recv_payload.size() == PKT_SIZE_BYTES/8, s);
+ end else begin
+ $sformat(s, "Incorrect packet size! Expected: %0d, Actual: %0d", PKT_SIZE_BYTES/8, recv_payload.size() + extra_samples);
+ `ASSERT_ERROR(recv_payload.size() == PKT_SIZE_BYTES/8 + extra_samples, s);
+ end
+ samples = 64'd0;
+ samples_old = 64'd0;
+ for (int i = 0; i < PKT_SIZE_BYTES/8; i++) begin
+ samples = recv_payload[i];
+ for (int j = 0; j < 4; j++) begin
+ // Need to check a range of values due to imperfect gain compensation
+ $sformat(s, "Ramp word %0d invalid! Expected: %0d-%0d, Received: %0d", 2*i,
+ samples_old[16*j +: 16], samples_old[16*j +: 16]+16'd4, samples[16*j +: 16]);
+ `ASSERT_ERROR((samples_old[16*j +: 16]+16'd4 >= samples[16*j +: 16]) && (samples >= samples_old[16*j +: 16]), s);
+ end
+ samples_old = samples;
+ end
+ $display("Check complete");
+ end
+ join
+ endtask
+
+
+ //---------------------------------------------------------------------------
+ // Test Process
+ //---------------------------------------------------------------------------
+
+ initial begin : tb_main
+ const int port = 0;
+ test.start_tb("rfnoc_block_ddc_tb");
+
+ // Start the BFMs running
+ blk_ctrl.run();
+
+
+ //-------------------------------------------------------------------------
+ // Reset
+ //-------------------------------------------------------------------------
+
+ test.start_test("Wait for Reset", 10us);
+ fork
+ blk_ctrl.reset_chdr();
+ blk_ctrl.reset_ctrl();
+ join;
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Check NoC ID and Block Info
+ //-------------------------------------------------------------------------
+
+ test.start_test("Verify Block Info", 2us);
+ `ASSERT_ERROR(blk_ctrl.get_noc_id() == rfnoc_block_ddc_i.NOC_ID, "Incorrect NOC_ID Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect NUM_DATA_I Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect NUM_DATA_O Value");
+ `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value");
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Test read-back regs
+ //-------------------------------------------------------------------------
+
+ begin
+ logic [63:0] val64;
+ test.start_test("Test registers", 10us);
+ read_user_reg(port, RB_NUM_HB, val64);
+ `ASSERT_ERROR(val64 == NUM_HB, "Register NUM_HB didn't read back expected value");
+ read_user_reg(port, RB_CIC_MAX_DECIM, val64);
+ `ASSERT_ERROR(val64 == CIC_MAX_DECIM, "Register CIC_MAX_DECIM didn't read back expected value");
+ test.end_test();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Test various decimation rates
+ //-------------------------------------------------------------------------
+
+ begin
+ test.start_test("Decimate by 1, 2, 3, 4, 6, 8, 12, 13, 16, 24, 40, 255, 2040", 0.5ms);
+
+ $display("Note: This test will take a long time!");
+
+ // List of rates to catch most issues
+ send_ramp(port, 1); // HBs enabled: 0, CIC rate: 1
+ send_ramp(port, 2); // HBs enabled: 1, CIC rate: 1
+ send_ramp(port, 3); // HBs enabled: 0, CIC rate: 3
+ send_ramp(port, 4); // HBs enabled: 2, CIC rate: 1
+ if (EXTENDED_TEST) send_ramp(port, 6); // HBs enabled: 1, CIC rate: 3
+ send_ramp(port, 8); // HBs enabled: 3, CIC rate: 1
+ send_ramp(port, 12); // HBs enabled: 2, CIC rate: 3
+ send_ramp(port, 13); // HBs enabled: 0, CIC rate: 13
+ if (EXTENDED_TEST) send_ramp(port, 16); // HBs enabled: 3, CIC rate: 2
+ if (EXTENDED_TEST) send_ramp(port, 24); // HBs enabled: 3, CIC rate: 3
+ send_ramp(port, 40); // HBs enabled: 3, CIC rate: 5
+ if (EXTENDED_TEST) send_ramp(port, 200); // HBs enabled: 3, CIC rate: 25
+ send_ramp(port, 255); // HBs enabled: 0, CIC rate: 255
+ if (EXTENDED_TEST) send_ramp(port, 2040); // HBs enabled: 3, CIC rate: 255
+
+ test.end_test();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Test timed tune
+ //-------------------------------------------------------------------------
+
+ // This test has not been implemented because the RFNoC FFT has not been
+ // ported yet.
+
+
+ //-------------------------------------------------------------------------
+ // Test passing through a partial packet
+ //-------------------------------------------------------------------------
+
+ test.start_test("Pass through partial packet");
+ send_ramp(port, 2, 0, 4);
+ send_ramp(port, 3, 0, 4);
+ send_ramp(port, 4, 0, 4);
+ if (EXTENDED_TEST) send_ramp(port, 8, 0, 4);
+ send_ramp(port, 13, 0, 4);
+ if (EXTENDED_TEST) send_ramp(port, 24, 0, 4);
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Finish
+ //-------------------------------------------------------------------------
+
+ // End the TB, but don't $finish, since we don't want to kill other
+ // instances of this testbench that may be running.
+ test.end_tb(0);
+
+ // Kill the clocks to end this instance of the testbench
+ rfnoc_chdr_clk_gen.kill();
+ rfnoc_ctrl_clk_gen.kill();
+ ddc_clk_gen.kill();
+ end
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile
new file mode 100644
index 000000000..6d1da3d60
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile
@@ -0,0 +1,67 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../../../top)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# IP Specific
+#-------------------------------------------------
+# If simulation contains IP, define the IP_DIR and point
+# it to the base level IP directory
+LIB_IP_DIR = $(BASE_DIR)/../lib/ip
+
+# Include makefiles and sources for all IP components
+# *after* defining the LIB_IP_DIR
+include $(LIB_IP_DIR)/axi_hb47/Makefile.inc
+include $(LIB_IP_DIR)/complex_multiplier_dds/Makefile.inc
+include $(LIB_IP_DIR)/dds_sin_cos_lut_only/Makefile.inc
+include $(BASE_DIR)/x300/coregen_dsp/Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(LIB_IP_AXI_HB47_SRCS) \
+$(LIB_IP_COMPLEX_MULTIPLIER_DDS_SRCS) \
+$(LIB_IP_DDS_SIN_COS_LUT_ONLY_SRCS) \
+$(COREGEN_DSP_SRCS) \
+)
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
+include Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_BLOCK_DUC_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = rfnoc_block_duc_tb
+
+# Add test bench, user design under test, and
+# additional user created files
+SIM_SRCS = \
+$(abspath rfnoc_block_duc_tb.sv)
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+# Include all simulator specific makefiles here
+# Each should define a unique target to simulate
+# e.g. xsim, vsim, etc and a common "clean" target
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile.srcs
new file mode 100644
index 000000000..69b6eaece
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile.srcs
@@ -0,0 +1,11 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+RFNOC_BLOCK_DUC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_duc/, \
+../rfnoc_block_ddc/noc_shell_ddc.v \
+rfnoc_block_duc_regs.vh \
+rfnoc_block_duc.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc.v
new file mode 100644
index 000000000..400e9d270
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc.v
@@ -0,0 +1,387 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_duc
+//
+// Description: An digital up-converter block for RFNoC.
+//
+// Parameters:
+//
+// THIS_PORTID : Control crossbar port to which this block is connected
+// CHDR_W : AXIS CHDR interface data width
+// NUM_PORTS : Number of DUC signal processing chains
+// MTU : Maximum transmission unit (i.e., maximum packet size) in
+// CHDR words is 2**MTU.
+// CTRL_FIFO_SIZE : Size of the Control Port slave FIFO. This affects the
+// number of outstanding commands that can be pending.
+// NUM_HB : Number of half-band filter blocks to include (0-3)
+// CIC_MAX_INTERP : Maximum interpolation to support in the CIC filter
+//
+
+module rfnoc_block_duc #(
+ parameter THIS_PORTID = 0,
+ parameter CHDR_W = 64,
+ parameter NUM_PORTS = 2,
+ parameter MTU = 10,
+ parameter CTRL_FIFO_SIZE = 6,
+ parameter NUM_HB = 2,
+ parameter CIC_MAX_INTERP = 128
+) (
+ //---------------------------------------------------------------------------
+ // AXIS CHDR Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_chdr_clk,
+ input wire ce_clk,
+
+ // CHDR inputs from framework
+ input wire [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tready,
+
+ // CHDR outputs to framework
+ output wire [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
+
+ // Backend interface
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+
+ //---------------------------------------------------------------------------
+ // AXIS CTRL Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_ctrl_clk,
+
+ // CTRL port requests from framework
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+
+ // CTRL port requests to framework
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready
+);
+
+ // These are the only supported values for now
+ localparam ITEM_W = 32;
+ localparam NIPC = 1;
+
+ localparam NOC_ID = 'hD0C0_0000;
+
+ localparam COMPAT_MAJOR = 16'h0;
+ localparam COMPAT_MINOR = 16'h0;
+
+ `include "rfnoc_block_duc_regs.vh"
+ `include "../../core/rfnoc_axis_ctrl_utils.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Signal Declarations
+ //---------------------------------------------------------------------------
+
+ wire rfnoc_chdr_rst;
+
+ wire ctrlport_req_wr;
+ wire ctrlport_req_rd;
+ wire [19:0] ctrlport_req_addr;
+ wire [31:0] ctrlport_req_data;
+ wire ctrlport_req_has_time;
+ wire [63:0] ctrlport_req_time;
+ wire ctrlport_resp_ack;
+ wire [31:0] ctrlport_resp_data;
+
+ wire [NUM_PORTS*ITEM_W-1:0] m_axis_data_tdata;
+ wire [ NUM_PORTS-1:0] m_axis_data_tlast;
+ wire [ NUM_PORTS-1:0] m_axis_data_tvalid;
+ wire [ NUM_PORTS-1:0] m_axis_data_tready;
+ wire [ NUM_PORTS*64-1:0] m_axis_data_ttimestamp;
+ wire [ NUM_PORTS-1:0] m_axis_data_thas_time;
+ wire [ NUM_PORTS*16-1:0] m_axis_data_tlength;
+ wire [ NUM_PORTS-1:0] m_axis_data_teob;
+ wire [ NUM_PORTS*128-1:0] m_axis_data_tuser;
+
+ wire [NUM_PORTS*ITEM_W-1:0] s_axis_data_tdata;
+ wire [ NUM_PORTS-1:0] s_axis_data_tlast;
+ wire [ NUM_PORTS-1:0] s_axis_data_tvalid;
+ wire [ NUM_PORTS-1:0] s_axis_data_tready;
+ wire [ NUM_PORTS*128-1:0] s_axis_data_tuser;
+ wire [ NUM_PORTS-1:0] s_axis_data_teob;
+ wire [ NUM_PORTS*64-1:0] s_axis_data_ttimestamp;
+ wire [ NUM_PORTS-1:0] s_axis_data_thas_time;
+
+ wire duc_rst;
+
+ // Cross the CHDR reset to the ce_clk domain
+ synchronizer duc_rst_sync_i (
+ .clk (ce_clk),
+ .rst (1'b0),
+ .in (rfnoc_chdr_rst),
+ .out (duc_rst)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // NoC Shell
+ //---------------------------------------------------------------------------
+
+ // TODO: Replace noc_shell_radio with a customized block
+ noc_shell_ddc #(
+ .NOC_ID (NOC_ID),
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .CTRLPORT_SLV_EN (0),
+ .CTRLPORT_MST_EN (1),
+ .CTRL_FIFO_SIZE (CTRL_FIFO_SIZE),
+ .NUM_DATA_I (NUM_PORTS),
+ .NUM_DATA_O (NUM_PORTS),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .PYLD_FIFO_SIZE (MTU),
+ .MTU (MTU)
+ ) noc_shell_ddc_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_ctrl_rst (),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (duc_rst),
+ .m_ctrlport_req_wr (ctrlport_req_wr),
+ .m_ctrlport_req_rd (ctrlport_req_rd),
+ .m_ctrlport_req_addr (ctrlport_req_addr),
+ .m_ctrlport_req_data (ctrlport_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (ctrlport_req_has_time),
+ .m_ctrlport_req_time (ctrlport_req_time),
+ .m_ctrlport_resp_ack (ctrlport_resp_ack),
+ .m_ctrlport_resp_status (AXIS_CTRL_STS_OKAY),
+ .m_ctrlport_resp_data (ctrlport_resp_data),
+ .s_ctrlport_req_wr (1'b0),
+ .s_ctrlport_req_rd (1'b0),
+ .s_ctrlport_req_addr (20'b0),
+ .s_ctrlport_req_portid (10'b0),
+ .s_ctrlport_req_rem_epid (16'b0),
+ .s_ctrlport_req_rem_portid (10'b0),
+ .s_ctrlport_req_data (32'b0),
+ .s_ctrlport_req_byte_en (4'b0),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (),
+ .axis_data_clk (ce_clk),
+ .axis_data_rst (duc_rst),
+ .m_axis_tdata (m_axis_data_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (m_axis_data_tlast),
+ .m_axis_tvalid (m_axis_data_tvalid),
+ .m_axis_tready (m_axis_data_tready),
+ .m_axis_ttimestamp (m_axis_data_ttimestamp),
+ .m_axis_thas_time (m_axis_data_thas_time),
+ .m_axis_tlength (m_axis_data_tlength),
+ .m_axis_teov (),
+ .m_axis_teob (m_axis_data_teob),
+ .s_axis_tdata (s_axis_data_tdata),
+ .s_axis_tkeep ({NUM_PORTS*NIPC{1'b1}}),
+ .s_axis_tlast (s_axis_data_tlast),
+ .s_axis_tvalid (s_axis_data_tvalid),
+ .s_axis_tready (s_axis_data_tready),
+ .s_axis_ttimestamp (s_axis_data_ttimestamp),
+ .s_axis_thas_time (s_axis_data_thas_time),
+ .s_axis_teov ({NUM_PORTS{1'b0}}),
+ .s_axis_teob (s_axis_data_teob)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Register Translation
+ //---------------------------------------------------------------------------
+ //
+ // Each DUC block is allocated an address spaces. This block translates CTRL
+ // port transactions in that space to settings bus.
+ //
+ //---------------------------------------------------------------------------
+
+ wire [ 8*NUM_PORTS-1:0] set_addr;
+ wire [32*NUM_PORTS-1:0] set_data;
+ wire [ NUM_PORTS-1:0] set_has_time;
+ wire [ NUM_PORTS-1:0] set_stb;
+ wire [64*NUM_PORTS-1:0] set_time;
+ wire [ 8*NUM_PORTS-1:0] rb_addr;
+ reg [64*NUM_PORTS-1:0] rb_data;
+
+ ctrlport_to_settings_bus # (
+ .NUM_PORTS (NUM_PORTS)
+ ) ctrlport_to_settings_bus_i (
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (duc_rst),
+ .s_ctrlport_req_wr (ctrlport_req_wr),
+ .s_ctrlport_req_rd (ctrlport_req_rd),
+ .s_ctrlport_req_addr (ctrlport_req_addr),
+ .s_ctrlport_req_data (ctrlport_req_data),
+ .s_ctrlport_req_has_time (ctrlport_req_has_time),
+ .s_ctrlport_req_time (ctrlport_req_time),
+ .s_ctrlport_resp_ack (ctrlport_resp_ack),
+ .s_ctrlport_resp_data (ctrlport_resp_data),
+ .set_data (set_data),
+ .set_addr (set_addr),
+ .set_stb (set_stb),
+ .set_time (set_time),
+ .set_has_time (set_has_time),
+ .rb_stb ({NUM_PORTS{1'b1}}),
+ .rb_addr (rb_addr),
+ .rb_data (rb_data));
+
+
+ //---------------------------------------------------------------------------
+ // DUC Implementation
+ //---------------------------------------------------------------------------
+
+ // Unused signals
+ wire [ NUM_PORTS-1:0] clear_tx_seqnum = 0;
+ wire [16*NUM_PORTS-1:0] src_sid = 0;
+ wire [16*NUM_PORTS-1:0] next_dst_sid = 0;
+
+ localparam MAX_M = CIC_MAX_INTERP * 2<<(NUM_HB-1);
+
+ genvar i;
+ generate
+ for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_duc_chains
+ wire clear_user;
+ wire clear_duc = clear_tx_seqnum[i] | clear_user;
+
+ wire set_stb_int = set_stb[i];
+ wire [7:0] set_addr_int = set_addr[8*i+7:8*i];
+ wire [31:0] set_data_int = set_data[32*i+31:32*i];
+ wire [63:0] set_time_int = set_time[64*i+63:64*i];
+ wire set_has_time_int = set_has_time[i];
+
+ // Build the expected tuser CHDR header
+ cvita_hdr_encoder cvita_hdr_encoder_i (
+ .pkt_type (2'b0),
+ .eob (m_axis_data_teob[i]),
+ .has_time (m_axis_data_thas_time[i]),
+ .seqnum (12'b0),
+ .payload_length (m_axis_data_tlength[16*i +: 16]),
+ .src_sid (16'b0),
+ .dst_sid (16'b0),
+ .vita_time (m_axis_data_ttimestamp[64*i +: 64]),
+ .header (m_axis_data_tuser[128*i+:128])
+ );
+
+ // Extract bit fields from outgoing tuser CHDR header
+ assign s_axis_data_teob[i] = s_axis_data_tuser[128*i+124 +: 1];
+ assign s_axis_data_thas_time[i] = s_axis_data_tuser[128*i+125 +: 1];
+ assign s_axis_data_ttimestamp[64*i+:64] = s_axis_data_tuser[128*i+ 0 +: 64];
+
+ // TODO Readback register for number of FIR filter taps
+ always @(*) begin
+ case(rb_addr[i*8+7:i*8])
+ RB_COMPAT_NUM : rb_data[i*64+63:i*64] <= {COMPAT_MAJOR, COMPAT_MINOR};
+ RB_NUM_HB : rb_data[i*64+63:i*64] <= NUM_HB;
+ RB_CIC_MAX_INTERP : rb_data[i*64+63:i*64] <= CIC_MAX_INTERP;
+ default : rb_data[i*64+63:i*64] <= 64'h0BADC0DE0BADC0DE;
+ endcase
+ end
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Timed CORDIC
+ // - Implements timed cordic tunes. Placed between AXI Wrapper
+ // and AXI Rate Change due to it needing access to the
+ // vita time of the samples.
+ //
+ ////////////////////////////////////////////////////////////
+ wire [31:0] m_axis_rc_tdata;
+ wire m_axis_rc_tlast;
+ wire m_axis_rc_tvalid;
+ wire m_axis_rc_tready;
+ wire [127:0] m_axis_rc_tuser;
+
+ dds_timed #(
+ .SR_FREQ_ADDR(SR_FREQ_ADDR),
+ .SR_SCALE_IQ_ADDR(SR_SCALE_IQ_ADDR))
+ dds_timed (
+ .clk(ce_clk), .reset(duc_rst), .clear(clear_tx_seqnum[i]),
+ .timed_cmd_fifo_full(),
+ .set_stb(set_stb_int), .set_addr(set_addr_int), .set_data(set_data_int),
+ .set_time(set_time_int), .set_has_time(set_has_time_int),
+ .i_tdata(m_axis_rc_tdata), .i_tlast(m_axis_rc_tlast), .i_tvalid(m_axis_rc_tvalid),
+ .i_tready(m_axis_rc_tready), .i_tuser(m_axis_rc_tuser),
+ .o_tdata(s_axis_data_tdata[ITEM_W*i+:ITEM_W]), .o_tlast(s_axis_data_tlast[i]), .o_tvalid(s_axis_data_tvalid[i]),
+ .o_tready(s_axis_data_tready[i]), .o_tuser(s_axis_data_tuser[128*i+:128]));
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Increase Rate
+ //
+ ////////////////////////////////////////////////////////////
+ wire [31:0] sample_tdata, sample_duc_tdata;
+ wire sample_tvalid, sample_tready;
+ wire sample_duc_tvalid, sample_duc_tready;
+ axi_rate_change #(
+ .WIDTH(32),
+ .MAX_N(1),
+ .MAX_M(MAX_M),
+ .SR_N_ADDR(SR_N_ADDR),
+ .SR_M_ADDR(SR_M_ADDR),
+ .SR_CONFIG_ADDR(SR_CONFIG_ADDR))
+ axi_rate_change (
+ .clk(ce_clk), .reset(duc_rst), .clear(clear_tx_seqnum[i]), .clear_user(clear_user),
+ .src_sid(src_sid[16*i+15:16*i]), .dst_sid(next_dst_sid[16*i+15:16*i]),
+ .set_stb(set_stb_int), .set_addr(set_addr_int), .set_data(set_data_int),
+ .i_tdata(m_axis_data_tdata[ITEM_W*i+:ITEM_W]), .i_tlast(m_axis_data_tlast[i]), .i_tvalid(m_axis_data_tvalid[i]),
+ .i_tready(m_axis_data_tready[i]), .i_tuser(m_axis_data_tuser[128*i+:128]),
+ .o_tdata(m_axis_rc_tdata), .o_tlast(m_axis_rc_tlast), .o_tvalid(m_axis_rc_tvalid),
+ .o_tready(m_axis_rc_tready), .o_tuser(m_axis_rc_tuser),
+ .m_axis_data_tdata({sample_tdata}), .m_axis_data_tlast(), .m_axis_data_tvalid(sample_tvalid),
+ .m_axis_data_tready(sample_tready),
+ .s_axis_data_tdata(sample_duc_tdata), .s_axis_data_tlast(1'b0), .s_axis_data_tvalid(sample_duc_tvalid),
+ .s_axis_data_tready(sample_duc_tready),
+ .warning_long_throttle(), .error_extra_outputs(), .error_drop_pkt_lockup());
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Digital Up Converter
+ //
+ ////////////////////////////////////////////////////////////
+ duc #(
+ .SR_INTERP_ADDR(SR_INTERP_ADDR),
+ .NUM_HB(NUM_HB),
+ .CIC_MAX_INTERP(CIC_MAX_INTERP))
+ duc (
+ .clk(ce_clk), .reset(duc_rst), .clear(clear_duc),
+ .set_stb(set_stb_int), .set_addr(set_addr_int), .set_data(set_data_int),
+ .i_tdata(sample_tdata), .i_tuser(128'b0), .i_tvalid(sample_tvalid), .i_tready(sample_tready),
+ .o_tdata(sample_duc_tdata), .o_tuser(), .o_tvalid(sample_duc_tvalid), .o_tready(sample_duc_tready));
+
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_regs.vh
new file mode 100644
index 000000000..fa239857e
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_regs.vh
@@ -0,0 +1,25 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_duc_regs (Header)
+//
+// Description: Header file for RFNoC DUC functionality. This includes
+// register offsets, bitfields and constants for the radio components.
+//
+
+// For now, these offsets match the original DUC
+localparam DUC_BASE_ADDR = 'h00;
+localparam DUC_ADDR_W = 8;
+
+localparam RB_COMPAT_NUM = 0;
+localparam RB_NUM_HB = 1;
+localparam RB_CIC_MAX_INTERP = 2;
+localparam SR_N_ADDR = 128;
+localparam SR_M_ADDR = 129;
+localparam SR_CONFIG_ADDR = 130;
+localparam SR_INTERP_ADDR = 131;
+localparam SR_FREQ_ADDR = 132;
+localparam SR_SCALE_IQ_ADDR = 133;
+
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv
new file mode 100644
index 000000000..5bca3f03b
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv
@@ -0,0 +1,387 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_duc_tb
+//
+// Description: Testbench for rfnoc_block_duc
+//
+
+
+module rfnoc_block_duc_tb();
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+
+ `include "rfnoc_block_duc_regs.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Simulation parameters
+ localparam real CHDR_CLK_PER = 5.0; // CHDR clock rate
+ localparam real DUC_CLK_PER = 4.0; // DUC IP clock rate
+ localparam int EXTENDED_TEST = 0; // Perform a longer test
+ localparam int SPP = 128; // Samples per packet
+ localparam int PKT_SIZE_BYTES = SPP*4; // Bytes per packet
+ localparam int STALL_PROB = 25; // BFM stall probability
+
+ // Block configuration
+ localparam int CHDR_W = 64;
+ localparam int THIS_PORTID = 'h123;
+ localparam int MTU = 8;
+ localparam int NUM_PORTS = 1;
+ localparam int NUM_HB = 3;
+ localparam int CIC_MAX_INTERP = 128;
+
+
+ //---------------------------------------------------------------------------
+ // Clocks
+ //---------------------------------------------------------------------------
+
+ bit rfnoc_chdr_clk;
+ bit rfnoc_ctrl_clk;
+
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst());
+ sim_clock_gen #(DUC_CLK_PER) duc_clk_gen (.clk(ce_clk), .rst());
+
+
+ //---------------------------------------------------------------------------
+ // Bus Functional Models
+ //---------------------------------------------------------------------------
+
+ RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk);
+ AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+
+ // Bus functional model for a software block controller
+ RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
+ new(backend, m_ctrl, s_ctrl);
+
+ // Connect block controller to BFMs
+ for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
+ initial begin
+ blk_ctrl.connect_master_data_port(i, m_chdr[i], PKT_SIZE_BYTES);
+ blk_ctrl.connect_slave_data_port(i, s_chdr[i]);
+ blk_ctrl.set_master_stall_prob(i, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(i, STALL_PROB);
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // DUT
+ //---------------------------------------------------------------------------
+
+ logic [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tready;
+
+ logic [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tready;
+
+ // Map the array of BFMs to a flat vector for the DUT
+ genvar i;
+ for (i = 0; i < NUM_PORTS; i++) begin : gen_dut_connections
+ // Connect BFM master to DUT slave port
+ assign s_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata;
+ assign s_rfnoc_chdr_tlast[i] = m_chdr[i].tlast;
+ assign s_rfnoc_chdr_tvalid[i] = m_chdr[i].tvalid;
+ assign m_chdr[i].tready = s_rfnoc_chdr_tready[i];
+
+ // Connect BFM slave to DUT master port
+ assign s_chdr[i].tdata = m_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W];
+ assign s_chdr[i].tlast = m_rfnoc_chdr_tlast[i];
+ assign s_chdr[i].tvalid = m_rfnoc_chdr_tvalid[i];
+ assign m_rfnoc_chdr_tready[i] = s_chdr[i].tready;
+ end
+
+ rfnoc_block_duc #(
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .NUM_PORTS (NUM_PORTS),
+ .MTU (MTU),
+ .NUM_HB (NUM_HB),
+ .CIC_MAX_INTERP (CIC_MAX_INTERP)
+ ) rfnoc_block_duc_i (
+ .rfnoc_chdr_clk (backend.chdr_clk),
+ .ce_clk (ce_clk),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .rfnoc_core_config (backend.cfg),
+ .rfnoc_core_status (backend.sts),
+ .rfnoc_ctrl_clk (backend.ctrl_clk),
+ .s_rfnoc_ctrl_tdata (m_ctrl.tdata),
+ .s_rfnoc_ctrl_tlast (m_ctrl.tlast),
+ .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid),
+ .s_rfnoc_ctrl_tready (m_ctrl.tready),
+ .m_rfnoc_ctrl_tdata (s_ctrl.tdata),
+ .m_rfnoc_ctrl_tlast (s_ctrl.tlast),
+ .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid),
+ .m_rfnoc_ctrl_tready (s_ctrl.tready)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Helper Tasks
+ //---------------------------------------------------------------------------
+
+ // Translate the desired register access to a ctrlport write request.
+ task automatic write_reg(int port, byte unsigned addr, bit [31:0] value);
+ blk_ctrl.reg_write(256*8*port + addr*8, value);
+ endtask : write_reg
+
+
+ // Translate the desired register access to a ctrlport read request.
+ task automatic read_user_reg(int port, byte unsigned addr, output logic [63:0] value);
+ blk_ctrl.reg_read(256*8*port + addr*8 + 0, value[31: 0]);
+ blk_ctrl.reg_read(256*8*port + addr*8 + 4, value[63:32]);
+ endtask : read_user_reg
+
+
+ // Set the interpolation rate
+ task automatic set_interp_rate(int port, int interp_rate);
+ begin
+ logic [7:0] cic_rate = 8'd0;
+ logic [7:0] hb_enables = 2'b0;
+
+ int _interp_rate = interp_rate;
+
+ // Calculate which half bands to enable and whatever is left over set the CIC
+ while ((_interp_rate[0] == 0) && (hb_enables < NUM_HB)) begin
+ hb_enables += 1'b1;
+ _interp_rate = _interp_rate >> 1;
+ end
+
+ // CIC rate cannot be set to 0
+ cic_rate = (_interp_rate[7:0] == 8'd0) ? 8'd1 : _interp_rate[7:0];
+ `ASSERT_ERROR(hb_enables <= NUM_HB, "Enabled halfbands may not exceed total number of half bands.");
+ `ASSERT_ERROR(cic_rate > 0 && cic_rate <= CIC_MAX_INTERP,
+ "CIC Interpolation rate must be positive, not exceed the max cic interpolation rate, and cannot equal 0!");
+
+ // Setup DUC
+ $display("Set interpolation to %0d", interp_rate);
+ $display("- Number of enabled HBs: %0d", hb_enables);
+ $display("- CIC Rate: %0d", cic_rate);
+ write_reg(port, SR_M_ADDR, interp_rate); // Set interpolation rate in AXI rate change
+ write_reg(port, SR_INTERP_ADDR, {hb_enables, cic_rate}); // Enable HBs, set CIC rate
+ end
+ endtask
+
+
+ // Test sending packets of ones
+ task automatic send_ones(int port, int interp_rate, bit has_time);
+ begin
+ const bit [63:0] start_time = 64'h0123456789ABCDEF;
+
+ set_interp_rate(port, interp_rate);
+
+ // Setup DUC
+ write_reg(port, SR_CONFIG_ADDR, 32'd1); // Enable clear EOB
+ write_reg(port, SR_FREQ_ADDR, 32'd0); // CORDIC phase increment
+ write_reg(port, SR_SCALE_IQ_ADDR, (1 << 14)); // Scaling, set to 1
+
+ fork
+ begin
+ chdr_word_t send_payload[$];
+ packet_info_t pkt_info;
+
+ $display("Send ones");
+
+ // Generate a payload of all ones
+ send_payload = {};
+ for (int i = 0; i < PKT_SIZE_BYTES/8; i++) begin
+ send_payload.push_back({16'hffff, 16'hffff, 16'hffff, 16'hffff});
+ end
+
+ // Send two packets with EOB on the second packet
+ pkt_info = 0;
+ pkt_info.has_time = has_time;
+ pkt_info.timestamp = start_time;
+ blk_ctrl.send_packets(port, send_payload, /*data_bytes*/, /*metadata*/, pkt_info);
+ pkt_info.timestamp = start_time + SPP;
+ pkt_info.eob = 1;
+ blk_ctrl.send_packets(port, send_payload, /*data_bytes*/, /*metadata*/, pkt_info);
+
+ $display("Send ones complete");
+ end
+ begin
+ string s;
+ chdr_word_t samples;
+ int data_bytes;
+ chdr_word_t recv_payload[$];
+ chdr_word_t metadata[$];
+ packet_info_t pkt_info;
+
+ $display("Check incoming samples");
+ for (int i = 0; i < 2*interp_rate; i++) begin
+ blk_ctrl.recv_adv(port, recv_payload, data_bytes, metadata, pkt_info);
+
+ // Check the packet size
+ $sformat(s, "incorrect (drop) packet size! expected: %0d, actual: %0d", PKT_SIZE_BYTES/8, recv_payload.size());
+ `ASSERT_ERROR(recv_payload.size() == PKT_SIZE_BYTES/8, s);
+
+ // Check the timestamp
+ if (has_time) begin
+ bit [63:0] expected_time;
+ // Calculate what the timestamp should be
+ expected_time = start_time + i * SPP;
+ $sformat(s, "Incorrect timestamp: has_time = %0d, timestamp = 0x%0X, expected 0x%0X",
+ pkt_info.has_time, pkt_info.timestamp, expected_time);
+ `ASSERT_ERROR(pkt_info.has_time == 1 && pkt_info.timestamp == expected_time, s);
+ end else begin
+ `ASSERT_ERROR(pkt_info.has_time == 0, "Packet has timestamp when it shouldn't");
+ end
+
+ // Check EOB
+ if (i == 2*interp_rate-1) begin
+ `ASSERT_ERROR(pkt_info.eob == 1, "EOB not set on last packet");
+ end else begin
+ `ASSERT_ERROR(pkt_info.eob == 0,
+ $sformatf("EOB unexpectedly set on packet %0d", i));
+ end
+
+ // Check the sample values
+ samples = 64'd0;
+ for (int j = 0; j < PKT_SIZE_BYTES/8; j++) begin
+ samples = recv_payload[j];
+ $sformat(s, "Ramp word %0d invalid! Expected a real value, Received: %0d", 2*j, samples);
+ `ASSERT_ERROR(samples >= 0, s);
+ end
+ end
+ $display("Check complete");
+ end
+ join
+ end
+ endtask
+
+
+ //---------------------------------------------------------------------------
+ // Test Process
+ //---------------------------------------------------------------------------
+
+ initial begin : tb_main
+ const int port = 0;
+ test.start_tb("rfnoc_block_duc_tb");
+
+ // Start the BFMs running
+ blk_ctrl.run();
+
+
+ //-------------------------------------------------------------------------
+ // Reset
+ //-------------------------------------------------------------------------
+
+ test.start_test("Wait for Reset", 10us);
+ fork
+ blk_ctrl.reset_chdr();
+ blk_ctrl.reset_ctrl();
+ join;
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Check NoC ID and Block Info
+ //-------------------------------------------------------------------------
+
+ test.start_test("Verify Block Info", 2us);
+ `ASSERT_ERROR(blk_ctrl.get_noc_id() == rfnoc_block_duc_i.NOC_ID, "Incorrect NOC_ID value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect NUM_DATA_I value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect NUM_DATA_O value");
+ `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU value");
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Test read-back regs
+ //-------------------------------------------------------------------------
+
+ begin
+ logic [63:0] val64;
+ test.start_test("Test registers", 10us);
+ read_user_reg(port, RB_NUM_HB, val64);
+ `ASSERT_ERROR(val64 == NUM_HB, "Register NUM_HB didn't read back expected value");
+ read_user_reg(port, RB_CIC_MAX_INTERP, val64);
+ `ASSERT_ERROR(val64 ==CIC_MAX_INTERP, "Register RB_CIC_MAX_INTERP didn't read back expected value");
+ test.end_test();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Test various interpolation rates (no timestamp)
+ //-------------------------------------------------------------------------
+
+ begin
+ test.start_test("Test interpolation rates (with timestamp)", 0.5ms);
+
+ $display("Note: This test will take a long time!");
+ send_ones(port, 1, 1); // HBs enabled: 0, CIC rate: 1
+ send_ones(port, 2, 1); // HBs enabled: 1, CIC rate: 1
+ send_ones(port, 3, 1); // HBs enabled: 0, CIC rate: 3
+ send_ones(port, 4, 1); // HBs enabled: 2, CIC rate: 1
+ send_ones(port, 6, 1); // HBs enabled: 1, CIC rate: 3
+ send_ones(port, 8, 1); // HBs enabled: 2, CIC rate: 2
+ send_ones(port, 12, 1); // HBs enabled: 2, CIC rate: 3
+ send_ones(port, 13, 1); // HBs enabled: 0, CIC rate: 13
+ send_ones(port, 16, 1); // HBs enabled: 2, CIC rate: 3
+ send_ones(port, 40, 1); // HBs enabled: 2, CIC rate: 20
+
+ test.end_test();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Test various interpolation rates (without timestamp)
+ //-------------------------------------------------------------------------
+
+ begin
+ test.start_test("Test interpolation rates (no timestamp)", 0.5ms);
+
+ send_ones(port, 1, 0); // HBs enabled: 0, CIC rate: 1
+ send_ones(port, 3, 0); // HBs enabled: 0, CIC rate: 3
+
+ test.end_test();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Test timed tune
+ //-------------------------------------------------------------------------
+
+ // This test has not been implemented because the RFNoC FFT has not been
+ // ported yet.
+
+
+ //-------------------------------------------------------------------------
+ // Finish
+ //-------------------------------------------------------------------------
+
+ // End the TB, but don't $finish, since we don't want to kill other
+ // instances of this testbench that may be running.
+ test.end_tb(0);
+
+ // Kill the clocks to end this instance of the testbench
+ rfnoc_chdr_clk_gen.kill();
+ rfnoc_ctrl_clk_gen.kill();
+ duc_clk_gen.kill();
+ end
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/Makefile
new file mode 100644
index 000000000..868246fbd
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/Makefile
@@ -0,0 +1,62 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../../../top)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# IP Specific
+#-------------------------------------------------
+# If simulation contains IP, define the IP_DIR and point
+# it to the base level IP directory
+LIB_IP_DIR = $(BASE_DIR)/../lib/ip
+
+# Include makefiles and sources for all IP components
+# *after* defining the LIB_IP_DIR
+include $(LIB_IP_DIR)/axi_fft/Makefile.inc
+include $(LIB_IP_DIR)/complex_to_magphase/Makefile.inc
+
+DESIGN_SRCS += $(abspath \
+$(LIB_IP_AXI_FFT_OUTS) \
+)
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
+include Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_OOT_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = rfnoc_block_fft_tb
+
+# Add test bench, user design under test, and
+# additional user created files
+SIM_SRCS = \
+$(abspath rfnoc_block_fft_tb.sv)
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+# Include all simulator specific makefiles here
+# Each should define a unique target to simulate
+# e.g. xsim, vsim, etc and a common "clean" target
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/Makefile.srcs
new file mode 100644
index 000000000..21ba967f2
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/Makefile.srcs
@@ -0,0 +1,10 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+RFNOC_OOT_SRCS += $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_fft/, \
+noc_shell_fft.v \
+rfnoc_block_fft.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/noc_shell_fft.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/noc_shell_fft.v
new file mode 100644
index 000000000..37a60ef31
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/noc_shell_fft.v
@@ -0,0 +1,294 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: noc_shell_fft
+//
+
+module noc_shell_fft #(
+ parameter [31:0] NOC_ID = 32 'h0,
+ parameter [ 9:0] THIS_PORTID = 10 'd0,
+ parameter CHDR_W = 64,
+ parameter [ 0:0] CTRLPORT_SLV_EN = 1,
+ parameter [ 0:0] CTRLPORT_MST_EN = 1,
+ parameter SYNC_CLKS = 0,
+ parameter [ 5:0] NUM_DATA_I = 1,
+ parameter [ 5:0] NUM_DATA_O = 1,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter PYLD_FIFO_SIZE = 5,
+ parameter CTXT_FIFO_SIZE = 5,
+ parameter MTU = 10
+) (
+ //---------------------------------------------------------------------------
+ // Framework Interface
+ //---------------------------------------------------------------------------
+
+ // RFNoC Framework Clocks and Resets
+ input wire rfnoc_chdr_clk,
+ output wire rfnoc_chdr_rst,
+ input wire rfnoc_ctrl_clk,
+ output wire rfnoc_ctrl_rst,
+ // RFNoC Backend Interface
+ input wire [ 511:0] rfnoc_core_config,
+ output wire [ 511:0] rfnoc_core_status,
+ // CHDR Input Ports (from framework)
+ input wire [(CHDR_W*NUM_DATA_I)-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tready,
+ // CHDR Output Ports (to framework)
+ output wire [(CHDR_W*NUM_DATA_O)-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tready,
+ // AXIS-Ctrl Input Port (from framework)
+ input wire [ 31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ // AXIS-Ctrl Output Port (to framework)
+ output wire [ 31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+
+ //---------------------------------------------------------------------------
+ // Client Control Port Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+ // Master
+ output wire m_ctrlport_req_wr,
+ output wire m_ctrlport_req_rd,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [31:0] m_ctrlport_req_data,
+ output wire [ 3:0] m_ctrlport_req_byte_en,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ input wire m_ctrlport_resp_ack,
+ input wire [ 1:0] m_ctrlport_resp_status,
+ input wire [31:0] m_ctrlport_resp_data,
+ // Slave
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [ 9:0] s_ctrlport_req_portid,
+ input wire [15:0] s_ctrlport_req_rem_epid,
+ input wire [ 9:0] s_ctrlport_req_rem_portid,
+ input wire [31:0] s_ctrlport_req_data,
+ input wire [ 3:0] s_ctrlport_req_byte_en,
+ input wire s_ctrlport_req_has_time,
+ input wire [63:0] s_ctrlport_req_time,
+ output wire s_ctrlport_resp_ack,
+ output wire [ 1:0] s_ctrlport_resp_status,
+ output wire [31:0] s_ctrlport_resp_data,
+
+ //---------------------------------------------------------------------------
+ // Client Data Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+
+ // Output data stream (to user logic)
+ output wire [(NUM_DATA_I*ITEM_W*NIPC)-1:0] m_axis_payload_tdata,
+ output wire [ (NUM_DATA_I*NIPC)-1:0] m_axis_payload_tkeep,
+ output wire [ NUM_DATA_I-1:0] m_axis_payload_tlast,
+ output wire [ NUM_DATA_I-1:0] m_axis_payload_tvalid,
+ input wire [ NUM_DATA_I-1:0] m_axis_payload_tready,
+
+ // Input data stream (from user logic)
+ input wire [(NUM_DATA_O*ITEM_W*NIPC)-1:0] s_axis_payload_tdata,
+ input wire [ (NUM_DATA_O*NIPC)-1:0] s_axis_payload_tkeep,
+ input wire [ NUM_DATA_O-1:0] s_axis_payload_tlast,
+ input wire [ NUM_DATA_O-1:0] s_axis_payload_tvalid,
+ output wire [ NUM_DATA_O-1:0] s_axis_payload_tready,
+
+ // Output context stream (to user logic)
+ output wire [(NUM_DATA_I*CHDR_W)-1:0] m_axis_context_tdata,
+ output wire [ (4*NUM_DATA_I)-1:0] m_axis_context_tuser,
+ output wire [ NUM_DATA_I-1:0] m_axis_context_tlast,
+ output wire [ NUM_DATA_I-1:0] m_axis_context_tvalid,
+ input wire [ NUM_DATA_I-1:0] m_axis_context_tready,
+
+ // Input context stream (from user logic)
+ input wire [(NUM_DATA_O*CHDR_W)-1:0] s_axis_context_tdata,
+ input wire [ (4*NUM_DATA_O)-1:0] s_axis_context_tuser,
+ input wire [ NUM_DATA_O-1:0] s_axis_context_tlast,
+ input wire [ NUM_DATA_O-1:0] s_axis_context_tvalid,
+ output wire [ NUM_DATA_O-1:0] s_axis_context_tready
+);
+
+ localparam CTRL_FIFO_SIZE = 5;
+
+
+ //---------------------------------------------------------------------------
+ // Backend Interface
+ //---------------------------------------------------------------------------
+
+ wire data_i_flush_en;
+ wire [31:0] data_i_flush_timeout;
+ wire [63:0] data_i_flush_active;
+ wire [63:0] data_i_flush_done;
+ wire data_o_flush_en;
+ wire [31:0] data_o_flush_timeout;
+ wire [63:0] data_o_flush_active;
+ wire [63:0] data_o_flush_done;
+
+ backend_iface #(
+ .NOC_ID (NOC_ID),
+ .NUM_DATA_I (NUM_DATA_I),
+ .NUM_DATA_O (NUM_DATA_O),
+ .CTRL_FIFOSIZE (CTRL_FIFO_SIZE),
+ .MTU (MTU)
+ ) backend_iface_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst),
+ .data_i_flush_en (data_i_flush_en),
+ .data_i_flush_timeout (data_i_flush_timeout),
+ .data_i_flush_active (data_i_flush_active),
+ .data_i_flush_done (data_i_flush_done),
+ .data_o_flush_en (data_o_flush_en),
+ .data_o_flush_timeout (data_o_flush_timeout),
+ .data_o_flush_active (data_o_flush_active),
+ .data_o_flush_done (data_o_flush_done)
+ );
+
+ //---------------------------------------------------------------------------
+ // Control Path
+ //---------------------------------------------------------------------------
+
+ ctrlport_endpoint #(
+ .THIS_PORTID (THIS_PORTID ),
+ .SYNC_CLKS (0 ),
+ .AXIS_CTRL_MST_EN (CTRLPORT_SLV_EN),
+ .AXIS_CTRL_SLV_EN (CTRLPORT_MST_EN),
+ .SLAVE_FIFO_SIZE (CTRL_FIFO_SIZE )
+ ) ctrlport_ep_i (
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk ),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst ),
+ .ctrlport_clk (ctrlport_clk ),
+ .ctrlport_rst (ctrlport_rst ),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata ),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast ),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid ),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready ),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata ),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast ),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid ),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready ),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr ),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd ),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr ),
+ .m_ctrlport_req_data (m_ctrlport_req_data ),
+ .m_ctrlport_req_byte_en (m_ctrlport_req_byte_en ),
+ .m_ctrlport_req_has_time (m_ctrlport_req_has_time ),
+ .m_ctrlport_req_time (m_ctrlport_req_time ),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack ),
+ .m_ctrlport_resp_status (m_ctrlport_resp_status ),
+ .m_ctrlport_resp_data (m_ctrlport_resp_data ),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr ),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd ),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr ),
+ .s_ctrlport_req_portid (s_ctrlport_req_portid ),
+ .s_ctrlport_req_rem_epid (s_ctrlport_req_rem_epid ),
+ .s_ctrlport_req_rem_portid(s_ctrlport_req_rem_portid),
+ .s_ctrlport_req_data (s_ctrlport_req_data ),
+ .s_ctrlport_req_byte_en (s_ctrlport_req_byte_en ),
+ .s_ctrlport_req_has_time (s_ctrlport_req_has_time ),
+ .s_ctrlport_req_time (s_ctrlport_req_time ),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack ),
+ .s_ctrlport_resp_status (s_ctrlport_resp_status ),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data )
+ );
+
+ //---------------------------------------------------------------------------
+ // Data Path
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ generate
+
+ for (i = 0; i < NUM_DATA_I; i = i + 1) begin: chdr_to_data
+ chdr_to_axis_pyld_ctxt #(
+ .CHDR_W (CHDR_W ),
+ .ITEM_W (ITEM_W ),
+ .NIPC (NIPC ),
+ .SYNC_CLKS (SYNC_CLKS ),
+ .CONTEXT_FIFO_SIZE (CTXT_FIFO_SIZE),
+ .PAYLOAD_FIFO_SIZE (PYLD_FIFO_SIZE),
+ .CONTEXT_PREFETCH_EN (1 )
+ ) chdr_to_axis_pyld_ctxt_i (
+ .axis_chdr_clk (rfnoc_chdr_clk ),
+ .axis_chdr_rst (rfnoc_chdr_rst ),
+ .axis_data_clk (axis_data_clk ),
+ .axis_data_rst (axis_data_rst ),
+ .s_axis_chdr_tdata (s_rfnoc_chdr_tdata [(i*CHDR_W)+:CHDR_W] ),
+ .s_axis_chdr_tlast (s_rfnoc_chdr_tlast [i] ),
+ .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid [i] ),
+ .s_axis_chdr_tready (s_rfnoc_chdr_tready [i] ),
+ .m_axis_payload_tdata (m_axis_payload_tdata [(i*ITEM_W*NIPC)+:(ITEM_W*NIPC)]),
+ .m_axis_payload_tkeep (m_axis_payload_tkeep [(i*NIPC)+:NIPC] ),
+ .m_axis_payload_tlast (m_axis_payload_tlast [i] ),
+ .m_axis_payload_tvalid(m_axis_payload_tvalid[i] ),
+ .m_axis_payload_tready(m_axis_payload_tready[i] ),
+ .m_axis_context_tdata (m_axis_context_tdata [(i*CHDR_W)+:(CHDR_W)] ),
+ .m_axis_context_tuser (m_axis_context_tuser [(i*4)+:4] ),
+ .m_axis_context_tlast (m_axis_context_tlast [i] ),
+ .m_axis_context_tvalid(m_axis_context_tvalid[i] ),
+ .m_axis_context_tready(m_axis_context_tready[i] ),
+ .flush_en (data_i_flush_en ),
+ .flush_timeout (data_i_flush_timeout ),
+ .flush_active (data_i_flush_active [i] ),
+ .flush_done (data_i_flush_done [i] )
+ );
+ end
+
+ for (i = 0; i < NUM_DATA_O; i = i + 1) begin: data_to_chdr
+ axis_pyld_ctxt_to_chdr #(
+ .CHDR_W (CHDR_W ),
+ .ITEM_W (ITEM_W ),
+ .NIPC (NIPC ),
+ .SYNC_CLKS (SYNC_CLKS ),
+ .CONTEXT_FIFO_SIZE (CTXT_FIFO_SIZE),
+ .PAYLOAD_FIFO_SIZE (PYLD_FIFO_SIZE),
+ .CONTEXT_PREFETCH_EN (1 ),
+ .MTU (MTU )
+ ) axis_pyld_ctxt_to_chdr_i (
+ .axis_chdr_clk (rfnoc_chdr_clk ),
+ .axis_chdr_rst (rfnoc_chdr_rst ),
+ .axis_data_clk (axis_data_clk ),
+ .axis_data_rst (axis_data_rst ),
+ .m_axis_chdr_tdata (m_rfnoc_chdr_tdata [(i*CHDR_W)+:CHDR_W] ),
+ .m_axis_chdr_tlast (m_rfnoc_chdr_tlast [i] ),
+ .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid [i] ),
+ .m_axis_chdr_tready (m_rfnoc_chdr_tready [i] ),
+ .s_axis_payload_tdata (s_axis_payload_tdata [(i*ITEM_W*NIPC)+:(ITEM_W*NIPC)]),
+ .s_axis_payload_tkeep (s_axis_payload_tkeep [(i*NIPC)+:NIPC] ),
+ .s_axis_payload_tlast (s_axis_payload_tlast [i] ),
+ .s_axis_payload_tvalid(s_axis_payload_tvalid[i] ),
+ .s_axis_payload_tready(s_axis_payload_tready[i] ),
+ .s_axis_context_tdata (s_axis_context_tdata [(i*CHDR_W)+:(CHDR_W)] ),
+ .s_axis_context_tuser (s_axis_context_tuser [(i*4)+:4] ),
+ .s_axis_context_tlast (s_axis_context_tlast [i] ),
+ .s_axis_context_tvalid(s_axis_context_tvalid[i] ),
+ .s_axis_context_tready(s_axis_context_tready[i] ),
+ .framer_errors ( ),
+ .flush_en (data_o_flush_en ),
+ .flush_timeout (data_o_flush_timeout ),
+ .flush_active (data_o_flush_active [i] ),
+ .flush_done (data_o_flush_done [i] )
+ );
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft.v
new file mode 100644
index 000000000..76ae37524
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft.v
@@ -0,0 +1,559 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_fft
+//
+// Description: An FFT block for RFNoC.
+//
+// Parameters:
+//
+// THIS_PORTID : Control crossbar port to which this block is connected
+// CHDR_W : AXIS CHDR interface data width
+// MTU : Maximum transmission unit (i.e., maximum packet size) in
+// CHDR words is 2**MTU.
+// EN_MAGNITUDE_OUT : CORDIC based magnitude calculation
+// EN_MAGNITUDE_APPROX_OUT : Multipler-less, lower resource usage
+// EN_MAGNITUDE_SQ_OUT : Magnitude squared
+// EN_FFT_SHIFT : Center zero frequency bin
+//
+
+module rfnoc_block_fft #(
+ parameter THIS_PORTID = 0,
+ parameter CHDR_W = 64,
+ parameter MTU = 10,
+
+ parameter EN_MAGNITUDE_OUT = 0,
+ parameter EN_MAGNITUDE_APPROX_OUT = 1,
+ parameter EN_MAGNITUDE_SQ_OUT = 1,
+ parameter EN_FFT_SHIFT = 1
+ )
+(
+ //---------------------------------------------------------------------------
+ // AXIS CHDR Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_chdr_clk,
+ input wire ce_clk,
+
+ // CHDR inputs from framework
+ input wire [CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire s_rfnoc_chdr_tlast,
+ input wire s_rfnoc_chdr_tvalid,
+ output wire s_rfnoc_chdr_tready,
+
+ // CHDR outputs to framework
+ output wire [CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire m_rfnoc_chdr_tlast,
+ output wire m_rfnoc_chdr_tvalid,
+ input wire m_rfnoc_chdr_tready,
+
+ // Backend interface
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+
+ //---------------------------------------------------------------------------
+ // AXIS CTRL Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_ctrl_clk,
+
+ // CTRL port requests from framework
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+
+ // CTRL port requests to framework
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready
+);
+
+ // These are the only supported values for now
+ localparam ITEM_W = 32;
+ localparam NIPC = 1;
+
+ localparam NOC_ID = 32'hFF70_0000;
+
+ `include "../../core/rfnoc_axis_ctrl_utils.vh"
+
+ //---------------------------------------------------------------------------
+ // Signal Declarations
+ //---------------------------------------------------------------------------
+
+ wire rfnoc_chdr_rst;
+
+ wire ctrlport_req_wr;
+ wire ctrlport_req_rd;
+ wire [19:0] ctrlport_req_addr;
+ wire [31:0] ctrlport_req_data;
+ wire ctrlport_req_has_time;
+ wire [63:0] ctrlport_req_time;
+ wire ctrlport_resp_ack;
+ wire [31:0] ctrlport_resp_data;
+
+ wire [ITEM_W-1:0] axis_to_fft_tdata;
+ wire axis_to_fft_tlast;
+ wire axis_to_fft_tvalid;
+ wire axis_to_fft_tready;
+
+ wire [ITEM_W-1:0] axis_from_fft_tdata;
+ wire axis_from_fft_tlast;
+ wire axis_from_fft_tvalid;
+ wire axis_from_fft_tready;
+
+ wire [CHDR_W-1:0] m_axis_context_tdata;
+ wire [ 3:0] m_axis_context_tuser;
+ wire [ 0:0] m_axis_context_tlast;
+ wire [ 0:0] m_axis_context_tvalid;
+ wire [ 0:0] m_axis_context_tready;
+
+ wire [CHDR_W-1:0] s_axis_context_tdata;
+ wire [ 3:0] s_axis_context_tuser;
+ wire [ 0:0] s_axis_context_tlast;
+ wire [ 0:0] s_axis_context_tvalid;
+ wire [ 0:0] s_axis_context_tready;
+
+ wire ce_rst;
+
+ // Cross the CHDR reset to the radio_clk domain
+ pulse_synchronizer #(
+ .MODE ("POSEDGE")
+ ) ctrl_rst_sync_i (
+ .clk_a (rfnoc_chdr_clk),
+ .rst_a (1'b0),
+ .pulse_a (rfnoc_chdr_rst),
+ .busy_a (),
+ .clk_b (ce_clk),
+ .pulse_b (ce_rst)
+ );
+
+ //---------------------------------------------------------------------------
+ // NoC Shell
+ //---------------------------------------------------------------------------
+
+ noc_shell_fft #(
+ .NOC_ID (NOC_ID ),
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W ),
+ .CTRLPORT_SLV_EN(0 ),
+ .CTRLPORT_MST_EN(1 ),
+ .SYNC_CLKS (0 ),
+ .NUM_DATA_I (1 ),
+ .NUM_DATA_O (1 ),
+ .ITEM_W (ITEM_W ),
+ .NIPC (NIPC ),
+ .PYLD_FIFO_SIZE (MTU ),
+ .CTXT_FIFO_SIZE (1 ),
+ .MTU (MTU )
+ ) noc_shell_fft_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk ),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst ),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk ),
+ .rfnoc_ctrl_rst ( ),
+ .rfnoc_core_config (rfnoc_core_config ),
+ .rfnoc_core_status (rfnoc_core_status ),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata ),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast ),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid ),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready ),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata ),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast ),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid ),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready ),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata ),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast ),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid ),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready ),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata ),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast ),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid ),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready ),
+ .ctrlport_clk (ce_clk ),
+ .ctrlport_rst (ce_rst ),
+ .m_ctrlport_req_wr (ctrlport_req_wr ),
+ .m_ctrlport_req_rd (ctrlport_req_rd ),
+ .m_ctrlport_req_addr (ctrlport_req_addr ),
+ .m_ctrlport_req_data (ctrlport_req_data ),
+ .m_ctrlport_req_byte_en ( ),
+ .m_ctrlport_req_has_time (ctrlport_req_has_time),
+ .m_ctrlport_req_time (ctrlport_req_time ),
+ .m_ctrlport_resp_ack (ctrlport_resp_ack ),
+ .m_ctrlport_resp_status (AXIS_CTRL_STS_OKAY ),
+ .m_ctrlport_resp_data (ctrlport_resp_data ),
+ .s_ctrlport_req_wr (1'b0 ),
+ .s_ctrlport_req_rd (1'b0 ),
+ .s_ctrlport_req_addr (20'b0 ),
+ .s_ctrlport_req_portid (10'b0 ),
+ .s_ctrlport_req_rem_epid (16'b0 ),
+ .s_ctrlport_req_rem_portid(10'b0 ),
+ .s_ctrlport_req_data (32'b0 ),
+ .s_ctrlport_req_byte_en (4'b0 ),
+ .s_ctrlport_req_has_time (1'b0 ),
+ .s_ctrlport_req_time (64'b0 ),
+ .s_ctrlport_resp_ack ( ),
+ .s_ctrlport_resp_status ( ),
+ .s_ctrlport_resp_data ( ),
+ .axis_data_clk (ce_clk ),
+ .axis_data_rst (ce_rst ),
+ .m_axis_payload_tdata (axis_to_fft_tdata ),
+ .m_axis_payload_tkeep ( ),
+ .m_axis_payload_tlast (axis_to_fft_tlast ),
+ .m_axis_payload_tvalid (axis_to_fft_tvalid ),
+ .m_axis_payload_tready (axis_to_fft_tready ),
+ .s_axis_payload_tdata (axis_from_fft_tdata ),
+ .s_axis_payload_tkeep ({1*NIPC{1'b1}} ),
+ .s_axis_payload_tlast (axis_from_fft_tlast ),
+ .s_axis_payload_tvalid (axis_from_fft_tvalid ),
+ .s_axis_payload_tready (axis_from_fft_tready ),
+ .m_axis_context_tdata (m_axis_context_tdata ),
+ .m_axis_context_tuser (m_axis_context_tuser ),
+ .m_axis_context_tlast (m_axis_context_tlast ),
+ .m_axis_context_tvalid (m_axis_context_tvalid),
+ .m_axis_context_tready (m_axis_context_tready),
+ .s_axis_context_tdata (s_axis_context_tdata ),
+ .s_axis_context_tuser (s_axis_context_tuser ),
+ .s_axis_context_tlast (s_axis_context_tlast ),
+ .s_axis_context_tvalid (s_axis_context_tvalid),
+ .s_axis_context_tready (s_axis_context_tready)
+ );
+
+ // The input packets are the same configuration as the output packets, so
+ // just use the header information for each incoming to create the header for
+ // each outgoing packet. This is done by connecting m_axis_context to
+ // directly to s_axis_context.
+ assign s_axis_context_tdata = m_axis_context_tdata;
+ assign s_axis_context_tuser = m_axis_context_tuser;
+ assign s_axis_context_tlast = m_axis_context_tlast;
+ assign s_axis_context_tvalid = m_axis_context_tvalid;
+ assign m_axis_context_tready = s_axis_context_tready;
+
+ wire [ 8-1:0] set_addr;
+ wire [32-1:0] set_data;
+ wire set_has_time;
+ wire set_stb;
+ wire [ 8-1:0] rb_addr;
+ reg [64-1:0] rb_data;
+
+ ctrlport_to_settings_bus # (
+ .NUM_PORTS (1)
+ ) ctrlport_to_settings_bus_i (
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (ce_rst),
+ .s_ctrlport_req_wr (ctrlport_req_wr),
+ .s_ctrlport_req_rd (ctrlport_req_rd),
+ .s_ctrlport_req_addr (ctrlport_req_addr),
+ .s_ctrlport_req_data (ctrlport_req_data),
+ .s_ctrlport_req_has_time (ctrlport_req_has_time),
+ .s_ctrlport_req_time (ctrlport_req_time),
+ .s_ctrlport_resp_ack (ctrlport_resp_ack),
+ .s_ctrlport_resp_data (ctrlport_resp_data),
+ .set_data (set_data),
+ .set_addr (set_addr),
+ .set_stb (set_stb),
+ .set_time (),
+ .set_has_time (set_has_time),
+ .rb_stb (1'b1),
+ .rb_addr (rb_addr),
+ .rb_data (rb_data));
+
+ localparam MAX_FFT_SIZE_LOG2 = 11;
+
+ localparam [31:0] SR_FFT_RESET = 131;
+ localparam [31:0] SR_FFT_SIZE_LOG2 = 132;
+ localparam [31:0] SR_MAGNITUDE_OUT = 133;
+ localparam [31:0] SR_FFT_DIRECTION = 134;
+ localparam [31:0] SR_FFT_SCALING = 135;
+ localparam [31:0] SR_FFT_SHIFT_CONFIG = 136;
+
+ // FFT Output
+ localparam [1:0] COMPLEX_OUT = 0;
+ localparam [1:0] MAG_OUT = 1;
+ localparam [1:0] MAG_SQ_OUT = 2;
+
+ // FFT Direction
+ localparam [0:0] FFT_REVERSE = 0;
+ localparam [0:0] FFT_FORWARD = 1;
+
+ wire [1:0] magnitude_out;
+ wire [31:0] fft_data_o_tdata;
+ wire fft_data_o_tlast;
+ wire fft_data_o_tvalid;
+ wire fft_data_o_tready;
+ wire [15:0] fft_data_o_tuser;
+ wire [31:0] fft_shift_o_tdata;
+ wire fft_shift_o_tlast;
+ wire fft_shift_o_tvalid;
+ wire fft_shift_o_tready;
+ wire [31:0] fft_mag_i_tdata, fft_mag_o_tdata, fft_mag_o_tdata_int;
+ wire fft_mag_i_tlast, fft_mag_o_tlast;
+ wire fft_mag_i_tvalid, fft_mag_o_tvalid;
+ wire fft_mag_i_tready, fft_mag_o_tready;
+ wire [31:0] fft_mag_sq_i_tdata, fft_mag_sq_o_tdata;
+ wire fft_mag_sq_i_tlast, fft_mag_sq_o_tlast;
+ wire fft_mag_sq_i_tvalid, fft_mag_sq_o_tvalid;
+ wire fft_mag_sq_i_tready, fft_mag_sq_o_tready;
+ wire [31:0] fft_mag_round_i_tdata, fft_mag_round_o_tdata;
+ wire fft_mag_round_i_tlast, fft_mag_round_o_tlast;
+ wire fft_mag_round_i_tvalid, fft_mag_round_o_tvalid;
+ wire fft_mag_round_i_tready, fft_mag_round_o_tready;
+
+ // Settings Registers
+ wire fft_reset;
+ setting_reg #(
+ .my_addr(SR_FFT_RESET), .awidth(8), .width(1))
+ sr_fft_reset (
+ .clk(ce_clk), .rst(ce_rst),
+ .strobe(set_stb), .addr(set_addr), .in(set_data), .out(fft_reset), .changed());
+
+ // Two instances of FFT size register, one for FFT core and one for FFT shift
+ localparam DEFAULT_FFT_SIZE = 8; // 256
+ wire [7:0] fft_size_log2_tdata ,fft_core_size_log2_tdata;
+ wire fft_size_log2_tvalid, fft_core_size_log2_tvalid, fft_size_log2_tready, fft_core_size_log2_tready;
+ axi_setting_reg #(
+ .ADDR(SR_FFT_SIZE_LOG2), .AWIDTH(8), .WIDTH(8), .DATA_AT_RESET(DEFAULT_FFT_SIZE), .VALID_AT_RESET(1))
+ sr_fft_size_log2 (
+ .clk(ce_clk), .reset(ce_rst),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .o_tdata(fft_size_log2_tdata), .o_tlast(), .o_tvalid(fft_size_log2_tvalid), .o_tready(fft_size_log2_tready));
+
+ axi_setting_reg #(
+ .ADDR(SR_FFT_SIZE_LOG2), .AWIDTH(8), .WIDTH(8), .DATA_AT_RESET(DEFAULT_FFT_SIZE), .VALID_AT_RESET(1))
+ sr_fft_size_log2_2 (
+ .clk(ce_clk), .reset(ce_rst),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .o_tdata(fft_core_size_log2_tdata), .o_tlast(), .o_tvalid(fft_core_size_log2_tvalid), .o_tready(fft_core_size_log2_tready));
+
+ // Forward = 0, Reverse = 1
+ localparam DEFAULT_FFT_DIRECTION = 0;
+ wire fft_direction_tdata;
+ wire fft_direction_tvalid, fft_direction_tready;
+ axi_setting_reg #(
+ .ADDR(SR_FFT_DIRECTION), .AWIDTH(8), .WIDTH(1), .DATA_AT_RESET(DEFAULT_FFT_DIRECTION), .VALID_AT_RESET(1))
+ sr_fft_direction (
+ .clk(ce_clk), .reset(ce_rst),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .o_tdata(fft_direction_tdata), .o_tlast(), .o_tvalid(fft_direction_tvalid), .o_tready(fft_direction_tready));
+
+ localparam [11:0] DEFAULT_FFT_SCALING = 12'b011010101010; // Conservative 1/N scaling
+ wire [11:0] fft_scaling_tdata;
+ wire fft_scaling_tvalid, fft_scaling_tready;
+ axi_setting_reg #(
+ .ADDR(SR_FFT_SCALING), .AWIDTH(8), .WIDTH(12), .DATA_AT_RESET(DEFAULT_FFT_SCALING), .VALID_AT_RESET(1))
+ sr_fft_scaling (
+ .clk(ce_clk), .reset(ce_rst),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .o_tdata(fft_scaling_tdata), .o_tlast(), .o_tvalid(fft_scaling_tvalid), .o_tready(fft_scaling_tready));
+
+ wire [1:0] fft_shift_config_tdata;
+ wire fft_shift_config_tvalid, fft_shift_config_tready;
+ axi_setting_reg #(
+ .ADDR(SR_FFT_SHIFT_CONFIG), .AWIDTH(8), .WIDTH(2))
+ sr_fft_shift_config (
+ .clk(ce_clk), .reset(ce_rst),
+ .set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
+ .o_tdata(fft_shift_config_tdata), .o_tlast(), .o_tvalid(fft_shift_config_tvalid), .o_tready(fft_shift_config_tready));
+
+ // Synchronize writing configuration to the FFT core
+ reg fft_config_ready;
+ wire fft_config_write = fft_config_ready & axis_to_fft_tvalid & axis_to_fft_tready;
+ always @(posedge ce_clk) begin
+ if (ce_rst | fft_reset) begin
+ fft_config_ready <= 1'b1;
+ end else begin
+ if (fft_config_write) begin
+ fft_config_ready <= 1'b0;
+ end else if (axis_to_fft_tlast) begin
+ fft_config_ready <= 1'b1;
+ end
+ end
+ end
+
+ wire [23:0] fft_config_tdata = {3'd0, fft_scaling_tdata, fft_direction_tdata, fft_core_size_log2_tdata};
+ wire fft_config_tvalid = fft_config_write & (fft_scaling_tvalid | fft_direction_tvalid | fft_core_size_log2_tvalid);
+ wire fft_config_tready;
+ assign fft_core_size_log2_tready = fft_config_tready & fft_config_write;
+ assign fft_direction_tready = fft_config_tready & fft_config_write;
+ assign fft_scaling_tready = fft_config_tready & fft_config_write;
+ axi_fft inst_axi_fft (
+ .aclk(ce_clk), .aresetn(~(fft_reset)),
+ .s_axis_data_tvalid(axis_to_fft_tvalid),
+ .s_axis_data_tready(axis_to_fft_tready),
+ .s_axis_data_tlast(axis_to_fft_tlast),
+ .s_axis_data_tdata({axis_to_fft_tdata[15:0],axis_to_fft_tdata[31:16]}),
+ .m_axis_data_tvalid(fft_data_o_tvalid),
+ .m_axis_data_tready(fft_data_o_tready),
+ .m_axis_data_tlast(fft_data_o_tlast),
+ .m_axis_data_tdata({fft_data_o_tdata[15:0],fft_data_o_tdata[31:16]}),
+ .m_axis_data_tuser(fft_data_o_tuser), // FFT index
+ .s_axis_config_tdata(fft_config_tdata),
+ .s_axis_config_tvalid(fft_config_tvalid),
+ .s_axis_config_tready(fft_config_tready),
+ .event_frame_started(),
+ .event_tlast_unexpected(),
+ .event_tlast_missing(),
+ .event_status_channel_halt(),
+ .event_data_in_channel_halt(),
+ .event_data_out_channel_halt());
+
+ // Mux control signals
+ assign fft_shift_o_tready = (magnitude_out == MAG_OUT) ? fft_mag_i_tready :
+ (magnitude_out == MAG_SQ_OUT) ? fft_mag_sq_i_tready : axis_from_fft_tready;
+ assign fft_mag_i_tvalid = (magnitude_out == MAG_OUT) ? fft_shift_o_tvalid : 1'b0;
+ assign fft_mag_i_tlast = (magnitude_out == MAG_OUT) ? fft_shift_o_tlast : 1'b0;
+ assign fft_mag_i_tdata = fft_shift_o_tdata;
+ assign fft_mag_o_tready = (magnitude_out == MAG_OUT) ? fft_mag_round_i_tready : 1'b0;
+ assign fft_mag_sq_i_tvalid = (magnitude_out == MAG_SQ_OUT) ? fft_shift_o_tvalid : 1'b0;
+ assign fft_mag_sq_i_tlast = (magnitude_out == MAG_SQ_OUT) ? fft_shift_o_tlast : 1'b0;
+ assign fft_mag_sq_i_tdata = fft_shift_o_tdata;
+ assign fft_mag_sq_o_tready = (magnitude_out == MAG_SQ_OUT) ? fft_mag_round_i_tready : 1'b0;
+ assign fft_mag_round_i_tvalid = (magnitude_out == MAG_OUT) ? fft_mag_o_tvalid :
+ (magnitude_out == MAG_SQ_OUT) ? fft_mag_sq_o_tvalid : 1'b0;
+ assign fft_mag_round_i_tlast = (magnitude_out == MAG_OUT) ? fft_mag_o_tlast :
+ (magnitude_out == MAG_SQ_OUT) ? fft_mag_sq_o_tlast : 1'b0;
+ assign fft_mag_round_i_tdata = (magnitude_out == MAG_OUT) ? fft_mag_o_tdata : fft_mag_sq_o_tdata;
+ assign fft_mag_round_o_tready = axis_from_fft_tready;
+ assign axis_from_fft_tvalid = (magnitude_out == MAG_OUT | magnitude_out == MAG_SQ_OUT) ? fft_mag_round_o_tvalid : fft_shift_o_tvalid;
+ assign axis_from_fft_tlast = (magnitude_out == MAG_OUT | magnitude_out == MAG_SQ_OUT) ? fft_mag_round_o_tlast : fft_shift_o_tlast;
+ assign axis_from_fft_tdata = (magnitude_out == MAG_OUT | magnitude_out == MAG_SQ_OUT) ? fft_mag_round_o_tdata : fft_shift_o_tdata;
+
+ // Conditionally synth magnitude / magnitude^2 logic
+ generate
+ if (EN_MAGNITUDE_OUT | EN_MAGNITUDE_APPROX_OUT | EN_MAGNITUDE_SQ_OUT) begin : generate_magnitude_out
+ setting_reg #(
+ .my_addr(SR_MAGNITUDE_OUT), .awidth(8), .width(2))
+ sr_magnitude_out (
+ .clk(ce_clk), .rst(ce_rst),
+ .strobe(set_stb), .addr(set_addr), .in(set_data), .out(magnitude_out), .changed());
+ end else begin : generate_magnitude_out_else
+ // Magnitude calculation logic not included, so always bypass
+ assign magnitude_out = 2'd0;
+ end
+
+ if (EN_FFT_SHIFT) begin : generate_fft_shift
+ fft_shift #(
+ .MAX_FFT_SIZE_LOG2(MAX_FFT_SIZE_LOG2),
+ .WIDTH(32))
+ inst_fft_shift (
+ .clk(ce_clk), .reset(ce_rst | fft_reset),
+ .config_tdata(fft_shift_config_tdata),
+ .config_tvalid(fft_shift_config_tvalid),
+ .config_tready(fft_shift_config_tready),
+ .fft_size_log2_tdata(fft_size_log2_tdata[$clog2(MAX_FFT_SIZE_LOG2)-1:0]),
+ .fft_size_log2_tvalid(fft_size_log2_tvalid),
+ .fft_size_log2_tready(fft_size_log2_tready),
+ .i_tdata(fft_data_o_tdata),
+ .i_tlast(fft_data_o_tlast),
+ .i_tvalid(fft_data_o_tvalid),
+ .i_tready(fft_data_o_tready),
+ .i_tuser(fft_data_o_tuser[MAX_FFT_SIZE_LOG2-1:0]),
+ .o_tdata(fft_shift_o_tdata),
+ .o_tlast(fft_shift_o_tlast),
+ .o_tvalid(fft_shift_o_tvalid),
+ .o_tready(fft_shift_o_tready));
+ end
+ else begin : generate_fft_shift_else
+ assign fft_shift_o_tdata = fft_data_o_tdata;
+ assign fft_shift_o_tlast = fft_data_o_tlast;
+ assign fft_shift_o_tvalid = fft_data_o_tvalid;
+ assign fft_data_o_tready = fft_shift_o_tready;
+ end
+
+ // More accurate magnitude calculation takes precedence if enabled
+ if (EN_MAGNITUDE_OUT) begin : generate_complex_to_magphase
+ complex_to_magphase
+ inst_complex_to_magphase (
+ .aclk(ce_clk), .aresetn(~(ce_rst | fft_reset)),
+ .s_axis_cartesian_tvalid(fft_mag_i_tvalid),
+ .s_axis_cartesian_tlast(fft_mag_i_tlast),
+ .s_axis_cartesian_tready(fft_mag_i_tready),
+ .s_axis_cartesian_tdata(fft_mag_i_tdata),
+ .m_axis_dout_tvalid(fft_mag_o_tvalid),
+ .m_axis_dout_tlast(fft_mag_o_tlast),
+ .m_axis_dout_tdata(fft_mag_o_tdata_int),
+ .m_axis_dout_tready(fft_mag_o_tready));
+ assign fft_mag_o_tdata = {1'b0, fft_mag_o_tdata_int[15:0], 15'd0};
+ end
+ else if (EN_MAGNITUDE_APPROX_OUT) begin : generate_complex_to_mag_approx
+ complex_to_mag_approx
+ inst_complex_to_mag_approx (
+ .clk(ce_clk), .reset(ce_rst | fft_reset), .clear(1'b0),
+ .i_tvalid(fft_mag_i_tvalid),
+ .i_tlast(fft_mag_i_tlast),
+ .i_tready(fft_mag_i_tready),
+ .i_tdata(fft_mag_i_tdata),
+ .o_tvalid(fft_mag_o_tvalid),
+ .o_tlast(fft_mag_o_tlast),
+ .o_tready(fft_mag_o_tready),
+ .o_tdata(fft_mag_o_tdata_int[15:0]));
+ assign fft_mag_o_tdata = {1'b0, fft_mag_o_tdata_int[15:0], 15'd0};
+ end
+ else begin : generate_complex_to_mag_approx_else
+ assign fft_mag_o_tdata = fft_mag_i_tdata;
+ assign fft_mag_o_tlast = fft_mag_i_tlast;
+ assign fft_mag_o_tvalid = fft_mag_i_tvalid;
+ assign fft_mag_i_tready = fft_mag_o_tready;
+ end
+
+ if (EN_MAGNITUDE_SQ_OUT) begin : generate_complex_to_magsq
+ complex_to_magsq
+ inst_complex_to_magsq (
+ .clk(ce_clk), .reset(ce_rst | fft_reset), .clear(1'b0),
+ .i_tvalid(fft_mag_sq_i_tvalid),
+ .i_tlast(fft_mag_sq_i_tlast),
+ .i_tready(fft_mag_sq_i_tready),
+ .i_tdata(fft_mag_sq_i_tdata),
+ .o_tvalid(fft_mag_sq_o_tvalid),
+ .o_tlast(fft_mag_sq_o_tlast),
+ .o_tready(fft_mag_sq_o_tready),
+ .o_tdata(fft_mag_sq_o_tdata));
+ end
+ else begin : generate_complex_to_magsq_else
+ assign fft_mag_sq_o_tdata = fft_mag_sq_i_tdata;
+ assign fft_mag_sq_o_tlast = fft_mag_sq_i_tlast;
+ assign fft_mag_sq_o_tvalid = fft_mag_sq_i_tvalid;
+ assign fft_mag_sq_i_tready = fft_mag_sq_o_tready;
+ end
+
+ // Convert to SC16
+ if (EN_MAGNITUDE_OUT | EN_MAGNITUDE_APPROX_OUT | EN_MAGNITUDE_SQ_OUT) begin : generate_axi_round_and_clip
+ axi_round_and_clip #(
+ .WIDTH_IN(32),
+ .WIDTH_OUT(16),
+ .CLIP_BITS(1))
+ inst_axi_round_and_clip (
+ .clk(ce_clk), .reset(ce_rst | fft_reset),
+ .i_tdata(fft_mag_round_i_tdata),
+ .i_tlast(fft_mag_round_i_tlast),
+ .i_tvalid(fft_mag_round_i_tvalid),
+ .i_tready(fft_mag_round_i_tready),
+ .o_tdata(fft_mag_round_o_tdata[31:16]),
+ .o_tlast(fft_mag_round_o_tlast),
+ .o_tvalid(fft_mag_round_o_tvalid),
+ .o_tready(fft_mag_round_o_tready));
+ assign fft_mag_round_o_tdata[15:0] = {16{16'd0}};
+ end
+ else begin : generate_axi_round_and_clip_else
+ assign fft_mag_round_o_tdata = fft_mag_round_i_tdata;
+ assign fft_mag_round_o_tlast = fft_mag_round_i_tlast;
+ assign fft_mag_round_o_tvalid = fft_mag_round_i_tvalid;
+ assign fft_mag_round_i_tready = fft_mag_round_o_tready;
+ end
+ endgenerate
+
+ // Readback registers
+ always @*
+ case(rb_addr)
+ 3'd0 : rb_data <= {63'd0, fft_reset};
+ 3'd1 : rb_data <= {62'd0, magnitude_out};
+ 3'd2 : rb_data <= {fft_size_log2_tdata};
+ 3'd3 : rb_data <= {63'd0, fft_direction_tdata};
+ 3'd4 : rb_data <= {52'd0, fft_scaling_tdata};
+ 3'd5 : rb_data <= {62'd0, fft_shift_config_tdata};
+ default : rb_data <= 64'h0BADC0DE0BADC0DE;
+ endcase
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft_tb.sv
new file mode 100644
index 000000000..bb46e3cc7
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft_tb.sv
@@ -0,0 +1,263 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_fft_tb
+//
+// Description: Testbench for rfnoc_block_fft
+//
+
+module rfnoc_block_fft_tb();
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Simulation parameters
+ localparam real CHDR_CLK_PER = 5.0; // Clock rate
+ localparam int SPP = 256; // Samples per packet
+ localparam int PKT_SIZE_BYTES = SPP*4; // Bytes per packet
+ localparam int STALL_PROB = 25; // BFM stall probability
+
+ // Block configuration
+ localparam int CHDR_W = 64;
+ localparam int THIS_PORTID = 'h123;
+ localparam int MTU = 10;
+ localparam int NUM_PORTS = 1;
+ localparam int NUM_HB = 3;
+ localparam int CIC_MAX_DECIM = 255;
+
+ // FFT specific settings
+ // FFT settings
+ localparam [31:0] FFT_SIZE = 256;
+ localparam [31:0] FFT_SIZE_LOG2 = $clog2(FFT_SIZE);
+ const logic [31:0] FFT_DIRECTION = DUT.FFT_FORWARD; // Forward
+ localparam [31:0] FFT_SCALING = 12'b011010101010; // Conservative scaling of 1/N
+ localparam [31:0] FFT_SHIFT_CONFIG = 0; // Normal FFT shift
+ localparam FFT_BIN = FFT_SIZE/8 + FFT_SIZE/2; // 1/8 sample rate freq + FFT shift
+ localparam NUM_ITERATIONS = 10;
+
+ //---------------------------------------------------------------------------
+ // Clocks
+ //---------------------------------------------------------------------------
+
+ bit rfnoc_chdr_clk;
+ bit rfnoc_ctrl_clk;
+
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst());
+
+ //---------------------------------------------------------------------------
+ // Bus Functional Models
+ //---------------------------------------------------------------------------
+
+ RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk);
+ AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) m_chdr (rfnoc_chdr_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) s_chdr (rfnoc_chdr_clk, 1'b0);
+
+ // Bus functional model for a software block controller
+ RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
+ new(backend, m_ctrl, s_ctrl);
+
+ // Connect block controller to BFMs
+ initial begin
+ blk_ctrl.connect_master_data_port(0, m_chdr, PKT_SIZE_BYTES);
+ blk_ctrl.connect_slave_data_port(0, s_chdr);
+ blk_ctrl.set_master_stall_prob(0, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(0, STALL_PROB);
+ end
+
+ //---------------------------------------------------------------------------
+ // DUT
+ //---------------------------------------------------------------------------
+
+ rfnoc_block_fft #(
+ .THIS_PORTID (0 ),
+ .CHDR_W (64 ),
+ .MTU (MTU),
+
+ .EN_MAGNITUDE_OUT (0 ),
+ .EN_MAGNITUDE_APPROX_OUT(1 ),
+ .EN_MAGNITUDE_SQ_OUT (1 ),
+ .EN_FFT_SHIFT (1 )
+ ) DUT (
+ .rfnoc_chdr_clk (backend.chdr_clk),
+ .ce_clk (backend.chdr_clk),
+ .s_rfnoc_chdr_tdata (m_chdr.tdata ),
+ .s_rfnoc_chdr_tlast (m_chdr.tlast ),
+ .s_rfnoc_chdr_tvalid(m_chdr.tvalid ),
+ .s_rfnoc_chdr_tready(m_chdr.tready ),
+
+ .m_rfnoc_chdr_tdata (s_chdr.tdata ),
+ .m_rfnoc_chdr_tlast (s_chdr.tlast ),
+ .m_rfnoc_chdr_tvalid(s_chdr.tvalid ),
+ .m_rfnoc_chdr_tready(s_chdr.tready ),
+
+ .rfnoc_core_config (backend.cfg ),
+ .rfnoc_core_status (backend.sts ),
+ .rfnoc_ctrl_clk (backend.ctrl_clk),
+
+ .s_rfnoc_ctrl_tdata (m_ctrl.tdata ),
+ .s_rfnoc_ctrl_tlast (m_ctrl.tlast ),
+ .s_rfnoc_ctrl_tvalid(m_ctrl.tvalid ),
+ .s_rfnoc_ctrl_tready(m_ctrl.tready ),
+
+ .m_rfnoc_ctrl_tdata (s_ctrl.tdata ),
+ .m_rfnoc_ctrl_tlast (s_ctrl.tlast ),
+ .m_rfnoc_ctrl_tvalid(s_ctrl.tvalid ),
+ .m_rfnoc_ctrl_tready(s_ctrl.tready )
+ );
+
+ //---------------------------------------------------------------------------
+ // Helper Tasks
+ //---------------------------------------------------------------------------
+
+ // Translate the desired register access to a ctrlport write request.
+ task automatic write_reg(int port, byte addr, bit [31:0] value);
+ blk_ctrl.reg_write(256*8*port + addr*8, value);
+ endtask : write_reg
+
+ // Translate the desired register access to a ctrlport read request.
+ task automatic read_user_reg(int port, byte addr, output logic [63:0] value);
+ blk_ctrl.reg_read(256*8*port + addr*8 + 0, value[31: 0]);
+ blk_ctrl.reg_read(256*8*port + addr*8 + 4, value[63:32]);
+ endtask : read_user_reg
+
+ //---------------------------------------------------------------------------
+ // Test Process
+ //---------------------------------------------------------------------------
+
+ task automatic send_sine_wave (
+ input int unsigned port
+ );
+ // Send a sine wave
+ fork
+ begin
+ chdr_word_t send_payload[$];
+
+ for (int n = 0; n < NUM_ITERATIONS; n++) begin
+ for (int i = 0; i < (FFT_SIZE/8); i++) begin
+ send_payload.push_back({ 16'h5A82, 16'h5A82, 16'h7FFF, 16'h0000});
+ send_payload.push_back({-16'h5A82, 16'h5A82, 16'h0000, 16'h7FFF});
+ send_payload.push_back({-16'h5A82,-16'h5A82,-16'h7FFF, 16'h0000});
+ send_payload.push_back({ 16'h5A82,-16'h5A82, 16'h0000,-16'h7FFF});
+ end
+
+ blk_ctrl.send(port, send_payload);
+ blk_ctrl.wait_complete(port);
+ send_payload = {};
+ end
+ end
+
+ begin
+ string s;
+ chdr_word_t recv_payload[$], temp_payload[$];
+ int data_bytes;
+ logic [15:0] real_val;
+ logic [15:0] cplx_val;
+
+ for (int n = 0; n < NUM_ITERATIONS; n++) begin
+ blk_ctrl.recv(port, recv_payload, data_bytes);
+
+ `ASSERT_ERROR(recv_payload.size * 2 == FFT_SIZE, "received wrong amount of data");
+
+ for (int k = 0; k < FFT_SIZE/2; k++) begin
+ chdr_word_t payload_word;
+ payload_word = recv_payload.pop_front();
+
+ for (int i = 0; i < 2; i++) begin
+ {real_val, cplx_val} = payload_word;
+ payload_word = payload_word[63:32];
+
+ if (2*k+i == FFT_BIN) begin
+ // Assert that for the special case of a 1/8th sample rate sine wave input,
+ // the real part of the corresponding 1/8th sample rate FFT bin should always be greater than 0 and
+ // the complex part equal to 0.
+
+ `ASSERT_ERROR(real_val > 32'd0, "FFT bin real part is not greater than 0!");
+ `ASSERT_ERROR(cplx_val == 32'd0, "FFT bin complex part is not 0!");
+ end else begin
+ // Assert all other FFT bins should be 0 for both complex and real parts
+ `ASSERT_ERROR(real_val == 32'd0, "FFT bin real part is not 0!");
+ `ASSERT_ERROR(cplx_val == 32'd0, "FFT bin complex part is not 0!");
+ end
+ end
+ end
+ end
+ end
+ join
+ endtask
+
+ initial begin : tb_main
+ const int port = 0;
+ test.start_tb("rfnoc_block_fft_tb");
+
+ // Start the BFMs running
+ blk_ctrl.run();
+
+ //-------------------------------------------------------------------------
+ // Reset
+ //-------------------------------------------------------------------------
+
+ test.start_test("Wait for Reset", 10us);
+ fork
+ blk_ctrl.reset_chdr();
+ blk_ctrl.reset_ctrl();
+ join;
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Check NoC ID and Block Info
+ //-------------------------------------------------------------------------
+
+ test.start_test("Verify Block Info", 2us);
+ `ASSERT_ERROR(blk_ctrl.get_noc_id() == DUT.NOC_ID, "Incorrect NOC_ID Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect NUM_DATA_I Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect NUM_DATA_O Value");
+ `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value");
+ test.end_test();
+
+ //-------------------------------------------------------------------------
+ // Setup FFT
+ //-------------------------------------------------------------------------
+
+ test.start_test("Setup FFT", 10us);
+ write_reg(port, DUT.SR_FFT_SIZE_LOG2, FFT_SIZE_LOG2);
+ write_reg(port, DUT.SR_FFT_DIRECTION, FFT_DIRECTION);
+ write_reg(port, DUT.SR_FFT_SCALING, FFT_SCALING);
+ write_reg(port, DUT.SR_FFT_SHIFT_CONFIG, FFT_SHIFT_CONFIG);
+ write_reg(port, DUT.SR_MAGNITUDE_OUT, DUT.COMPLEX_OUT); // Enable real/imag out
+ test.end_test();
+
+ //-------------------------------------------------------------------------76
+ // Test sine wave
+ //-------------------------------------------------------------------------
+
+ test.start_test("Test sine wave", 20us);
+ send_sine_wave (port);
+ test.end_test();
+
+ //-------------------------------------------------------------------------
+ // Finish
+ //-------------------------------------------------------------------------
+
+ // End the TB, but don't $finish, since we don't want to kill other
+ // instances of this testbench that may be running.
+ test.end_tb(0);
+
+ // Kill the clocks to end this instance of the testbench
+ rfnoc_chdr_clk_gen.kill();
+ rfnoc_ctrl_clk_gen.kill();
+ end
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/Makefile
new file mode 100644
index 000000000..7d6d84f82
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/Makefile
@@ -0,0 +1,46 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../../../top)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
+include Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_OOT_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = rfnoc_block_fir_filter_tb
+
+# Add test bench, user design under test, and
+# additional user created files
+SIM_SRCS = \
+$(abspath rfnoc_block_fir_filter_tb.sv)
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+# Include all simulator specific makefiles here
+# Each should define a unique target to simulate
+# e.g. xsim, vsim, etc and a common "clean" target
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/Makefile.srcs
new file mode 100644
index 000000000..f8c696096
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/Makefile.srcs
@@ -0,0 +1,12 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+RFNOC_OOT_SRCS += $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_fir_filter/, \
+noc_shell_fir_filter.v \
+rfnoc_fir_filter_regs.vh \
+rfnoc_fir_filter_core.v \
+rfnoc_block_fir_filter.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/noc_shell_fir_filter.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/noc_shell_fir_filter.v
new file mode 100644
index 000000000..ce9a66fd9
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/noc_shell_fir_filter.v
@@ -0,0 +1,297 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: noc_shell_fir_filter
+//
+// Description: A NoC Shell for RFNoC. This should eventually be replaced
+// by an auto-generated NoC Shell.
+//
+
+module noc_shell_fir_filter #(
+ parameter [31:0] NOC_ID = 32 'h0,
+ parameter [ 9:0] THIS_PORTID = 10 'd0,
+ parameter CHDR_W = 64,
+ parameter [ 0:0] CTRLPORT_SLV_EN = 1,
+ parameter [ 0:0] CTRLPORT_MST_EN = 1,
+ parameter SYNC_CLKS = 0,
+ parameter [ 5:0] NUM_DATA_I = 1,
+ parameter [ 5:0] NUM_DATA_O = 1,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter PYLD_FIFO_SIZE = 5,
+ parameter CTXT_FIFO_SIZE = 5,
+ parameter MTU = 10
+) (
+ //---------------------------------------------------------------------------
+ // Framework Interface
+ //---------------------------------------------------------------------------
+
+ // RFNoC Framework Clocks and Resets
+ input wire rfnoc_chdr_clk,
+ output wire rfnoc_chdr_rst,
+ input wire rfnoc_ctrl_clk,
+ output wire rfnoc_ctrl_rst,
+ // RFNoC Backend Interface
+ input wire [ 511:0] rfnoc_core_config,
+ output wire [ 511:0] rfnoc_core_status,
+ // CHDR Input Ports (from framework)
+ input wire [(CHDR_W*NUM_DATA_I)-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tready,
+ // CHDR Output Ports (to framework)
+ output wire [(CHDR_W*NUM_DATA_O)-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tready,
+ // AXIS-Ctrl Input Port (from framework)
+ input wire [ 31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ // AXIS-Ctrl Output Port (to framework)
+ output wire [ 31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+
+ //---------------------------------------------------------------------------
+ // Client Control Port Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+ // Master
+ output wire m_ctrlport_req_wr,
+ output wire m_ctrlport_req_rd,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [31:0] m_ctrlport_req_data,
+ output wire [ 3:0] m_ctrlport_req_byte_en,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ input wire m_ctrlport_resp_ack,
+ input wire [ 1:0] m_ctrlport_resp_status,
+ input wire [31:0] m_ctrlport_resp_data,
+ // Slave
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [ 9:0] s_ctrlport_req_portid,
+ input wire [15:0] s_ctrlport_req_rem_epid,
+ input wire [ 9:0] s_ctrlport_req_rem_portid,
+ input wire [31:0] s_ctrlport_req_data,
+ input wire [ 3:0] s_ctrlport_req_byte_en,
+ input wire s_ctrlport_req_has_time,
+ input wire [63:0] s_ctrlport_req_time,
+ output wire s_ctrlport_resp_ack,
+ output wire [ 1:0] s_ctrlport_resp_status,
+ output wire [31:0] s_ctrlport_resp_data,
+
+ //---------------------------------------------------------------------------
+ // Client Data Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+
+ // Output data stream (to user logic)
+ output wire [(NUM_DATA_I*ITEM_W*NIPC)-1:0] m_axis_payload_tdata,
+ output wire [ (NUM_DATA_I*NIPC)-1:0] m_axis_payload_tkeep,
+ output wire [ NUM_DATA_I-1:0] m_axis_payload_tlast,
+ output wire [ NUM_DATA_I-1:0] m_axis_payload_tvalid,
+ input wire [ NUM_DATA_I-1:0] m_axis_payload_tready,
+
+ // Input data stream (from user logic)
+ input wire [(NUM_DATA_O*ITEM_W*NIPC)-1:0] s_axis_payload_tdata,
+ input wire [ (NUM_DATA_O*NIPC)-1:0] s_axis_payload_tkeep,
+ input wire [ NUM_DATA_O-1:0] s_axis_payload_tlast,
+ input wire [ NUM_DATA_O-1:0] s_axis_payload_tvalid,
+ output wire [ NUM_DATA_O-1:0] s_axis_payload_tready,
+
+ // Output context stream (to user logic)
+ output wire [(NUM_DATA_I*CHDR_W)-1:0] m_axis_context_tdata,
+ output wire [ (4*NUM_DATA_I)-1:0] m_axis_context_tuser,
+ output wire [ NUM_DATA_I-1:0] m_axis_context_tlast,
+ output wire [ NUM_DATA_I-1:0] m_axis_context_tvalid,
+ input wire [ NUM_DATA_I-1:0] m_axis_context_tready,
+
+ // Input context stream (from user logic)
+ input wire [(NUM_DATA_O*CHDR_W)-1:0] s_axis_context_tdata,
+ input wire [ (4*NUM_DATA_O)-1:0] s_axis_context_tuser,
+ input wire [ NUM_DATA_O-1:0] s_axis_context_tlast,
+ input wire [ NUM_DATA_O-1:0] s_axis_context_tvalid,
+ output wire [ NUM_DATA_O-1:0] s_axis_context_tready
+);
+
+ localparam CTRL_FIFO_SIZE = 5;
+
+
+ //---------------------------------------------------------------------------
+ // Backend Interface
+ //---------------------------------------------------------------------------
+
+ wire data_i_flush_en;
+ wire [31:0] data_i_flush_timeout;
+ wire [63:0] data_i_flush_active;
+ wire [63:0] data_i_flush_done;
+ wire data_o_flush_en;
+ wire [31:0] data_o_flush_timeout;
+ wire [63:0] data_o_flush_active;
+ wire [63:0] data_o_flush_done;
+
+ backend_iface #(
+ .NOC_ID (NOC_ID),
+ .NUM_DATA_I (NUM_DATA_I),
+ .NUM_DATA_O (NUM_DATA_O),
+ .CTRL_FIFOSIZE (CTRL_FIFO_SIZE),
+ .MTU (MTU)
+ ) backend_iface_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst),
+ .data_i_flush_en (data_i_flush_en),
+ .data_i_flush_timeout (data_i_flush_timeout),
+ .data_i_flush_active (data_i_flush_active),
+ .data_i_flush_done (data_i_flush_done),
+ .data_o_flush_en (data_o_flush_en),
+ .data_o_flush_timeout (data_o_flush_timeout),
+ .data_o_flush_active (data_o_flush_active),
+ .data_o_flush_done (data_o_flush_done)
+ );
+
+ //---------------------------------------------------------------------------
+ // Control Path
+ //---------------------------------------------------------------------------
+
+ ctrlport_endpoint #(
+ .THIS_PORTID (THIS_PORTID ),
+ .SYNC_CLKS (0 ),
+ .AXIS_CTRL_MST_EN (CTRLPORT_SLV_EN),
+ .AXIS_CTRL_SLV_EN (CTRLPORT_MST_EN),
+ .SLAVE_FIFO_SIZE (CTRL_FIFO_SIZE )
+ ) ctrlport_ep_i (
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk ),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst ),
+ .ctrlport_clk (ctrlport_clk ),
+ .ctrlport_rst (ctrlport_rst ),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata ),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast ),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid ),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready ),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata ),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast ),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid ),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready ),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr ),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd ),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr ),
+ .m_ctrlport_req_data (m_ctrlport_req_data ),
+ .m_ctrlport_req_byte_en (m_ctrlport_req_byte_en ),
+ .m_ctrlport_req_has_time (m_ctrlport_req_has_time ),
+ .m_ctrlport_req_time (m_ctrlport_req_time ),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack ),
+ .m_ctrlport_resp_status (m_ctrlport_resp_status ),
+ .m_ctrlport_resp_data (m_ctrlport_resp_data ),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr ),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd ),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr ),
+ .s_ctrlport_req_portid (s_ctrlport_req_portid ),
+ .s_ctrlport_req_rem_epid (s_ctrlport_req_rem_epid ),
+ .s_ctrlport_req_rem_portid(s_ctrlport_req_rem_portid),
+ .s_ctrlport_req_data (s_ctrlport_req_data ),
+ .s_ctrlport_req_byte_en (s_ctrlport_req_byte_en ),
+ .s_ctrlport_req_has_time (s_ctrlport_req_has_time ),
+ .s_ctrlport_req_time (s_ctrlport_req_time ),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack ),
+ .s_ctrlport_resp_status (s_ctrlport_resp_status ),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data )
+ );
+
+ //---------------------------------------------------------------------------
+ // Data Path
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ generate
+
+ for (i = 0; i < NUM_DATA_I; i = i + 1) begin: chdr_to_data
+ chdr_to_axis_pyld_ctxt #(
+ .CHDR_W (CHDR_W ),
+ .ITEM_W (ITEM_W ),
+ .NIPC (NIPC ),
+ .SYNC_CLKS (SYNC_CLKS ),
+ .CONTEXT_FIFO_SIZE (CTXT_FIFO_SIZE),
+ .PAYLOAD_FIFO_SIZE (PYLD_FIFO_SIZE),
+ .CONTEXT_PREFETCH_EN (1 )
+ ) chdr_to_axis_pyld_ctxt_i (
+ .axis_chdr_clk (rfnoc_chdr_clk ),
+ .axis_chdr_rst (rfnoc_chdr_rst ),
+ .axis_data_clk (axis_data_clk ),
+ .axis_data_rst (axis_data_rst ),
+ .s_axis_chdr_tdata (s_rfnoc_chdr_tdata [(i*CHDR_W)+:CHDR_W] ),
+ .s_axis_chdr_tlast (s_rfnoc_chdr_tlast [i] ),
+ .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid [i] ),
+ .s_axis_chdr_tready (s_rfnoc_chdr_tready [i] ),
+ .m_axis_payload_tdata (m_axis_payload_tdata [(i*ITEM_W*NIPC)+:(ITEM_W*NIPC)]),
+ .m_axis_payload_tkeep (m_axis_payload_tkeep [(i*NIPC)+:NIPC] ),
+ .m_axis_payload_tlast (m_axis_payload_tlast [i] ),
+ .m_axis_payload_tvalid(m_axis_payload_tvalid[i] ),
+ .m_axis_payload_tready(m_axis_payload_tready[i] ),
+ .m_axis_context_tdata (m_axis_context_tdata [(i*CHDR_W)+:(CHDR_W)] ),
+ .m_axis_context_tuser (m_axis_context_tuser [(i*4)+:4] ),
+ .m_axis_context_tlast (m_axis_context_tlast [i] ),
+ .m_axis_context_tvalid(m_axis_context_tvalid[i] ),
+ .m_axis_context_tready(m_axis_context_tready[i] ),
+ .flush_en (data_i_flush_en ),
+ .flush_timeout (data_i_flush_timeout ),
+ .flush_active (data_i_flush_active [i] ),
+ .flush_done (data_i_flush_done [i] )
+ );
+ end
+
+ for (i = 0; i < NUM_DATA_O; i = i + 1) begin: data_to_chdr
+ axis_pyld_ctxt_to_chdr #(
+ .CHDR_W (CHDR_W ),
+ .ITEM_W (ITEM_W ),
+ .NIPC (NIPC ),
+ .SYNC_CLKS (SYNC_CLKS ),
+ .CONTEXT_FIFO_SIZE (CTXT_FIFO_SIZE),
+ .PAYLOAD_FIFO_SIZE (PYLD_FIFO_SIZE),
+ .CONTEXT_PREFETCH_EN (1 ),
+ .MTU (MTU )
+ ) axis_pyld_ctxt_to_chdr_i (
+ .axis_chdr_clk (rfnoc_chdr_clk ),
+ .axis_chdr_rst (rfnoc_chdr_rst ),
+ .axis_data_clk (axis_data_clk ),
+ .axis_data_rst (axis_data_rst ),
+ .m_axis_chdr_tdata (m_rfnoc_chdr_tdata [(i*CHDR_W)+:CHDR_W] ),
+ .m_axis_chdr_tlast (m_rfnoc_chdr_tlast [i] ),
+ .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid [i] ),
+ .m_axis_chdr_tready (m_rfnoc_chdr_tready [i] ),
+ .s_axis_payload_tdata (s_axis_payload_tdata [(i*ITEM_W*NIPC)+:(ITEM_W*NIPC)]),
+ .s_axis_payload_tkeep (s_axis_payload_tkeep [(i*NIPC)+:NIPC] ),
+ .s_axis_payload_tlast (s_axis_payload_tlast [i] ),
+ .s_axis_payload_tvalid(s_axis_payload_tvalid[i] ),
+ .s_axis_payload_tready(s_axis_payload_tready[i] ),
+ .s_axis_context_tdata (s_axis_context_tdata [(i*CHDR_W)+:(CHDR_W)] ),
+ .s_axis_context_tuser (s_axis_context_tuser [(i*4)+:4] ),
+ .s_axis_context_tlast (s_axis_context_tlast [i] ),
+ .s_axis_context_tvalid(s_axis_context_tvalid[i] ),
+ .s_axis_context_tready(s_axis_context_tready[i] ),
+ .framer_errors ( ),
+ .flush_en (data_o_flush_en ),
+ .flush_timeout (data_o_flush_timeout ),
+ .flush_active (data_o_flush_active [i] ),
+ .flush_done (data_o_flush_done [i] )
+ );
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter.v
new file mode 100644
index 000000000..f007049cc
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter.v
@@ -0,0 +1,343 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Description:
+//
+// Parameterized FIR filter RFNoC block with optional re-loadable
+// coefficients.
+//
+// It has several optimizations for resource utilization such as using half
+// the number of DSP slices for symmetric coefficients, skipping coefficients
+// that are always set to zero, and using internal DSP slice registers to
+// hold coefficients.
+//
+// For the most efficient DSP slice inference use these settings, set
+// COEFF_WIDTH to be less than 18.
+//
+// Parameters:
+//
+// COEFF_WIDTH : Coefficient width
+//
+// NUM_COEFFS : Number of coefficients / filter taps
+//
+// COEFFS_VEC : Vector of NUM_COEFFS values, each of width
+// COEFF_WIDTH, to initialize the filter
+// coefficients. Defaults to an impulse.
+//
+// RELOADABLE_COEFFS : Enable (1) or disable (0) reloading
+// coefficients at runtime
+//
+// SYMMETRIC_COEFFS : Reduce multiplier usage by approximately half
+// if coefficients are symmetric
+//
+// SKIP_ZERO_COEFFS : Reduce multiplier usage by assuming zero valued
+// coefficients in DEFAULT_COEFFS are always zero.
+// Useful for halfband filters.
+//
+// USE_EMBEDDED_REGS_COEFFS : Reduce register usage by only using embedded
+// registers in DSP slices. Updating taps while
+// streaming will cause temporary output
+// corruption!
+//
+// Note: If using USE_EMBEDDED_REGS_COEFFS, coefficients must be written at
+// least once since COEFFS_VEC is ignored!
+//
+
+
+module rfnoc_block_fir_filter #(
+ // RFNoC Parameters
+ parameter THIS_PORTID = 0,
+ parameter CHDR_W = 64,
+ parameter NUM_PORTS = 2,
+ parameter MTU = 10,
+ // FIR Filter Parameters
+ parameter COEFF_WIDTH = 16,
+ parameter NUM_COEFFS = 41,
+ parameter [NUM_COEFFS*COEFF_WIDTH-1:0] COEFFS_VEC = // Make impulse by default
+ {
+ {1'b0, {(COEFF_WIDTH-1){1'b1}} }, // Max positive value
+ {(COEFF_WIDTH*(NUM_COEFFS-1)){1'b0}} // Zero for remaining coefficients
+ },
+ parameter RELOADABLE_COEFFS = 1,
+ parameter SYMMETRIC_COEFFS = 0,
+ parameter SKIP_ZERO_COEFFS = 0,
+ parameter USE_EMBEDDED_REGS_COEFFS = 1
+)(
+ // Clock to use for signal processing
+ input wire ce_clk,
+
+
+ //---------------------------------------------------------------------------
+ // AXIS CHDR Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_chdr_clk,
+
+ // CHDR inputs from framework
+ input wire [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tready,
+
+ // CHDR outputs to framework
+ output wire [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
+
+ // Backend interface
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+
+
+ //---------------------------------------------------------------------------
+ // AXIS CTRL Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_ctrl_clk,
+
+ // CTRL port requests from framework
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+
+ // CTRL port requests to framework
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready
+);
+
+ `include "rfnoc_fir_filter_regs.vh"
+
+ // These are the only supported values for now
+ localparam ITEM_W = 32;
+ localparam NIPC = 1;
+
+
+ //---------------------------------------------------------------------------
+ // NoC Shell
+ //---------------------------------------------------------------------------
+
+ wire ctrlport_reg_req_wr;
+ wire ctrlport_reg_req_rd;
+ wire [19:0] ctrlport_reg_req_addr;
+ wire [31:0] ctrlport_reg_req_data;
+ wire ctrlport_reg_resp_ack;
+ wire [ 1:0] ctrlport_reg_resp_status;
+ wire [31:0] ctrlport_reg_resp_data;
+
+ wire [(NUM_PORTS*ITEM_W*NIPC)-1:0] axis_to_fir_tdata;
+ wire [ NUM_PORTS-1:0] axis_to_fir_tlast;
+ wire [ NUM_PORTS-1:0] axis_to_fir_tvalid;
+ wire [ NUM_PORTS-1:0] axis_to_fir_tready;
+
+ wire [(NUM_PORTS*ITEM_W*NIPC)-1:0] axis_from_fir_tdata;
+ wire [ NUM_PORTS-1:0] axis_from_fir_tlast;
+ wire [ NUM_PORTS-1:0] axis_from_fir_tvalid;
+ wire [ NUM_PORTS-1:0] axis_from_fir_tready;
+
+ wire [(NUM_PORTS*CHDR_W)-1:0] m_axis_context_tdata;
+ wire [ (4*NUM_PORTS)-1:0] m_axis_context_tuser;
+ wire [ NUM_PORTS-1:0] m_axis_context_tlast;
+ wire [ NUM_PORTS-1:0] m_axis_context_tvalid;
+ wire [ NUM_PORTS-1:0] m_axis_context_tready;
+
+ wire [(NUM_PORTS*CHDR_W)-1:0] s_axis_context_tdata;
+ wire [ (4*NUM_PORTS)-1:0] s_axis_context_tuser;
+ wire [ NUM_PORTS-1:0] s_axis_context_tlast;
+ wire [ NUM_PORTS-1:0] s_axis_context_tvalid;
+ wire [ NUM_PORTS-1:0] s_axis_context_tready;
+
+ wire rfnoc_chdr_rst;
+ wire ce_rst;
+
+ localparam NOC_ID = 32'hF112_0000;
+
+
+ // Cross the CHDR reset to the ddc_clk domain
+ synchronizer ce_rst_sync_i (
+ .clk (ce_clk),
+ .rst (1'b0),
+ .in (rfnoc_chdr_rst),
+ .out (ce_rst)
+ );
+
+
+ noc_shell_fir_filter #(
+ .NOC_ID (NOC_ID),
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .CTRLPORT_SLV_EN (0),
+ .CTRLPORT_MST_EN (1),
+ .NUM_DATA_I (NUM_PORTS),
+ .NUM_DATA_O (NUM_PORTS),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .PYLD_FIFO_SIZE (5),
+ .CTXT_FIFO_SIZE (5),
+ .MTU (MTU)
+ ) noc_shell_fir_filter_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_ctrl_rst (),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (ce_rst),
+ .m_ctrlport_req_wr (ctrlport_reg_req_wr),
+ .m_ctrlport_req_rd (ctrlport_reg_req_rd),
+ .m_ctrlport_req_addr (ctrlport_reg_req_addr),
+ .m_ctrlport_req_data (ctrlport_reg_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (),
+ .m_ctrlport_req_time (),
+ .m_ctrlport_resp_ack (ctrlport_reg_resp_ack),
+ .m_ctrlport_resp_status (ctrlport_reg_resp_status),
+ .m_ctrlport_resp_data (ctrlport_reg_resp_data),
+ .s_ctrlport_req_wr (1'b0),
+ .s_ctrlport_req_rd (1'b0),
+ .s_ctrlport_req_addr (20'b0),
+ .s_ctrlport_req_portid (10'b0),
+ .s_ctrlport_req_rem_epid (16'b0),
+ .s_ctrlport_req_rem_portid (10'b0),
+ .s_ctrlport_req_data (32'b0),
+ .s_ctrlport_req_byte_en (4'hF),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (),
+ .axis_data_clk (ce_clk),
+ .axis_data_rst (ce_rst),
+ .m_axis_payload_tdata (axis_to_fir_tdata),
+ .m_axis_payload_tkeep (),
+ .m_axis_payload_tlast (axis_to_fir_tlast),
+ .m_axis_payload_tvalid (axis_to_fir_tvalid),
+ .m_axis_payload_tready (axis_to_fir_tready),
+ .s_axis_payload_tdata (axis_from_fir_tdata),
+ .s_axis_payload_tkeep ({NUM_PORTS*NIPC{1'b1}}),
+ .s_axis_payload_tlast (axis_from_fir_tlast),
+ .s_axis_payload_tvalid (axis_from_fir_tvalid),
+ .s_axis_payload_tready (axis_from_fir_tready),
+ .m_axis_context_tdata (m_axis_context_tdata),
+ .m_axis_context_tuser (m_axis_context_tuser),
+ .m_axis_context_tlast (m_axis_context_tlast),
+ .m_axis_context_tvalid (m_axis_context_tvalid),
+ .m_axis_context_tready (m_axis_context_tready),
+ .s_axis_context_tdata (s_axis_context_tdata),
+ .s_axis_context_tuser (s_axis_context_tuser),
+ .s_axis_context_tlast (s_axis_context_tlast),
+ .s_axis_context_tvalid (s_axis_context_tvalid),
+ .s_axis_context_tready (s_axis_context_tready)
+ );
+
+
+ // The input packets are the same configuration as the output packets, so
+ // just use the header information for each incoming to create the header for
+ // each outgoing packet. This is done by connecting m_axis_context to
+ // directly to s_axis_context.
+ assign s_axis_context_tdata = m_axis_context_tdata;
+ assign s_axis_context_tuser = m_axis_context_tuser;
+ assign s_axis_context_tlast = m_axis_context_tlast;
+ assign s_axis_context_tvalid = m_axis_context_tvalid;
+ assign m_axis_context_tready = s_axis_context_tready;
+
+
+ //---------------------------------------------------------------------------
+ // Control Port Address Decoding
+ //---------------------------------------------------------------------------
+
+ wire [ NUM_PORTS-1:0] m_ctrlport_req_wr;
+ wire [ NUM_PORTS-1:0] m_ctrlport_req_rd;
+ wire [20*NUM_PORTS-1:0] m_ctrlport_req_addr;
+ wire [32*NUM_PORTS-1:0] m_ctrlport_req_data;
+ wire [ NUM_PORTS-1:0] m_ctrlport_resp_ack;
+ wire [32*NUM_PORTS-1:0] m_ctrlport_resp_data;
+
+ ctrlport_decoder #(
+ .NUM_SLAVES (NUM_PORTS),
+ .BASE_ADDR (0),
+ .SLAVE_ADDR_W (FIR_FILTER_ADDR_W)
+ ) ctrlport_deocder_i (
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (ce_rst),
+ .s_ctrlport_req_wr (ctrlport_reg_req_wr),
+ .s_ctrlport_req_rd (ctrlport_reg_req_rd),
+ .s_ctrlport_req_addr (ctrlport_reg_req_addr),
+ .s_ctrlport_req_data (ctrlport_reg_req_data),
+ .s_ctrlport_req_byte_en (4'b0),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (ctrlport_reg_resp_ack),
+ .s_ctrlport_resp_status (ctrlport_reg_resp_status),
+ .s_ctrlport_resp_data (ctrlport_reg_resp_data),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr),
+ .m_ctrlport_req_data (m_ctrlport_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (),
+ .m_ctrlport_req_time (),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack),
+ .m_ctrlport_resp_status ({NUM_PORTS{2'b0}}),
+ .m_ctrlport_resp_data (m_ctrlport_resp_data)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // FIR Core Instances
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ for (i = 0; i < NUM_PORTS; i = i+1) begin : gen_rfnoc_fir_filter_cores
+ rfnoc_fir_filter_core #(
+ .DATA_W (ITEM_W*NIPC),
+ .COEFF_WIDTH (COEFF_WIDTH),
+ .NUM_COEFFS (NUM_COEFFS),
+ .COEFFS_VEC (COEFFS_VEC),
+ .RELOADABLE_COEFFS (RELOADABLE_COEFFS),
+ .SYMMETRIC_COEFFS (SYMMETRIC_COEFFS),
+ .SKIP_ZERO_COEFFS (SKIP_ZERO_COEFFS),
+ .USE_EMBEDDED_REGS_COEFFS (USE_EMBEDDED_REGS_COEFFS)
+ ) rfnoc_fir_filter_core_i (
+ .clk (ce_clk),
+ .rst (ce_rst),
+ .s_ctrlport_req_wr (m_ctrlport_req_wr[i]),
+ .s_ctrlport_req_rd (m_ctrlport_req_rd[i]),
+ .s_ctrlport_req_addr (m_ctrlport_req_addr[20*i +: 20]),
+ .s_ctrlport_req_data (m_ctrlport_req_data[32*i +: 32]),
+ .s_ctrlport_resp_ack (m_ctrlport_resp_ack[i]),
+ .s_ctrlport_resp_data (m_ctrlport_resp_data[32*i +: 32]),
+ .s_axis_tdata (axis_to_fir_tdata[i*(ITEM_W*NIPC) +: (ITEM_W*NIPC)]),
+ .s_axis_tlast (axis_to_fir_tlast[i]),
+ .s_axis_tvalid (axis_to_fir_tvalid[i]),
+ .s_axis_tready (axis_to_fir_tready[i]),
+ .m_axis_tdata (axis_from_fir_tdata[i*(ITEM_W*NIPC) +: (ITEM_W*NIPC)]),
+ .m_axis_tlast (axis_from_fir_tlast[i]),
+ .m_axis_tvalid (axis_from_fir_tvalid[i]),
+ .m_axis_tready (axis_from_fir_tready[i])
+ );
+ end
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter_tb.sv
new file mode 100644
index 000000000..28b5493ac
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter_tb.sv
@@ -0,0 +1,524 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_fir_filter_tb
+//
+// Description: Testbench for rfnoc_block_fir_filter
+//
+
+
+module rfnoc_block_fir_filter_tb #(
+ parameter int NUM_PORTS = 2
+);
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+
+ `include "rfnoc_fir_filter_regs.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Simulation parameters
+ localparam real CHDR_CLK_PER = 6.0; // 166 MHz
+ localparam real CE_CLK_PER = 5.0; // 200 MHz
+ localparam int STALL_PROB = 25; // BFM stall probability
+
+ // DUT parameters to test
+ localparam int CHDR_W = 64;
+ localparam int THIS_PORTID = 'h123;
+ localparam int MTU = 8;
+ //
+ localparam int NUM_COEFFS = 41;
+ localparam int COEFF_WIDTH = 16;
+ localparam int RELOADABLE_COEFFS = 1;
+ localparam int SYMMETRIC_COEFFS = 1;
+ localparam int SKIP_ZERO_COEFFS = 1;
+ localparam int USE_EMBEDDED_REGS_COEFFS = 1;
+
+ localparam logic [COEFF_WIDTH*NUM_COEFFS-1:0] COEFFS_VEC_0 = {
+ 16'sd158, 16'sd0, 16'sd33, -16'sd0, -16'sd256,
+ 16'sd553, 16'sd573, -16'sd542, -16'sd1012, 16'sd349,
+ 16'sd1536, 16'sd123, -16'sd2097, -16'sd1012, 16'sd1633,
+ 16'sd1608, -16'sd3077, -16'sd5946, 16'sd3370, 16'sd10513,
+ 16'sd19295,
+ 16'sd10513, 16'sd3370, -16'sd5946, -16'sd3077, 16'sd1608,
+ 16'sd1633, -16'sd1012, -16'sd2097, 16'sd123, 16'sd1536,
+ 16'sd349, -16'sd1012, -16'sd542, 16'sd573, 16'sd553,
+ -16'sd256, -16'sd0, 16'sd33, 16'sd0, 16'sd158
+ };
+
+ localparam logic [COEFF_WIDTH*NUM_COEFFS-1:0] COEFFS_VEC_1 = {
+ 16'sd32767, 16'sd0, -16'sd32767, 16'sd0, 16'sd32767,
+ -16'sd32767, 16'sd32767, -16'sd32767, 16'sd32767, -16'sd32767,
+ 16'sd32767, 16'sd32767, 16'sd32767, 16'sd32767, 16'sd32767,
+ -16'sd32767, -16'sd32767, -16'sd32767, -16'sd32767, -16'sd32767,
+ 16'sd32767,
+ -16'sd32767, -16'sd32767, -16'sd32767, -16'sd32767, -16'sd32767,
+ 16'sd32767, 16'sd32767, 16'sd32767, 16'sd32767, 16'sd32767,
+ -16'sd32767, 16'sd32767, -16'sd32767, 16'sd32767, -16'sd32767,
+ 16'sd32767, 16'sd0, -16'sd32767, 16'sd0, 16'sd32767
+ };
+
+ //---------------------------------------------------------------------------
+ // Clocks
+ //---------------------------------------------------------------------------
+
+ bit rfnoc_chdr_clk;
+ bit rfnoc_ctrl_clk;
+
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst());
+ sim_clock_gen #(CE_CLK_PER) ce_clk_gen (.clk(ce_clk), .rst());
+
+
+ //---------------------------------------------------------------------------
+ // Bus Functional Models
+ //---------------------------------------------------------------------------
+
+ RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk);
+ AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+
+ // Bus functional model for a software block controller
+ RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl = new(backend, m_ctrl, s_ctrl);
+
+ // Connect block controller to BFMs
+ for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
+ initial begin
+ blk_ctrl.connect_master_data_port(i, m_chdr[i]);
+ blk_ctrl.connect_slave_data_port(i, s_chdr[i]);
+ blk_ctrl.set_master_stall_prob(i, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(i, STALL_PROB);
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // DUT
+ //---------------------------------------------------------------------------
+
+ logic [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tready;
+
+ logic [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tready;
+
+ // Map the array of BFMs to a flat vector for the DUT
+ genvar i;
+ for (i = 0; i < NUM_PORTS; i++) begin : gen_dut_connections
+ // Connect BFM master to DUT slave port
+ assign s_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata;
+ assign s_rfnoc_chdr_tlast[i] = m_chdr[i].tlast;
+ assign s_rfnoc_chdr_tvalid[i] = m_chdr[i].tvalid;
+ assign m_chdr[i].tready = s_rfnoc_chdr_tready[i];
+
+ // Connect BFM slave to DUT master port
+ assign s_chdr[i].tdata = m_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W];
+ assign s_chdr[i].tlast = m_rfnoc_chdr_tlast[i];
+ assign s_chdr[i].tvalid = m_rfnoc_chdr_tvalid[i];
+ assign m_rfnoc_chdr_tready[i] = s_chdr[i].tready;
+ end
+
+
+ rfnoc_block_fir_filter #(
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .NUM_PORTS (NUM_PORTS),
+ .MTU (MTU),
+ .COEFF_WIDTH (COEFF_WIDTH),
+ .NUM_COEFFS (NUM_COEFFS),
+ .COEFFS_VEC (COEFFS_VEC_0),
+ .RELOADABLE_COEFFS (RELOADABLE_COEFFS),
+ .SYMMETRIC_COEFFS (SYMMETRIC_COEFFS),
+ .SKIP_ZERO_COEFFS (SKIP_ZERO_COEFFS),
+ .USE_EMBEDDED_REGS_COEFFS (USE_EMBEDDED_REGS_COEFFS)
+ ) rfnoc_block_fir_filter_i (
+ .ce_clk (ce_clk),
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .rfnoc_core_config (backend.cfg),
+ .rfnoc_core_status (backend.sts),
+ .rfnoc_ctrl_clk (backend.ctrl_clk),
+ .s_rfnoc_ctrl_tdata (m_ctrl.tdata),
+ .s_rfnoc_ctrl_tlast (m_ctrl.tlast),
+ .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid),
+ .s_rfnoc_ctrl_tready (m_ctrl.tready),
+ .m_rfnoc_ctrl_tdata (s_ctrl.tdata),
+ .m_rfnoc_ctrl_tlast (s_ctrl.tlast),
+ .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid),
+ .m_rfnoc_ctrl_tready (s_ctrl.tready)
+ );
+
+
+
+ //---------------------------------------------------------------------------
+ // Helper Tasks
+ //---------------------------------------------------------------------------
+
+ // Translate the desired register access to a ctrlport write request.
+ task automatic write_reg(int port, byte addr, bit [31:0] value);
+ blk_ctrl.reg_write(port * (2**FIR_FILTER_ADDR_W) + addr, value);
+ endtask : write_reg
+
+ // Translate the desired register access to a ctrlport read request.
+ task automatic read_reg(int port, byte addr, output logic [31:0] value);
+ blk_ctrl.reg_read(port * (2**FIR_FILTER_ADDR_W), value);
+ endtask : read_reg
+
+
+
+ //---------------------------------------------------------------------------
+ // Test Process
+ //---------------------------------------------------------------------------
+
+ initial begin : tb_main
+ // Display testbench start message
+ test.start_tb("rfnoc_block_fir_filter_tb");
+
+ // Start the BFMs running
+ blk_ctrl.run();
+
+
+ //-------------------------------------------------------------------------
+ // Reset
+ //-------------------------------------------------------------------------
+
+ test.start_test("Wait for Reset", 10us);
+ fork
+ blk_ctrl.reset_chdr();
+ blk_ctrl.reset_ctrl();
+ join;
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Check NoC ID and Block Info
+ //-------------------------------------------------------------------------
+
+ test.start_test("Verify Block Info", 2us);
+ `ASSERT_ERROR(blk_ctrl.get_noc_id() == rfnoc_block_fir_filter_i.NOC_ID, "Incorrect NOC_ID Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect NUM_DATA_I Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect NUM_DATA_O Value");
+ `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value");
+ test.end_test();
+
+
+ // Test all ports
+ for (int port = 0; port < NUM_PORTS; port++) begin : port_loop
+
+ //-----------------------------------------------------------------------
+ // Check filter length
+ //-----------------------------------------------------------------------
+
+ begin
+ int num_coeffs, num_coeffs_to_send;
+
+ test.start_test("Check filter length", 20us);
+
+ read_reg(port, REG_FIR_NUM_COEFFS, num_coeffs);
+ `ASSERT_ERROR(num_coeffs, "Incorrect number of coefficients");
+
+ // If using symmetric coefficients, send just first half
+ if (SYMMETRIC_COEFFS) begin
+ num_coeffs_to_send = num_coeffs/2 + num_coeffs[0];
+ end else begin
+ num_coeffs_to_send = num_coeffs;
+ end
+
+ // If using embedded register, coefficients must be preloaded
+ if (USE_EMBEDDED_REGS_COEFFS) begin
+ int i;
+ for (i = 0; i < num_coeffs_to_send-1; i++) begin
+ write_reg(port, REG_FIR_LOAD_COEFF, COEFFS_VEC_0[COEFF_WIDTH*i +: COEFF_WIDTH]);
+ end
+ write_reg(port, REG_FIR_LOAD_COEFF_LAST, COEFFS_VEC_0[COEFF_WIDTH*i +: COEFF_WIDTH]);
+ end
+
+ test.end_test();
+ end
+
+
+ //-----------------------------------------------------------------------
+ // Test impulse response with default coefficients
+ //-----------------------------------------------------------------------
+ //
+ // Sending an impulse should cause the coefficients to be output.
+ //
+ //-----------------------------------------------------------------------
+
+ begin
+ chdr_word_t send_payload[$];
+ chdr_word_t recv_payload[$];
+ int num_bytes;
+ logic signed [15:0] i_samp, q_samp, i_coeff, q_coeff;
+ string s;
+
+ test.start_test("Test impulse response (default coefficients)", 20us);
+
+ // Generate packet containing an impulse and enqueue it for transfer
+ send_payload = {};
+ send_payload.push_back({16'b0, 16'b0, 16'h7FFF, 16'h7FFF});
+ for (int i = 0; i < NUM_COEFFS/2; i++) begin
+ send_payload.push_back(0);
+ end
+ blk_ctrl.send(port, send_payload, NUM_COEFFS*4);
+
+ // Enqueue two packets with zeros to push out the impulse from the
+ // pipeline (one to push out the data and one to overcome some pipeline
+ // registering).
+ send_payload = {};
+ for (int i = 0; i < NUM_COEFFS/2+1; i++) begin
+ send_payload.push_back(0);
+ end
+ for (int n = 0; n < 2; n++) begin
+ blk_ctrl.send(port, send_payload, NUM_COEFFS*4);
+ end
+
+ // Receive the result
+ blk_ctrl.recv(port, recv_payload, num_bytes);
+
+ // Check the length of the packet
+ `ASSERT_ERROR(
+ num_bytes == NUM_COEFFS*4,
+ "Received packet didn't have expected length"
+ );
+
+ for (int i = 0; i < NUM_COEFFS; i++) begin
+ // Compute the expected sample
+ i_coeff = $signed(COEFFS_VEC_0[COEFF_WIDTH*i +: COEFF_WIDTH]);
+ q_coeff = i_coeff;
+
+ // Grab the next sample
+ {i_samp, q_samp} = recv_payload[i/2][i[0]*32 +: 32];
+
+ // Check I / Q values
+ $sformat(
+ s, "Incorrect I value received on sample %0d! Expected: %0d, Received: %0d",
+ i, i_coeff, i_samp);
+ `ASSERT_ERROR(
+ (i_samp == i_coeff) || (i_samp-1 == i_coeff) || (i_samp+1 == i_coeff), s);
+ $sformat(
+ s, "Incorrect Q value received on sample %0d! Expected: %0d, Received: %0d",
+ i, q_coeff, q_samp);
+ `ASSERT_ERROR(
+ (q_samp == q_coeff) || (q_samp-1 == q_coeff) || (q_samp+1 == q_coeff), s);
+ end
+
+ test.end_test();
+ end
+
+
+ //-----------------------------------------------------------------------
+ // Load new coefficients
+ //-----------------------------------------------------------------------
+
+ begin
+ int i;
+ int num_coeffs_to_send;
+
+ // If using symmetric coefficients, send just first half
+ if (SYMMETRIC_COEFFS) begin
+ num_coeffs_to_send = NUM_COEFFS/2 + NUM_COEFFS[0];
+ end else begin
+ num_coeffs_to_send = NUM_COEFFS;
+ end
+
+ test.start_test("Load new coefficients", 20us);
+ for (i = 0; i < num_coeffs_to_send-1; i++) begin
+ write_reg(port, REG_FIR_LOAD_COEFF, COEFFS_VEC_1[COEFF_WIDTH*i +: COEFF_WIDTH]);
+ end
+ write_reg(port, REG_FIR_LOAD_COEFF_LAST, COEFFS_VEC_1[COEFF_WIDTH*i +: COEFF_WIDTH]);
+ test.end_test();
+ end
+
+
+ //-----------------------------------------------------------------------
+ // Test impulse response with new coefficients
+ //-----------------------------------------------------------------------
+ //
+ // Sending an impulse should cause the coefficients to be output.
+ //
+ //-----------------------------------------------------------------------
+
+ begin
+ chdr_word_t send_payload[$];
+ chdr_word_t recv_payload[$];
+ int num_bytes;
+ logic signed [15:0] i_samp, q_samp, i_coeff, q_coeff;
+ string s;
+
+ test.start_test("Test impulse response (loaded coefficients)", 20us);
+
+ // Generate packet containing an impulse and enqueue it for transfer
+ send_payload = {};
+ send_payload.push_back({16'b0, 16'b0, 16'h7FFF, 16'h7FFF});
+ for (int i = 0; i < NUM_COEFFS/2; i++) begin
+ send_payload.push_back(0);
+ end
+ blk_ctrl.send(port, send_payload, NUM_COEFFS*4);
+
+ // Enqueue two packets with zeros to push out the impulse from the
+ // pipeline (one to push out the data and one to overcome some pipeline
+ // registering).
+ send_payload = {};
+ for (int i = 0; i < NUM_COEFFS/2+1; i++) begin
+ send_payload.push_back(0);
+ end
+ for (int n = 0; n < 2; n++) begin
+ blk_ctrl.send(port, send_payload, NUM_COEFFS*4);
+ end
+
+ // Ignore the first two packets (discard the extra data we put in when
+ // we checked the default coefficients).
+ blk_ctrl.recv(port, recv_payload, num_bytes);
+ blk_ctrl.recv(port, recv_payload, num_bytes);
+
+ // Receive the result
+ blk_ctrl.recv(port, recv_payload, num_bytes);
+
+ // Check the length of the packet
+ `ASSERT_ERROR(
+ num_bytes == NUM_COEFFS*4,
+ "Received packet didn't have expected length"
+ );
+
+ for (int i = 0; i < NUM_COEFFS; i++) begin
+ // Compute the expected sample
+ i_coeff = $signed(COEFFS_VEC_1[COEFF_WIDTH*i +: COEFF_WIDTH]);
+ q_coeff = i_coeff;
+
+ // Grab the next sample
+ {i_samp, q_samp} = recv_payload[i/2][i[0]*32 +: 32];
+
+ // Check I / Q values
+ $sformat(
+ s, "Incorrect I value received on sample %0d! Expected: %0d, Received: %0d",
+ i, i_coeff, i_samp);
+ `ASSERT_ERROR(
+ (i_samp == i_coeff) || (i_samp-1 == i_coeff) || (i_samp+1 == i_coeff), s);
+ $sformat(
+ s, "Incorrect Q value received on sample %0d! Expected: %0d, Received: %0d",
+ i, q_coeff, q_samp);
+ `ASSERT_ERROR(
+ (q_samp == q_coeff) || (q_samp-1 == q_coeff) || (q_samp+1 == q_coeff), s);
+ end
+
+ test.end_test();
+ end
+
+
+ //-----------------------------------------------------------------------
+ // Test step response
+ //-----------------------------------------------------------------------
+
+ begin
+ chdr_word_t send_payload[$];
+ chdr_word_t recv_payload[$];
+ int num_bytes;
+ int coeff_sum;
+ logic signed [15:0] i_samp, q_samp;
+ string s;
+
+ test.start_test("Test step response", 20us);
+
+ // Generate a step function packet
+ send_payload = {};
+ for (int i = 0; i < NUM_COEFFS/2+1; i++) begin
+ send_payload.push_back({16'h7FFF,16'h7FFF,16'h7FFF,16'h7FFF});
+ end
+
+ // Enqueue step function two times, once to fill up the pipeline and
+ // another to get the actual response.
+ for (int n = 0; n < 2; n++) begin
+ blk_ctrl.send(port, send_payload, NUM_COEFFS*4);
+ end
+
+ // Enqueue two packets with zeros to push out the impulse from the
+ // pipeline (one to push out the data and one to overcome some pipeline
+ // registering).
+ send_payload = {};
+ for (int i = 0; i < NUM_COEFFS/2+1; i++) begin
+ send_payload.push_back(0);
+ end
+ for (int n = 0; n < 2; n++) begin
+ blk_ctrl.send(port, send_payload, NUM_COEFFS*4);
+ end
+
+ // Ignore the first two packets (discard the extra data we put in
+ // during the previous test).
+ for (int n = 0; n < 3; n++) begin
+ blk_ctrl.recv(port, recv_payload, num_bytes);
+ end
+
+ // Receive the result
+ blk_ctrl.recv(port, recv_payload, num_bytes);
+
+ // Check the length of the packet
+ `ASSERT_ERROR(
+ num_bytes == NUM_COEFFS*4,
+ "Received packet didn't have expected length"
+ );
+
+ // Calculate sum of all the coefficients
+ coeff_sum = 0;
+ for (int i = 0; i < NUM_COEFFS; i++) begin
+ coeff_sum += $signed(COEFFS_VEC_1[COEFF_WIDTH*i +: COEFF_WIDTH]);
+ end
+
+ for (int i = 0; i < NUM_COEFFS; i++) begin
+ // Grab the next sample
+ {i_samp, q_samp} = recv_payload[i/2][i[0]*32 +: 32];
+
+ // Check I / Q values
+ $sformat(
+ s, "Incorrect I value received on sample %0d! Expected: %0d, Received: %0d",
+ i, coeff_sum, i_samp);
+ `ASSERT_ERROR(
+ (i_samp == coeff_sum) || (i_samp-1 == coeff_sum) || (i_samp+1 == coeff_sum),
+ s
+ );
+ $sformat(
+ s, "Incorrect Q value received on sample %0d! Expected: %0d, Received: %0d",
+ i, coeff_sum, q_samp);
+ `ASSERT_ERROR(
+ (q_samp == coeff_sum) || (q_samp-1 == coeff_sum) || (q_samp+1 == coeff_sum),
+ s
+ );
+ end
+
+ test.end_test();
+ end
+
+ end : port_loop
+
+
+ //-------------------------------------------------------------------------
+ // All done!
+ //-------------------------------------------------------------------------
+
+ test.end_tb(1);
+
+ end : tb_main
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_fir_filter_core.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_fir_filter_core.v
new file mode 100644
index 000000000..774f43761
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_fir_filter_core.v
@@ -0,0 +1,228 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Description:
+//
+// Core module for a single instance of an FIR filter, implementing the
+// registers and signal processing for a single I/Q filter. It assumes the
+// data stream is an IQ pair with I in the upper 32 bits and Q is the lower
+// 32 bits.
+//
+// Parameters:
+//
+// DATA_W : Width of the input/output data stream to
+// process.
+//
+// BASE_ADDR : Control port base address to which this block
+// responds.
+//
+// COEFF_WIDTH : Coefficient width
+//
+// NUM_COEFFS : Number of coefficients / filter taps
+//
+// COEFFS_VEC : Vector of NUM_COEFFS values, each of width
+// COEFF_WIDTH, to initialize the filter
+// coefficients. Defaults to an impulse.
+//
+// RELOADABLE_COEFFS : Enable (1) or disable (0) reloading
+// coefficients at runtime
+//
+// SYMMETRIC_COEFFS : Reduce multiplier usage by approximately half
+// if coefficients are symmetric
+//
+// SKIP_ZERO_COEFFS : Reduce multiplier usage by assuming zero valued
+// coefficients in DEFAULT_COEFFS are always zero.
+// Useful for halfband filters.
+//
+// USE_EMBEDDED_REGS_COEFFS : Reduce register usage by only using embedded
+// registers in DSP slices. Updating taps while
+// streaming will cause temporary output
+// corruption!
+//
+// Note: If using USE_EMBEDDED_REGS_COEFFS, coefficients must be written at
+// least once since COEFFS_VEC is ignored!
+
+
+module rfnoc_fir_filter_core #(
+ parameter DATA_W = 32,
+ parameter [19:0] BASE_ADDR = 0,
+
+ // FIR Filter Parameters
+ parameter COEFF_WIDTH = 16,
+ parameter NUM_COEFFS = 41,
+ parameter [NUM_COEFFS*COEFF_WIDTH-1:0] COEFFS_VEC = // Make impulse by default
+ {
+ {1'b0, {(COEFF_WIDTH-1){1'b1}} }, // Max positive value
+ {(COEFF_WIDTH*(NUM_COEFFS-1)){1'b0}} // Zero for remaining coefficients
+ },
+ parameter RELOADABLE_COEFFS = 1,
+ parameter SYMMETRIC_COEFFS = 0,
+ parameter SKIP_ZERO_COEFFS = 0,
+ parameter USE_EMBEDDED_REGS_COEFFS = 1
+) (
+
+ input wire clk,
+ input wire rst,
+
+ //---------------------------------------------------------------------------
+ // AXIS CTRL Port
+ //---------------------------------------------------------------------------
+
+ // Master
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ output reg s_ctrlport_resp_ack,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ //---------------------------------------------------------------------------
+ // Data Interface
+ //---------------------------------------------------------------------------
+
+ // Input data stream
+ input wire [DATA_W-1:0] s_axis_tdata,
+ input wire s_axis_tlast,
+ input wire s_axis_tvalid,
+ output wire s_axis_tready,
+
+ // Output data stream
+ output wire [DATA_W-1:0] m_axis_tdata,
+ output wire m_axis_tlast,
+ output wire m_axis_tvalid,
+ input wire m_axis_tready
+);
+
+ reg [COEFF_WIDTH-1:0] m_axis_reload_tdata;
+ reg m_axis_reload_tvalid;
+ reg m_axis_reload_tlast;
+ wire m_axis_reload_tready;
+
+
+ //---------------------------------------------------------------------------
+ // Registers
+ //---------------------------------------------------------------------------
+
+ `include "rfnoc_fir_filter_regs.vh"
+
+ // Separate the address into the block and register portions. Ignore the byte
+ // offset.
+ wire [20:0] block_addr = s_ctrlport_req_addr[19:FIR_FILTER_ADDR_W];
+ wire [19:0] reg_addr = { s_ctrlport_req_addr[FIR_FILTER_ADDR_W:2], 2'b0 };
+
+ always @(posedge clk) begin
+ if (rst) begin
+ s_ctrlport_resp_ack <= 0;
+ m_axis_reload_tvalid <= 0;
+ s_ctrlport_resp_data <= {32{1'bX}};
+ m_axis_reload_tdata <= {DATA_W{1'bX}};
+ m_axis_reload_tlast <= 1'bX;
+ end else if (block_addr == BASE_ADDR) begin
+ // Default assignments
+ s_ctrlport_resp_ack <= 0;
+ s_ctrlport_resp_data <= 0;
+
+ // Handle write acknowledgments. Don't ack the register write until it
+ // gets accepted by the FIR filter.
+ if (m_axis_reload_tvalid && m_axis_reload_tready) begin
+ m_axis_reload_tvalid <= 1'b0;
+ s_ctrlport_resp_ack <= 1'b1;
+ end
+
+ // Handle register writes
+ if (s_ctrlport_req_wr) begin
+ if (reg_addr == REG_FIR_LOAD_COEFF) begin
+ m_axis_reload_tdata <= s_ctrlport_req_data[COEFF_WIDTH-1:0];
+ m_axis_reload_tvalid <= 1'b1;
+ m_axis_reload_tlast <= 1'b0;
+ end else if (reg_addr == REG_FIR_LOAD_COEFF_LAST) begin
+ m_axis_reload_tdata <= s_ctrlport_req_data[COEFF_WIDTH-1:0];
+ m_axis_reload_tvalid <= 1'b1;
+ m_axis_reload_tlast <= 1'b1;
+ end
+ end
+
+ // Handle register reads
+ if (s_ctrlport_req_rd) begin
+ // Ignore the upper bits so the we respond to any port
+ if (reg_addr == REG_FIR_NUM_COEFFS) begin
+ s_ctrlport_resp_data <= NUM_COEFFS;
+ s_ctrlport_resp_ack <= 1;
+ end
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // FIR Filter Instances
+ //---------------------------------------------------------------------------
+
+ localparam IN_WIDTH = DATA_W/2;
+ localparam OUT_WIDTH = DATA_W/2;
+
+ // I
+ axi_fir_filter #(
+ .IN_WIDTH (IN_WIDTH),
+ .COEFF_WIDTH (COEFF_WIDTH),
+ .OUT_WIDTH (OUT_WIDTH),
+ .NUM_COEFFS (NUM_COEFFS),
+ .COEFFS_VEC (COEFFS_VEC),
+ .RELOADABLE_COEFFS (RELOADABLE_COEFFS),
+ .BLANK_OUTPUT (1),
+ // Optional optimizations
+ .SYMMETRIC_COEFFS (SYMMETRIC_COEFFS),
+ .SKIP_ZERO_COEFFS (SKIP_ZERO_COEFFS),
+ .USE_EMBEDDED_REGS_COEFFS (USE_EMBEDDED_REGS_COEFFS)
+ ) inst_axi_fir_filter_i (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .s_axis_data_tdata (s_axis_tdata[2*IN_WIDTH-1:IN_WIDTH]),
+ .s_axis_data_tlast (s_axis_tlast),
+ .s_axis_data_tvalid (s_axis_tvalid),
+ .s_axis_data_tready (s_axis_tready),
+ .m_axis_data_tdata (m_axis_tdata[2*OUT_WIDTH-1:OUT_WIDTH]),
+ .m_axis_data_tlast (m_axis_tlast),
+ .m_axis_data_tvalid (m_axis_tvalid),
+ .m_axis_data_tready (m_axis_tready),
+ .s_axis_reload_tdata (m_axis_reload_tdata),
+ .s_axis_reload_tlast (m_axis_reload_tlast),
+ .s_axis_reload_tvalid (m_axis_reload_tvalid),
+ .s_axis_reload_tready (m_axis_reload_tready)
+ );
+
+ // Q
+ axi_fir_filter #(
+ .IN_WIDTH (IN_WIDTH),
+ .COEFF_WIDTH (COEFF_WIDTH),
+ .OUT_WIDTH (OUT_WIDTH),
+ .NUM_COEFFS (NUM_COEFFS),
+ .COEFFS_VEC (COEFFS_VEC),
+ .RELOADABLE_COEFFS (RELOADABLE_COEFFS),
+ .BLANK_OUTPUT (1),
+ // Optional optimizations
+ .SYMMETRIC_COEFFS (SYMMETRIC_COEFFS),
+ .SKIP_ZERO_COEFFS (SKIP_ZERO_COEFFS),
+ .USE_EMBEDDED_REGS_COEFFS (USE_EMBEDDED_REGS_COEFFS)
+ ) inst_axi_fir_filter_q (
+ .clk (clk),
+ .reset (rst),
+ .clear (1'b0),
+ .s_axis_data_tdata (s_axis_tdata[IN_WIDTH-1:0]),
+ .s_axis_data_tlast (s_axis_tlast),
+ .s_axis_data_tvalid (s_axis_tvalid),
+ .s_axis_data_tready (),
+ .m_axis_data_tdata (m_axis_tdata[OUT_WIDTH-1:0]),
+ .m_axis_data_tlast (),
+ .m_axis_data_tvalid (),
+ .m_axis_data_tready (m_axis_tready),
+ .s_axis_reload_tdata (m_axis_reload_tdata),
+ .s_axis_reload_tlast (m_axis_reload_tlast),
+ .s_axis_reload_tvalid (m_axis_reload_tvalid),
+ .s_axis_reload_tready ()
+ );
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_fir_filter_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_fir_filter_regs.vh
new file mode 100644
index 000000000..0e070e3a3
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_fir_filter_regs.vh
@@ -0,0 +1,51 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: fir_filter_regs (Header)
+//
+// Description: Header file for rfnoc_block_fir_filter. All registers are
+// 32-bit words from software's perspective.
+//
+
+// Address space size, per FIR filter. That is, each filter is separated in the
+// CTRL Port address space by 2^FIR_FILTER_ADDR_W bytes.
+localparam FIR_FILTER_ADDR_W = 4;
+
+
+
+// REG_FIR_NUM_COEFFS (R)
+//
+// Contains the number of coefficients for the filter.
+//
+// [31:0] : Returns the number of coefficients (read-only)
+//
+localparam REG_FIR_NUM_COEFFS = 'h0;
+
+
+// REG_FIR_LOAD_COEFF (R)
+//
+// Register for inputting the next coefficient to be loaded into the filter. To
+// load a new set of filter coefficients, write NUM_COEFFS-1 coefficients to
+// this register, then write the last coefficient to REG_FIR_LOAD_COEFF_LAST.
+// The width of each coefficient is set by the COEFF_WIDTH parameter on the
+// block.
+//
+// [31:(32-COEFF_WIDTH)] : Reserved
+// [COEFF_WIDTH-1:0] : The next coefficient to be loaded
+//
+localparam REG_FIR_LOAD_COEFF = 'h4;
+
+
+// REG_FIR_LOAD_COEFF_LAST (R)
+//
+// Register for inputting the last coefficient to be loaded into the filter. To
+// load a new set of filter coefficients, write NUM_COEFFS-1 coefficients to
+// REG_FIR_LOAD_COEFF, then write the last coefficient to this register. The
+// width of each coefficient is set by the COEFF_WIDTH parameter on the block.
+//
+// [31:(32-COEFF_WIDTH)] : Reserved
+// [COEFF_WIDTH-1:0] : The next coefficient to be loaded
+//
+localparam REG_FIR_LOAD_COEFF_LAST = 'h8; \ No newline at end of file
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/Makefile
new file mode 100644
index 000000000..30ce14aec
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/Makefile
@@ -0,0 +1,45 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../../../top)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
+include Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_OOT_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+SIM_TOP = rfnoc_block_null_src_sink_tb
+
+SIM_SRCS = \
+$(abspath rfnoc_block_null_src_sink_tb.sv) \
+
+# MODELSIM_USER_DO = $(abspath wave.do)
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+# Include all simulator specific makefiles here
+# Each should define a unique target to simulate
+# e.g. xsim, vsim, etc and a common "clean" target
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/Makefile.srcs
new file mode 100644
index 000000000..a99bec7db
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/Makefile.srcs
@@ -0,0 +1,12 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Brand
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+##################################################
+# RFNoC Utility Sources
+##################################################
+RFNOC_OOT_SRCS += $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_null_src_sink/, \
+rfnoc_block_null_src_sink.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink.v
new file mode 100644
index 000000000..f4f4d7651
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink.v
@@ -0,0 +1,338 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_null_src_sink
+// Description:
+//
+// Parameters:
+//
+// Signals:
+
+module rfnoc_block_null_src_sink #(
+ parameter [9:0] THIS_PORTID = 10'd0,
+ parameter CHDR_W = 64,
+ parameter NIPC = 2,
+ parameter [5:0] MTU = 10
+)(
+ // RFNoC Framework Clocks and Resets
+ input wire rfnoc_chdr_clk,
+ input wire rfnoc_ctrl_clk,
+ // RFNoC Backend Interface
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+ // 2 CHDR Input Ports (from framework)
+ input wire [(CHDR_W*2)-1:0] s_rfnoc_chdr_tdata,
+ input wire [1:0] s_rfnoc_chdr_tlast,
+ input wire [1:0] s_rfnoc_chdr_tvalid,
+ output wire [1:0] s_rfnoc_chdr_tready,
+ // 2 CHDR Output Ports (to framework)
+ output wire [(CHDR_W*2)-1:0] m_rfnoc_chdr_tdata,
+ output wire [1:0] m_rfnoc_chdr_tlast,
+ output wire [1:0] m_rfnoc_chdr_tvalid,
+ input wire [1:0] m_rfnoc_chdr_tready,
+ // AXIS-Ctrl Input Port (from framework)
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ // AXIS-Ctrl Output Port (to framework)
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready
+);
+
+ `include "../../core/rfnoc_chdr_utils.vh"
+
+ localparam [19:0] REG_CTRL_STATUS = 20'h00;
+ localparam [19:0] REG_SRC_LINES_PER_PKT = 20'h04;
+ localparam [19:0] REG_SRC_BYTES_PER_PKT = 20'h08;
+ localparam [19:0] REG_SRC_THROTTLE_CYC = 20'h0C;
+ localparam [19:0] REG_SNK_LINE_CNT_LO = 20'h10;
+ localparam [19:0] REG_SNK_LINE_CNT_HI = 20'h14;
+ localparam [19:0] REG_SNK_PKT_CNT_LO = 20'h18;
+ localparam [19:0] REG_SNK_PKT_CNT_HI = 20'h1C;
+ localparam [19:0] REG_SRC_LINE_CNT_LO = 20'h20;
+ localparam [19:0] REG_SRC_LINE_CNT_HI = 20'h24;
+ localparam [19:0] REG_SRC_PKT_CNT_LO = 20'h28;
+ localparam [19:0] REG_SRC_PKT_CNT_HI = 20'h2C;
+ localparam [19:0] REG_LOOP_LINE_CNT_LO = 20'h30;
+ localparam [19:0] REG_LOOP_LINE_CNT_HI = 20'h34;
+ localparam [19:0] REG_LOOP_PKT_CNT_LO = 20'h38;
+ localparam [19:0] REG_LOOP_PKT_CNT_HI = 20'h3C;
+
+ wire rfnoc_chdr_rst;
+ wire rfnoc_ctrl_rst;
+
+ wire ctrlport_req_wr;
+ wire ctrlport_req_rd;
+ wire [19:0] ctrlport_req_addr;
+ wire [31:0] ctrlport_req_data;
+ reg ctrlport_resp_ack;
+ reg [31:0] ctrlport_resp_data;
+
+ wire [(32*NIPC)-1:0] src_pyld_tdata , snk_pyld_tdata , loop_pyld_tdata ;
+ wire [NIPC-1:0] src_pyld_tkeep , snk_pyld_tkeep , loop_pyld_tkeep ;
+ wire src_pyld_tlast , snk_pyld_tlast , loop_pyld_tlast ;
+ wire src_pyld_tvalid, snk_pyld_tvalid, loop_pyld_tvalid;
+ wire src_pyld_tready, snk_pyld_tready, loop_pyld_tready;
+
+ wire [CHDR_W-1:0] src_ctxt_tdata , snk_ctxt_tdata , loop_ctxt_tdata ;
+ wire [3:0] src_ctxt_tuser , snk_ctxt_tuser , loop_ctxt_tuser ;
+ wire src_ctxt_tlast , snk_ctxt_tlast , loop_ctxt_tlast ;
+ wire src_ctxt_tvalid, snk_ctxt_tvalid, loop_ctxt_tvalid;
+ wire src_ctxt_tready, snk_ctxt_tready, loop_ctxt_tready;
+
+ // NoC Shell
+ // ---------------------------
+ noc_shell_generic_ctrlport_pyld_chdr #(
+ .NOC_ID (32'h0000_0001),
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .CTRL_FIFOSIZE (5),
+ .CTRLPORT_SLV_EN (0),
+ .NUM_DATA_I (2),
+ .NUM_DATA_O (2),
+ .ITEM_W (32),
+ .NIPC (NIPC),
+ .MTU (MTU),
+ .CTXT_FIFOSIZE (1),
+ .PYLD_FIFOSIZE (1)
+ ) noc_shell_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk ),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst ),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk ),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst ),
+ .rfnoc_core_config (rfnoc_core_config ),
+ .rfnoc_core_status (rfnoc_core_status ),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata ),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast ),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid ),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready ),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata ),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast ),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid ),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready ),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata ),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast ),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid ),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready ),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata ),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast ),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid ),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready ),
+ .m_ctrlport_req_wr (ctrlport_req_wr ),
+ .m_ctrlport_req_rd (ctrlport_req_rd ),
+ .m_ctrlport_req_addr (ctrlport_req_addr ),
+ .m_ctrlport_req_data (ctrlport_req_data ),
+ .m_ctrlport_req_byte_en ( ),
+ .m_ctrlport_req_has_time ( ),
+ .m_ctrlport_req_time ( ),
+ .m_ctrlport_resp_ack (ctrlport_resp_ack ),
+ .m_ctrlport_resp_status (2'd0 ),
+ .m_ctrlport_resp_data (ctrlport_resp_data ),
+ .s_ctrlport_req_wr ('h0 ),
+ .s_ctrlport_req_rd ('h0 ),
+ .s_ctrlport_req_addr ('h0 ),
+ .s_ctrlport_req_portid ('h0 ),
+ .s_ctrlport_req_rem_epid ('h0 ),
+ .s_ctrlport_req_rem_portid('h0 ),
+ .s_ctrlport_req_data ('h0 ),
+ .s_ctrlport_req_byte_en ('h0 ),
+ .s_ctrlport_req_has_time ('h0 ),
+ .s_ctrlport_req_time ('h0 ),
+ .s_ctrlport_resp_ack ( ),
+ .s_ctrlport_resp_status ( ),
+ .s_ctrlport_resp_data ( ),
+ .m_axis_payload_tdata ({loop_pyld_tdata , snk_pyld_tdata }),
+ .m_axis_payload_tkeep ({loop_pyld_tkeep , snk_pyld_tkeep }),
+ .m_axis_payload_tlast ({loop_pyld_tlast , snk_pyld_tlast }),
+ .m_axis_payload_tvalid ({loop_pyld_tvalid, snk_pyld_tvalid}),
+ .m_axis_payload_tready ({loop_pyld_tready, snk_pyld_tready}),
+ .m_axis_context_tdata ({loop_ctxt_tdata , snk_ctxt_tdata }),
+ .m_axis_context_tuser ({loop_ctxt_tuser , snk_ctxt_tuser }),
+ .m_axis_context_tlast ({loop_ctxt_tlast , snk_ctxt_tlast }),
+ .m_axis_context_tvalid ({loop_ctxt_tvalid, snk_ctxt_tvalid}),
+ .m_axis_context_tready ({loop_ctxt_tready, snk_ctxt_tready}),
+ .s_axis_payload_tdata ({loop_pyld_tdata , src_pyld_tdata }),
+ .s_axis_payload_tkeep ({loop_pyld_tkeep , src_pyld_tkeep }),
+ .s_axis_payload_tlast ({loop_pyld_tlast , src_pyld_tlast }),
+ .s_axis_payload_tvalid ({loop_pyld_tvalid, src_pyld_tvalid}),
+ .s_axis_payload_tready ({loop_pyld_tready, src_pyld_tready}),
+ .s_axis_context_tdata ({loop_ctxt_tdata , src_ctxt_tdata }),
+ .s_axis_context_tuser ({loop_ctxt_tuser , src_ctxt_tuser }),
+ .s_axis_context_tlast ({loop_ctxt_tlast , src_ctxt_tlast }),
+ .s_axis_context_tvalid ({loop_ctxt_tvalid, src_ctxt_tvalid}),
+ .s_axis_context_tready ({loop_ctxt_tready, src_ctxt_tready})
+ );
+
+ // Packet Counters
+ // ---------------------------
+ reg reg_clear_cnts = 1'b0;
+ reg [63:0] snk_line_cnt = 64'd0, snk_pkt_cnt = 64'd0;
+ reg [63:0] src_line_cnt = 64'd0, src_pkt_cnt = 64'd0;
+ reg [63:0] loop_line_cnt = 64'd0, loop_pkt_cnt = 64'd0;
+
+ always @(posedge rfnoc_chdr_clk) begin
+ if (rfnoc_chdr_rst | reg_clear_cnts) begin
+ snk_line_cnt <= 64'd0;
+ snk_pkt_cnt <= 64'd0;
+ src_line_cnt <= 64'd0;
+ src_pkt_cnt <= 64'd0;
+ loop_line_cnt <= 64'd0;
+ loop_pkt_cnt <= 64'd0;
+ end else begin
+ if (snk_pyld_tvalid & snk_pyld_tready) begin
+ snk_line_cnt <= snk_line_cnt + 1;
+ if (snk_pyld_tlast)
+ snk_pkt_cnt <= snk_pkt_cnt + 1;
+ end
+ if (src_pyld_tvalid & src_pyld_tready) begin
+ src_line_cnt <= src_line_cnt + 1;
+ if (src_pyld_tlast)
+ src_pkt_cnt <= src_pkt_cnt + 1;
+ end
+ if (loop_pyld_tvalid & loop_pyld_tready) begin
+ loop_line_cnt <= loop_line_cnt + 1;
+ if (loop_pyld_tlast)
+ loop_pkt_cnt <= loop_pkt_cnt + 1;
+ end
+ end
+ end
+
+ // NULL Sink
+ // ---------------------------
+ assign snk_pyld_tready = 1'b1;
+ assign snk_ctxt_tready = 1'b1;
+
+ // NULL Source
+ // ---------------------------
+ reg reg_src_en = 1'b0;
+ reg [11:0] reg_src_lpp = 12'd0;
+ reg [15:0] reg_src_bpp = 16'd0;
+ reg [9:0] reg_throttle_cyc = 10'd0;
+
+ localparam [1:0] ST_HDR = 2'd0;
+ localparam [1:0] ST_PYLD = 2'd1;
+ localparam [1:0] ST_WAIT = 2'd2;
+
+ reg [1:0] state = ST_HDR;
+ reg [11:0] lines_left = 12'd0;
+ reg [9:0] throttle_cntr = 10'd0;
+
+ always @(posedge rfnoc_chdr_clk) begin
+ if (rfnoc_chdr_rst) begin
+ state <= ST_HDR;
+ end else begin
+ case (state)
+ ST_HDR: begin
+ if (src_ctxt_tvalid && src_ctxt_tready) begin
+ state <= ST_PYLD;
+ lines_left <= reg_src_lpp;
+ end
+ end
+ ST_PYLD: begin
+ if (src_pyld_tvalid && src_pyld_tready) begin
+ if (src_pyld_tlast) begin
+ if (reg_throttle_cyc == 10'd0) begin
+ state <= ST_HDR;
+ end else begin
+ state <= ST_WAIT;
+ throttle_cntr <= reg_throttle_cyc;
+ end
+ end else begin
+ lines_left <= lines_left - 12'd1;
+ end
+ end
+ end
+ ST_WAIT: begin
+ if (throttle_cntr == 10'd0)
+ state <= ST_HDR;
+ else
+ throttle_cntr <= throttle_cntr - 10'd1;
+ end
+ default: begin
+ state <= ST_HDR;
+ end
+ endcase
+ end
+ end
+
+ assign src_pyld_tdata = {NIPC{{~src_line_cnt[15:0], src_line_cnt[15:0]}}};
+ assign src_pyld_tkeep = {NIPC{1'b1}};
+ assign src_pyld_tlast = (lines_left == 12'd0);
+ assign src_pyld_tvalid = (state == ST_PYLD);
+
+ assign src_ctxt_tdata = chdr_build_header(
+ 6'd0, 1'b0, 1'b0, CHDR_PKT_TYPE_DATA, CHDR_NO_MDATA, src_pkt_cnt[15:0], reg_src_bpp, 16'd0);
+ assign src_ctxt_tuser = CONTEXT_FIELD_HDR;
+ assign src_ctxt_tlast = 1'b1;
+ assign src_ctxt_tvalid = (state == ST_HDR && reg_src_en);
+
+
+ // Register Interface
+ // ---------------------------
+ always @(posedge rfnoc_chdr_clk) begin
+ if (rfnoc_chdr_rst) begin
+ ctrlport_resp_ack <= 1'b0;
+ end else begin
+ // All transactions finish in 1 cycle
+ ctrlport_resp_ack <= ctrlport_req_wr | ctrlport_req_rd;
+ // Handle register writes
+ if (ctrlport_req_wr) begin
+ case(ctrlport_req_addr)
+ REG_CTRL_STATUS:
+ {reg_src_en, reg_clear_cnts} <= ctrlport_req_data[1:0];
+ REG_SRC_LINES_PER_PKT:
+ reg_src_lpp <= ctrlport_req_data[11:0];
+ REG_SRC_BYTES_PER_PKT:
+ reg_src_bpp <= ctrlport_req_data[15:0];
+ REG_SRC_THROTTLE_CYC:
+ reg_throttle_cyc <= ctrlport_req_data[9:0];
+ endcase
+ end
+ // Handle register reads
+ if (ctrlport_req_rd) begin
+ case(ctrlport_req_addr)
+ REG_CTRL_STATUS:
+ ctrlport_resp_data <= {NIPC[7:0], 8'd32, state, 12'h0, reg_src_en, reg_clear_cnts};
+ REG_SRC_LINES_PER_PKT:
+ ctrlport_resp_data <= {20'h0, reg_src_lpp};
+ REG_SRC_BYTES_PER_PKT:
+ ctrlport_resp_data <= {16'h0, reg_src_bpp};
+ REG_SRC_THROTTLE_CYC:
+ ctrlport_resp_data <= {22'h0, reg_throttle_cyc};
+ REG_SNK_LINE_CNT_LO:
+ ctrlport_resp_data <= snk_line_cnt[31:0];
+ REG_SNK_LINE_CNT_HI:
+ ctrlport_resp_data <= snk_line_cnt[63:32];
+ REG_SNK_PKT_CNT_LO:
+ ctrlport_resp_data <= snk_pkt_cnt[31:0];
+ REG_SNK_PKT_CNT_HI:
+ ctrlport_resp_data <= snk_pkt_cnt[63:32];
+ REG_SRC_LINE_CNT_LO:
+ ctrlport_resp_data <= src_line_cnt[31:0];
+ REG_SRC_LINE_CNT_HI:
+ ctrlport_resp_data <= src_line_cnt[63:32];
+ REG_SRC_PKT_CNT_LO:
+ ctrlport_resp_data <= src_pkt_cnt[31:0];
+ REG_SRC_PKT_CNT_HI:
+ ctrlport_resp_data <= src_pkt_cnt[63:32];
+ REG_LOOP_LINE_CNT_LO:
+ ctrlport_resp_data <= loop_line_cnt[31:0];
+ REG_LOOP_LINE_CNT_HI:
+ ctrlport_resp_data <= loop_line_cnt[63:32];
+ REG_LOOP_PKT_CNT_LO:
+ ctrlport_resp_data <= loop_pkt_cnt[31:0];
+ REG_LOOP_PKT_CNT_HI:
+ ctrlport_resp_data <= loop_pkt_cnt[63:32];
+ default:
+ ctrlport_resp_data <= 32'h0;
+ endcase
+ end
+ end
+ end
+
+endmodule // rfnoc_block_null_src_sink
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink_tb.sv
new file mode 100644
index 000000000..f25e762b3
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink_tb.sv
@@ -0,0 +1,268 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_null_src_sink_tb
+//
+
+`default_nettype none
+
+
+module rfnoc_block_null_src_sink_tb;
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+ import PkgRfnocItemUtils::*;
+
+ // Parameters
+ localparam [9:0] THIS_PORTID = 10'h17;
+ localparam [15:0] THIS_EPID = 16'hDEAD;
+ localparam int CHDR_W = 64;
+ localparam int SPP = 201;
+ localparam int LPP = ((SPP+1)/2);
+ localparam int NUM_PKTS = 50;
+
+ localparam int PORT_SRCSNK = 0;
+ localparam int PORT_LOOP = 1;
+
+ // Clock and Reset Definition
+ bit rfnoc_chdr_clk;
+ sim_clock_gen #(2.5) rfnoc_chdr_clk_gen (rfnoc_chdr_clk); // 400 MHz
+
+ // ----------------------------------------
+ // Instantiate DUT
+ // ----------------------------------------
+
+ // Connections to DUT as interfaces:
+ RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_chdr_clk); // Required backend iface
+ AxiStreamIf #(32) m_ctrl (rfnoc_chdr_clk); // Required control iface
+ AxiStreamIf #(32) s_ctrl (rfnoc_chdr_clk); // Required control iface
+ AxiStreamIf #(CHDR_W) m0_chdr (rfnoc_chdr_clk); // Optional data iface
+ AxiStreamIf #(CHDR_W) m1_chdr (rfnoc_chdr_clk); // Optional data iface
+ AxiStreamIf #(CHDR_W) s0_chdr (rfnoc_chdr_clk); // Optional data iface
+ AxiStreamIf #(CHDR_W) s1_chdr (rfnoc_chdr_clk); // Optional data iface
+
+ // Bus functional model for a software block controller
+ RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl;
+
+ // DUT
+ rfnoc_block_null_src_sink #(
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .NIPC (2),
+ .MTU (10)
+ ) dut (
+ .rfnoc_chdr_clk (backend.chdr_clk),
+ .rfnoc_ctrl_clk (backend.ctrl_clk),
+ .rfnoc_core_config (backend.slave.cfg),
+ .rfnoc_core_status (backend.slave.sts),
+ .s_rfnoc_chdr_tdata ({m1_chdr.slave.tdata , m0_chdr.slave.tdata }),
+ .s_rfnoc_chdr_tlast ({m1_chdr.slave.tlast , m0_chdr.slave.tlast }),
+ .s_rfnoc_chdr_tvalid({m1_chdr.slave.tvalid , m0_chdr.slave.tvalid }),
+ .s_rfnoc_chdr_tready({m1_chdr.slave.tready , m0_chdr.slave.tready }),
+ .m_rfnoc_chdr_tdata ({s1_chdr.master.tdata , s0_chdr.master.tdata }),
+ .m_rfnoc_chdr_tlast ({s1_chdr.master.tlast , s0_chdr.master.tlast }),
+ .m_rfnoc_chdr_tvalid({s1_chdr.master.tvalid, s0_chdr.master.tvalid}),
+ .m_rfnoc_chdr_tready({s1_chdr.master.tready, s0_chdr.master.tready}),
+ .s_rfnoc_ctrl_tdata (m_ctrl.slave.tdata ),
+ .s_rfnoc_ctrl_tlast (m_ctrl.slave.tlast ),
+ .s_rfnoc_ctrl_tvalid(m_ctrl.slave.tvalid ),
+ .s_rfnoc_ctrl_tready(m_ctrl.slave.tready ),
+ .m_rfnoc_ctrl_tdata (s_ctrl.master.tdata ),
+ .m_rfnoc_ctrl_tlast (s_ctrl.master.tlast ),
+ .m_rfnoc_ctrl_tvalid(s_ctrl.master.tvalid),
+ .m_rfnoc_ctrl_tready(s_ctrl.master.tready)
+ );
+
+ // ----------------------------------------
+ // Test Process
+ // ----------------------------------------
+
+ initial begin
+ // Shared Variables
+ // ----------------------------------------
+ timeout_t timeout;
+ ctrl_word_t rvalue = 0;
+
+ // Initialize
+ // ----------------------------------------
+ test.start_tb("rfnoc_block_null_src_sink_tb");
+
+ // Start the stream endpoint BFM
+ blk_ctrl = new(backend, m_ctrl, s_ctrl);
+ blk_ctrl.add_master_data_port(m0_chdr);
+ blk_ctrl.add_slave_data_port(s0_chdr);
+ blk_ctrl.add_master_data_port(m1_chdr);
+ blk_ctrl.add_slave_data_port(s1_chdr);
+ blk_ctrl.run();
+
+ // Startup block (Software initialization)
+ // ----------------------------------------
+ test.start_test("Flush block then reset it");
+ begin
+ test.start_timeout(timeout, 10us, "Waiting for flush_and_reset");
+ #100; //Wait for GSR to deassert
+ blk_ctrl.flush_and_reset();
+ test.end_timeout(timeout);
+ end
+ test.end_test();
+
+ // Run Tests
+ // ----------------------------------------
+ test.start_test("Read Block Info");
+ begin
+ test.start_timeout(timeout, 1us, "Waiting for block info response");
+ // Get static block info and validate it
+ `ASSERT_ERROR(blk_ctrl.get_noc_id() == 1, "Incorrect noc_id Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_i() == 2, "Incorrect num_data_i Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_o() == 2, "Incorrect num_data_o Value");
+ `ASSERT_ERROR(blk_ctrl.get_ctrl_fifosize() == 5, "Incorrect ctrl_fifosize Value");
+ `ASSERT_ERROR(blk_ctrl.get_mtu() == 10, "Incorrect mtu Value");
+
+ // Read status register and validate it
+ blk_ctrl.reg_read(dut.REG_CTRL_STATUS, rvalue);
+ `ASSERT_ERROR(rvalue[31:24] == 2, "Incorrect NIPC Value");
+ `ASSERT_ERROR(rvalue[23:16] == 32, "Incorrect ITEM_W Value");
+ test.end_timeout(timeout);
+ end
+ test.end_test();
+
+ test.start_test("Stream Data Through Loopback Port");
+ begin
+ // Send and receive packets
+ repeat (NUM_PKTS) begin
+ chdr_word_t rx_data[$];
+ int rx_bytes;
+ automatic ItemDataBuff #(logic[31:0]) tx_dbuff = new, rx_dbuff = new;
+ for (int i = 0; i < SPP; i++)
+ tx_dbuff.put($urandom());
+ test.start_timeout(timeout, 5us, "Waiting for pkt to loop back");
+ blk_ctrl.send(PORT_LOOP, tx_dbuff.to_chdr_payload(), tx_dbuff.get_bytes());
+ blk_ctrl.recv(PORT_LOOP, rx_data, rx_bytes);
+ rx_dbuff.from_chdr_payload(rx_data, rx_bytes);
+ `ASSERT_ERROR(rx_dbuff.equal(tx_dbuff), "Data mismatch");
+ test.end_timeout(timeout);
+ end
+
+ // Read item and packet counts on loopback port
+ blk_ctrl.reg_read(dut.REG_LOOP_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == (LPP*NUM_PKTS), "Incorrect REG_LOOP_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_LOOP_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == NUM_PKTS, "Incorrect REG_LOOP_PKT_CNT_LO value");
+
+ // Read item and packet counts on source port
+ blk_ctrl.reg_read(dut.REG_SRC_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_PKT_CNT_LO value");
+
+ // Read item and packet counts on sink port
+ blk_ctrl.reg_read(dut.REG_SNK_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SNK_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_SNK_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SNK_PKT_CNT_LO value");
+ end
+ test.end_test();
+
+ test.start_test("Stream Data To Sink Port");
+ begin
+ // Send packets
+ repeat (NUM_PKTS) begin
+ chdr_word_t rx_data[$];
+ int rx_bytes;
+ automatic ItemDataBuff #(logic[31:0]) tx_dbuff = new;
+ for (int i = 0; i < SPP; i++)
+ tx_dbuff.put($urandom());
+ test.start_timeout(timeout, 5us, "Waiting for pkt to loop back");
+ blk_ctrl.send(PORT_SRCSNK, tx_dbuff.to_chdr_payload(), tx_dbuff.get_bytes());
+ test.end_timeout(timeout);
+ end
+ repeat (NUM_PKTS * SPP * 2) @(posedge rfnoc_chdr_clk);
+
+ // Read item and packet counts on loopback port
+ blk_ctrl.reg_read(dut.REG_LOOP_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == (LPP*NUM_PKTS), "Incorrect REG_LOOP_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_LOOP_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == NUM_PKTS, "Incorrect REG_LOOP_PKT_CNT_LO value");
+
+ // Read item and packet counts on source port
+ blk_ctrl.reg_read(dut.REG_SRC_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_PKT_CNT_LO value");
+
+ // Read item and packet counts on sink port
+ blk_ctrl.reg_read(dut.REG_SNK_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == (LPP*NUM_PKTS), "Incorrect REG_SNK_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_SNK_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == NUM_PKTS, "Incorrect REG_SNK_PKT_CNT_LO value");
+ end
+ test.end_test();
+
+ test.start_test("Stream Data From Source Port");
+ begin
+ // Turn on the source for some time then stop it
+ blk_ctrl.reg_write(dut.REG_SRC_LINES_PER_PKT, LPP-1);
+ blk_ctrl.reg_write(dut.REG_SRC_BYTES_PER_PKT, (LPP+1)*8);
+ blk_ctrl.reg_write(dut.REG_CTRL_STATUS, 2'b10);
+ repeat ((NUM_PKTS / 10) * LPP) @(posedge rfnoc_chdr_clk);
+ blk_ctrl.reg_write(dut.REG_CTRL_STATUS, 2'b00);
+ blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
+ repeat (rvalue * LPP * 2) @(posedge rfnoc_chdr_clk);
+ blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
+
+ // Gather the accumulated packets and verify contents
+ for (int p = 0; p < rvalue; p++) begin
+ chdr_word_t exp_data[$];
+ chdr_word_t rx_data[$];
+ int rx_bytes;
+ test.start_timeout(timeout, 5us, "Waiting for pkt to arrive");
+ exp_data.delete();
+ for (int i = p*LPP; i < (p+1)*LPP; i++)
+ exp_data.push_back({~i[15:0], i[15:0], ~i[15:0], i[15:0]});
+ blk_ctrl.recv(PORT_SRCSNK, rx_data, rx_bytes);
+ `ASSERT_ERROR(blk_ctrl.compare_data(exp_data, rx_data), "Data mismatch");
+ test.end_timeout(timeout);
+ end
+ end
+ test.end_test();
+
+ test.start_test("Clear Counts");
+ begin
+ test.start_timeout(timeout, 1us, "Waiting for clear and readbacks");
+ // Clear
+ blk_ctrl.reg_write(dut.REG_CTRL_STATUS, 2'b01);
+
+ // Read item and packet counts on loopback port
+ blk_ctrl.reg_read(dut.REG_LOOP_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_LOOP_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_LOOP_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_LOOP_PKT_CNT_LO value");
+
+ // Read item and packet counts on source port
+ blk_ctrl.reg_read(dut.REG_SRC_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_PKT_CNT_LO value");
+
+ // Read item and packet counts on sink port
+ blk_ctrl.reg_read(dut.REG_SNK_LINE_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SNK_LINE_CNT_LO value");
+ blk_ctrl.reg_read(dut.REG_SNK_PKT_CNT_LO, rvalue);
+ `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SNK_PKT_CNT_LO value");
+ test.end_timeout(timeout);
+ end
+ test.end_test();
+
+ // Finish Up
+ // ----------------------------------------
+ // Display final statistics and results
+ test.end_tb();
+ end
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/Makefile
new file mode 100644
index 000000000..63d6f1851
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/Makefile
@@ -0,0 +1,47 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../../../top)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
+include Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_BLOCK_RADIO_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+SIM_TOP = rfnoc_block_radio_all_tb
+
+SIM_SRCS = \
+$(abspath sim_radio_gen.sv) \
+$(abspath rfnoc_block_radio_tb.sv) \
+$(abspath rfnoc_block_radio_all_tb.sv)
+
+# MODELSIM_USER_DO = $(abspath wave.do)
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+# Include all simulator specific makefiles here
+# Each should define a unique target to simulate
+# e.g. xsim, vsim, etc and a common "clean" target
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/Makefile.srcs
new file mode 100644
index 000000000..84dd01541
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/Makefile.srcs
@@ -0,0 +1,20 @@
+#
+# Copyright 2018 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+##################################################
+# RFNoC Utility Sources
+##################################################
+RFNOC_BLOCK_RADIO_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_radio/, \
+rfnoc_block_radio_regs.vh \
+radio_rx_core.v \
+radio_tx_core.v \
+radio_core.v \
+noc_shell_radio.v \
+rfnoc_block_radio.v \
+rx_frontend_gen3.v \
+tx_frontend_gen3.v \
+quarter_rate_downconverter.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/noc_shell_radio.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/noc_shell_radio.v
new file mode 100644
index 000000000..32ab32b63
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/noc_shell_radio.v
@@ -0,0 +1,290 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: noc_shell_radio
+//
+// Description: A NoC Shell for RFNoC. This should eventually be replaced
+// by an auto-generated NoC Shell.
+//
+
+module noc_shell_radio #(
+ parameter [31:0] NOC_ID = 32'h0,
+ parameter [ 9:0] THIS_PORTID = 10'd0,
+ parameter CHDR_W = 64,
+ parameter [ 0:0] CTRLPORT_SLV_EN = 1,
+ parameter [ 0:0] CTRLPORT_MST_EN = 1,
+ parameter [ 5:0] CTRL_FIFO_SIZE = 9,
+ parameter [ 5:0] NUM_DATA_I = 1,
+ parameter [ 5:0] NUM_DATA_O = 1,
+ parameter ITEM_W = 32,
+ parameter NIPC = 2,
+ parameter PYLD_FIFO_SIZE = 10,
+ parameter MTU = 10
+)(
+ //---------------------------------------------------------------------------
+ // Framework Interface
+ //---------------------------------------------------------------------------
+
+ // RFNoC Framework Clocks and Resets
+ input wire rfnoc_chdr_clk,
+ output wire rfnoc_chdr_rst,
+ input wire rfnoc_ctrl_clk,
+ output wire rfnoc_ctrl_rst,
+ // RFNoC Backend Interface
+ input wire [ 511:0] rfnoc_core_config,
+ output wire [ 511:0] rfnoc_core_status,
+ // CHDR Input Ports (from framework)
+ input wire [(CHDR_W*NUM_DATA_I)-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_DATA_I-1:0] s_rfnoc_chdr_tready,
+ // CHDR Output Ports (to framework)
+ output wire [(CHDR_W*NUM_DATA_O)-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_DATA_O-1:0] m_rfnoc_chdr_tready,
+ // AXIS-Ctrl Input Port (from framework)
+ input wire [ 31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+ // AXIS-Ctrl Output Port (to framework)
+ output wire [ 31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+
+ //---------------------------------------------------------------------------
+ // Client Control Port Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire ctrlport_clk,
+ input wire ctrlport_rst,
+ // Master
+ output wire m_ctrlport_req_wr,
+ output wire m_ctrlport_req_rd,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [31:0] m_ctrlport_req_data,
+ output wire [ 3:0] m_ctrlport_req_byte_en,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ input wire m_ctrlport_resp_ack,
+ input wire [ 1:0] m_ctrlport_resp_status,
+ input wire [31:0] m_ctrlport_resp_data,
+ // Slave
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [ 9:0] s_ctrlport_req_portid,
+ input wire [15:0] s_ctrlport_req_rem_epid,
+ input wire [ 9:0] s_ctrlport_req_rem_portid,
+ input wire [31:0] s_ctrlport_req_data,
+ input wire [ 3:0] s_ctrlport_req_byte_en,
+ input wire s_ctrlport_req_has_time,
+ input wire [63:0] s_ctrlport_req_time,
+ output wire s_ctrlport_resp_ack,
+ output wire [ 1:0] s_ctrlport_resp_status,
+ output wire [31:0] s_ctrlport_resp_data,
+
+ //---------------------------------------------------------------------------
+ // Client Data Interface
+ //---------------------------------------------------------------------------
+
+ // Clock
+ input wire axis_data_clk,
+ input wire axis_data_rst,
+
+ // Output data stream (to user logic)
+ output wire [(NUM_DATA_I*ITEM_W*NIPC)-1:0] m_axis_tdata,
+ output wire [ (NUM_DATA_I*NIPC)-1:0] m_axis_tkeep,
+ output wire [ NUM_DATA_I-1:0] m_axis_tlast,
+ output wire [ NUM_DATA_I-1:0] m_axis_tvalid,
+ input wire [ NUM_DATA_I-1:0] m_axis_tready,
+ // Sideband information
+ output wire [ (NUM_DATA_I*64)-1:0] m_axis_ttimestamp,
+ output wire [ NUM_DATA_I-1:0] m_axis_thas_time,
+ output wire [ NUM_DATA_I-1:0] m_axis_teov,
+ output wire [ NUM_DATA_I-1:0] m_axis_teob,
+
+ // Input data stream (from user logic)
+ input wire [(NUM_DATA_O*ITEM_W*NIPC)-1:0] s_axis_tdata,
+ input wire [ (NUM_DATA_O*NIPC)-1:0] s_axis_tkeep,
+ input wire [ NUM_DATA_O-1:0] s_axis_tlast,
+ input wire [ NUM_DATA_O-1:0] s_axis_tvalid,
+ output wire [ NUM_DATA_O-1:0] s_axis_tready,
+ // Sideband info (sampled on the first cycle of the packet)
+ input wire [ (NUM_DATA_O*64)-1:0] s_axis_ttimestamp,
+ input wire [ NUM_DATA_O-1:0] s_axis_thas_time,
+ input wire [ NUM_DATA_O-1:0] s_axis_teov,
+ input wire [ NUM_DATA_O-1:0] s_axis_teob
+);
+
+ localparam SNK_INFO_FIFO_SIZE = 4;
+ localparam SNK_PYLD_FIFO_SIZE = PYLD_FIFO_SIZE;
+ localparam SRC_INFO_FIFO_SIZE = 4;
+ localparam SRC_PYLD_FIFO_SIZE = MTU;
+
+ //---------------------------------------------------------------------------
+ // Backend Interface
+ //---------------------------------------------------------------------------
+
+ wire data_i_flush_en;
+ wire [31:0] data_i_flush_timeout;
+ wire [63:0] data_i_flush_active;
+ wire [63:0] data_i_flush_done;
+ wire data_o_flush_en;
+ wire [31:0] data_o_flush_timeout;
+ wire [63:0] data_o_flush_active;
+ wire [63:0] data_o_flush_done;
+
+ backend_iface #(
+ .NOC_ID (NOC_ID),
+ .NUM_DATA_I (NUM_DATA_I),
+ .NUM_DATA_O (NUM_DATA_O),
+ .CTRL_FIFOSIZE (CTRL_FIFO_SIZE),
+ .MTU (MTU)
+ ) backend_iface_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst),
+ .data_i_flush_en (data_i_flush_en),
+ .data_i_flush_timeout (data_i_flush_timeout),
+ .data_i_flush_active (data_i_flush_active),
+ .data_i_flush_done (data_i_flush_done),
+ .data_o_flush_en (data_o_flush_en),
+ .data_o_flush_timeout (data_o_flush_timeout),
+ .data_o_flush_active (data_o_flush_active),
+ .data_o_flush_done (data_o_flush_done)
+ );
+
+ //---------------------------------------------------------------------------
+ // Control Path
+ //---------------------------------------------------------------------------
+
+ ctrlport_endpoint #(
+ .THIS_PORTID (THIS_PORTID ),
+ .SYNC_CLKS (0 ),
+ .AXIS_CTRL_MST_EN (CTRLPORT_SLV_EN),
+ .AXIS_CTRL_SLV_EN (CTRLPORT_MST_EN),
+ .SLAVE_FIFO_SIZE (CTRL_FIFO_SIZE )
+ ) ctrlport_ep_i (
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk ),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst ),
+ .ctrlport_clk (ctrlport_clk ),
+ .ctrlport_rst (ctrlport_rst ),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata ),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast ),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid ),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready ),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata ),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast ),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid ),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready ),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr ),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd ),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr ),
+ .m_ctrlport_req_data (m_ctrlport_req_data ),
+ .m_ctrlport_req_byte_en (m_ctrlport_req_byte_en ),
+ .m_ctrlport_req_has_time (m_ctrlport_req_has_time ),
+ .m_ctrlport_req_time (m_ctrlport_req_time ),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack ),
+ .m_ctrlport_resp_status (m_ctrlport_resp_status ),
+ .m_ctrlport_resp_data (m_ctrlport_resp_data ),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr ),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd ),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr ),
+ .s_ctrlport_req_portid (s_ctrlport_req_portid ),
+ .s_ctrlport_req_rem_epid (s_ctrlport_req_rem_epid ),
+ .s_ctrlport_req_rem_portid(s_ctrlport_req_rem_portid),
+ .s_ctrlport_req_data (s_ctrlport_req_data ),
+ .s_ctrlport_req_byte_en (s_ctrlport_req_byte_en ),
+ .s_ctrlport_req_has_time (s_ctrlport_req_has_time ),
+ .s_ctrlport_req_time (s_ctrlport_req_time ),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack ),
+ .s_ctrlport_resp_status (s_ctrlport_resp_status ),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data )
+ );
+
+ //---------------------------------------------------------------------------
+ // Data Path
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ generate
+
+ for (i = 0; i < NUM_DATA_I; i = i + 1) begin: chdr_to_data
+ chdr_to_axis_data #(
+ .CHDR_W (CHDR_W),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .SYNC_CLKS (0),
+ .INFO_FIFO_SIZE (SNK_INFO_FIFO_SIZE),
+ .PYLD_FIFO_SIZE (SNK_PYLD_FIFO_SIZE)
+ ) chdr_to_axis_data_i (
+ .axis_chdr_clk (rfnoc_chdr_clk),
+ .axis_chdr_rst (rfnoc_chdr_rst),
+ .axis_data_clk (axis_data_clk),
+ .axis_data_rst (axis_data_rst),
+ .s_axis_chdr_tdata (s_rfnoc_chdr_tdata [(i*CHDR_W)+:CHDR_W]),
+ .s_axis_chdr_tlast (s_rfnoc_chdr_tlast [i]),
+ .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid [i]),
+ .s_axis_chdr_tready (s_rfnoc_chdr_tready [i]),
+ .m_axis_tdata (m_axis_tdata [i*ITEM_W*NIPC +: ITEM_W*NIPC]),
+ .m_axis_tkeep (m_axis_tkeep [i*NIPC +: NIPC]),
+ .m_axis_tlast (m_axis_tlast [i]),
+ .m_axis_tvalid (m_axis_tvalid [i]),
+ .m_axis_tready (m_axis_tready [i]),
+ .m_axis_ttimestamp (m_axis_ttimestamp [i*64 +: 64]),
+ .m_axis_thas_time (m_axis_thas_time [i]),
+ .m_axis_tlength (),
+ .m_axis_teov (m_axis_teov [i]),
+ .m_axis_teob (m_axis_teob [i]),
+ .flush_en (data_i_flush_en),
+ .flush_timeout (data_i_flush_timeout),
+ .flush_active (data_i_flush_active [i]),
+ .flush_done (data_i_flush_done [i])
+ );
+ end
+
+ for (i = 0; i < NUM_DATA_O; i = i + 1) begin: data_to_chdr
+ axis_data_to_chdr #(
+ .CHDR_W (CHDR_W),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .SYNC_CLKS (0),
+ .INFO_FIFO_SIZE (4),
+ .PYLD_FIFO_SIZE (SRC_INFO_FIFO_SIZE),
+ .MTU (SRC_PYLD_FIFO_SIZE)
+ ) axis_data_to_chdr_i (
+ .axis_chdr_clk (rfnoc_chdr_clk),
+ .axis_chdr_rst (rfnoc_chdr_rst),
+ .axis_data_clk (axis_data_clk),
+ .axis_data_rst (axis_data_rst),
+ .m_axis_chdr_tdata (m_rfnoc_chdr_tdata [i*CHDR_W +: CHDR_W]),
+ .m_axis_chdr_tlast (m_rfnoc_chdr_tlast [i]),
+ .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid [i]),
+ .m_axis_chdr_tready (m_rfnoc_chdr_tready [i]),
+ .s_axis_tdata (s_axis_tdata [i*ITEM_W*NIPC +: ITEM_W*NIPC]),
+ .s_axis_tkeep (s_axis_tkeep [i*NIPC +: NIPC]),
+ .s_axis_tlast (s_axis_tlast [i]),
+ .s_axis_tvalid (s_axis_tvalid [i]),
+ .s_axis_tready (s_axis_tready [i]),
+ .s_axis_ttimestamp (s_axis_ttimestamp [i*64 +: 64]),
+ .s_axis_thas_time (s_axis_thas_time [i]),
+ .s_axis_teov (s_axis_teov [i]),
+ .s_axis_teob (s_axis_teob [i]),
+ .flush_en (data_o_flush_en),
+ .flush_timeout (data_o_flush_timeout),
+ .flush_active (data_o_flush_active [i]),
+ .flush_done (data_o_flush_done [i])
+ );
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/quarter_rate_downconverter.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/quarter_rate_downconverter.v
new file mode 100644
index 000000000..ded9a8c0b
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/quarter_rate_downconverter.v
@@ -0,0 +1,138 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+// mixer with 90 degree angles, i.e., multiplying the input signal with 1, i, -1, -i:
+
+// Let S(t) = I(t) + i*Q(t) be the input signal based on inputs i_in and q_in
+// Multiplying with (1,i,-1,-i) then becomes:
+// S(t) * 1 = I(t) + i*Q(t)
+// S(t) * i = -Q(t) + i*I(t)
+// S(t) * -1 = -I(t) - i*Q(t)
+// S(t) * -i = Q(t) - i*I(t)
+
+// To control the direction of rotation, the dirctn input is used
+// When set to 0, the phase is increased with pi/2 every sample, i.e., rotating counter clock wise
+// When set to 1, the phase is increased with -pi/2 every sample, i.e., rotating clock wise
+
+// the input is the concatenation of the i and q signal: {i_in, q_in}
+
+module quarter_rate_downconverter #(
+ parameter WIDTH=24
+)(
+ input clk,
+ input reset,
+ input phase_sync,
+
+ input [2*WIDTH-1:0] i_tdata,
+ input i_tlast,
+ input i_tvalid,
+ output i_tready,
+
+ output [2*WIDTH-1:0] o_tdata,
+ output o_tlast,
+ output o_tvalid,
+ input o_tready,
+
+ input dirctn
+);
+
+ // temporary signals for i and q after rotation
+ reg [WIDTH-1:0] tmp_i = {WIDTH{1'b0}};
+ reg [WIDTH-1:0] tmp_q = {WIDTH{1'b0}};
+
+ // State machine types and reg
+ localparam S0=0, S1=1, S2=2, S3=3;
+ reg[1:0] cur_state;
+
+ // split input into i and q signal
+ wire[WIDTH-1:0] i_in, q_in;
+ assign i_in = i_tdata[2*WIDTH-1:WIDTH];
+ assign q_in = i_tdata[WIDTH-1:0];
+
+ // The state machine doing the rotations among states
+ always @(posedge clk) begin
+ if(reset || phase_sync) begin
+ cur_state <= S0;
+ end else begin
+ case (cur_state)
+ S0: begin
+ if(i_tvalid == 1'b1 && i_tready == 1'b1)
+ if(dirctn == 1'b0)
+ cur_state <= S1;
+ else
+ cur_state <= S3;
+ else
+ cur_state <= S0;
+ end
+ S1: begin
+ if(i_tvalid == 1'b1 && i_tready == 1'b1)
+ if(dirctn == 1'b0)
+ cur_state <= S2;
+ else
+ cur_state <= S0;
+ else
+ cur_state <= S1;
+ end
+ S2: begin
+ if(i_tvalid == 1'b1 && i_tready == 1'b1)
+ if(dirctn == 1'b0)
+ cur_state <= S3;
+ else
+ cur_state <= S1;
+ else
+ cur_state <= S2;
+ end
+ S3: begin
+ if(i_tvalid == 1'b1 && i_tready == 1'b1)
+ if(dirctn == 1'b0)
+ cur_state <= S0;
+ else
+ cur_state <= S2;
+ else
+ cur_state <= S3;
+ end
+ endcase
+ end
+ end
+
+ // Multiplication of input IQ signal with (1,i,-1,-i):
+ always @(*) begin
+ case (cur_state)
+ S0: begin
+ // S(t) * 1 = I(t) + iQ(t):
+ tmp_i = i_in;
+ tmp_q = q_in;
+ end
+ S1: begin
+ // S(t) * i = -Q(t) + iI(t):
+ tmp_i = -q_in;
+ tmp_q = i_in;
+ end
+ S2: begin
+ // S(t) * -1 = -I(t) - iQ(t):
+ tmp_i = -i_in;
+ tmp_q = -q_in;
+ end
+ S3: begin
+ // S(t) * -i = Q(t) - iI(t):
+ tmp_i = q_in;
+ tmp_q = -i_in;
+ end
+ default: begin
+ tmp_i = i_in;
+ tmp_q = q_in;
+ end
+ endcase
+ end
+
+ // Flop for valid and ready signals and shortening of comb. paths.
+ axi_fifo #(.WIDTH(2*WIDTH + 1), .SIZE(1)) flop (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata({i_tlast, tmp_i, tmp_q}), .i_tvalid(i_tvalid), .i_tready(i_tready),
+ .o_tdata({o_tlast, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
+ .occupied(), .space());
+
+endmodule // quarter_rate_downconverter
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_core.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_core.v
new file mode 100644
index 000000000..9456fc398
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_core.v
@@ -0,0 +1,370 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: radio_core
+//
+// Description:
+//
+// A radio core for RFNoC. This core contains all logic in the radio clock
+// domain for interfacing to a single RX/TX radio. It includes registers shared
+// by both Rx and Tx logic and instantiates Rx and Tx interface cores.
+//
+// Parameters:
+//
+// BASE_ADDR : Base address for this radio block instance
+// SAMP_W : Width of a radio sample
+// NSPC : Number of radio samples per radio clock cycle
+//
+
+
+module radio_core #(
+ parameter SAMP_W = 32,
+ parameter NSPC = 1
+) (
+ input wire radio_clk,
+ input wire radio_rst,
+
+
+ //---------------------------------------------------------------------------
+ // Control Interface
+ //---------------------------------------------------------------------------
+
+ // Slave
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ output wire s_ctrlport_resp_ack,
+ output wire [31:0] s_ctrlport_resp_data,
+
+ // Master
+ output wire m_ctrlport_req_wr,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [ 9:0] m_ctrlport_req_portid,
+ output wire [15:0] m_ctrlport_req_rem_epid,
+ output wire [ 9:0] m_ctrlport_req_rem_portid,
+ output wire [31:0] m_ctrlport_req_data,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ input wire m_ctrlport_resp_ack,
+
+
+ //---------------------------------------------------------------------------
+ // Data Interface
+ //---------------------------------------------------------------------------
+
+ // Tx Radio Data Stream
+ input wire [(SAMP_W*NSPC)-1:0] s_axis_tdata,
+ input wire s_axis_tlast,
+ input wire s_axis_tvalid,
+ output wire s_axis_tready,
+ // Sideband info
+ input wire [ 63:0] s_axis_ttimestamp,
+ input wire s_axis_thas_time,
+ input wire s_axis_teob,
+
+ // Rx Radio Data Stream
+ output wire [(SAMP_W*NSPC)-1:0] m_axis_tdata,
+ output wire m_axis_tlast,
+ output wire m_axis_tvalid,
+ input wire m_axis_tready,
+ // Sideband info
+ output wire [ 63:0] m_axis_ttimestamp,
+ output wire m_axis_thas_time,
+ output wire m_axis_teob,
+
+
+ //---------------------------------------------------------------------------
+ // Radio Interface
+ //---------------------------------------------------------------------------
+
+ input wire [63:0] radio_time,
+
+ // Radio Rx Interface
+ input wire [SAMP_W*NSPC-1:0] radio_rx_data,
+ input wire radio_rx_stb,
+ output wire radio_rx_running,
+
+ // Radio Tx Interface
+ output wire [SAMP_W*NSPC-1:0] radio_tx_data,
+ input wire radio_tx_stb,
+ output wire radio_tx_running
+);
+
+ `include "rfnoc_block_radio_regs.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Split Control Port Interface
+ //---------------------------------------------------------------------------
+ //
+ // This block splits the single slave interface of the radio core into
+ // multiple interfaces, one for each subcomponent. The responses from each
+ // subcomponent are merged into a single response and sent back out the slave
+ // interface.
+ //
+ //---------------------------------------------------------------------------
+
+ // Registers shared by Rx and Tx
+ wire ctrlport_general_req_wr;
+ wire ctrlport_general_req_rd;
+ wire [19:0] ctrlport_general_req_addr;
+ wire [31:0] ctrlport_general_req_data;
+ reg ctrlport_general_resp_ack = 1'b0;
+ reg [31:0] ctrlport_general_resp_data = 0;
+
+ // Tx core registers
+ wire ctrlport_tx_req_wr;
+ wire ctrlport_tx_req_rd;
+ wire [19:0] ctrlport_tx_req_addr;
+ wire [31:0] ctrlport_tx_req_data;
+ wire ctrlport_tx_resp_ack;
+ wire [31:0] ctrlport_tx_resp_data;
+
+ // Rx core registers
+ wire ctrlport_rx_req_wr;
+ wire ctrlport_rx_req_rd;
+ wire [19:0] ctrlport_rx_req_addr;
+ wire [31:0] ctrlport_rx_req_data;
+ wire ctrlport_rx_resp_ack;
+ wire [31:0] ctrlport_rx_resp_data;
+
+ ctrlport_splitter #(
+ .NUM_SLAVES (3)
+ ) ctrlport_decoder_i (
+ .ctrlport_clk (radio_clk),
+ .ctrlport_rst (radio_rst),
+ .s_ctrlport_req_wr (s_ctrlport_req_wr),
+ .s_ctrlport_req_rd (s_ctrlport_req_rd),
+ .s_ctrlport_req_addr (s_ctrlport_req_addr),
+ .s_ctrlport_req_data (s_ctrlport_req_data),
+ .s_ctrlport_req_byte_en (4'b0),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (s_ctrlport_resp_ack),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (s_ctrlport_resp_data),
+ .m_ctrlport_req_wr ({ctrlport_general_req_wr,
+ ctrlport_tx_req_wr,
+ ctrlport_rx_req_wr}),
+ .m_ctrlport_req_rd ({ctrlport_general_req_rd,
+ ctrlport_tx_req_rd,
+ ctrlport_rx_req_rd}),
+ .m_ctrlport_req_addr ({ctrlport_general_req_addr,
+ ctrlport_tx_req_addr,
+ ctrlport_rx_req_addr}),
+ .m_ctrlport_req_data ({ctrlport_general_req_data,
+ ctrlport_tx_req_data,
+ ctrlport_rx_req_data}),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (),
+ .m_ctrlport_req_time (),
+ .m_ctrlport_resp_ack ({ctrlport_general_resp_ack,
+ ctrlport_tx_resp_ack,
+ ctrlport_rx_resp_ack}),
+ .m_ctrlport_resp_status (6'b0),
+ .m_ctrlport_resp_data ({ctrlport_general_resp_data,
+ ctrlport_tx_resp_data,
+ ctrlport_rx_resp_data})
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Merge Control Port Interfaces
+ //---------------------------------------------------------------------------
+ //
+ // This block merges the master control port interfaces of the Rx and Tx
+ // cores into a single master control port interface. Both the Rx and Tx
+ // cores support error reporting by writing to a control port interface. This
+ // block arbitrates the requests between the Rx and Tx cores. Rx and Tx only
+ // support writes for error reporting, not reads. Time and byte enables are
+ // also not needed. Hence, several ports are unconnected.
+ //
+ //---------------------------------------------------------------------------
+
+ // Tx and Rx error reporting signals
+ wire ctrlport_err_tx_req_wr, ctrlport_err_rx_req_wr;
+ wire [19:0] ctrlport_err_tx_req_addr, ctrlport_err_rx_req_addr;
+ wire [31:0] ctrlport_err_tx_req_data, ctrlport_err_rx_req_data;
+ wire ctrlport_err_tx_req_has_time, ctrlport_err_rx_req_has_time;
+ wire [63:0] ctrlport_err_tx_req_time, ctrlport_err_rx_req_time;
+ wire [ 9:0] ctrlport_err_tx_req_portid, ctrlport_err_rx_req_portid;
+ wire [15:0] ctrlport_err_tx_req_rem_epid, ctrlport_err_rx_req_rem_epid;
+ wire [ 9:0] ctrlport_err_tx_req_rem_portid, ctrlport_err_rx_req_rem_portid;
+ wire ctrlport_err_tx_resp_ack, ctrlport_err_rx_resp_ack;
+
+
+ ctrlport_combiner #(
+ .NUM_MASTERS (2),
+ .PRIORITY (0)
+ ) ctrlport_req_combine_i (
+ .ctrlport_clk (radio_clk),
+ .ctrlport_rst (radio_rst),
+ .s_ctrlport_req_wr ({ctrlport_err_tx_req_wr, ctrlport_err_rx_req_wr}),
+ .s_ctrlport_req_rd (2'b0),
+ .s_ctrlport_req_addr ({ctrlport_err_tx_req_addr, ctrlport_err_rx_req_addr}),
+ .s_ctrlport_req_portid ({ctrlport_err_tx_req_portid, ctrlport_err_rx_req_portid}),
+ .s_ctrlport_req_rem_epid ({ctrlport_err_tx_req_rem_epid, ctrlport_err_rx_req_rem_epid}),
+ .s_ctrlport_req_rem_portid ({ctrlport_err_tx_req_rem_portid, ctrlport_err_rx_req_rem_portid}),
+ .s_ctrlport_req_data ({ctrlport_err_tx_req_data, ctrlport_err_rx_req_data}),
+ .s_ctrlport_req_byte_en (8'hFF),
+ .s_ctrlport_req_has_time ({ctrlport_err_tx_req_has_time, ctrlport_err_rx_req_has_time}),
+ .s_ctrlport_req_time ({ctrlport_err_tx_req_time, ctrlport_err_rx_req_time}),
+ .s_ctrlport_resp_ack ({ctrlport_err_tx_resp_ack, ctrlport_err_rx_resp_ack}),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr),
+ .m_ctrlport_req_rd (),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr),
+ .m_ctrlport_req_portid (m_ctrlport_req_portid),
+ .m_ctrlport_req_rem_epid (m_ctrlport_req_rem_epid),
+ .m_ctrlport_req_rem_portid (m_ctrlport_req_rem_portid),
+ .m_ctrlport_req_data (m_ctrlport_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (m_ctrlport_req_has_time),
+ .m_ctrlport_req_time (m_ctrlport_req_time),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack),
+ .m_ctrlport_resp_status (2'b0),
+ .m_ctrlport_resp_data (0)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // General Registers
+ //---------------------------------------------------------------------------
+ //
+ // These are registers that apply to both Rx and Tx and are shared by both.
+ //
+ //---------------------------------------------------------------------------
+
+ reg reg_loopback_en = 1'b0;
+
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ ctrlport_general_resp_ack <= 0;
+ ctrlport_general_resp_data <= 0;
+ reg_loopback_en <= 0;
+ end else begin
+ // Default assignments
+ ctrlport_general_resp_ack <= 0;
+ ctrlport_general_resp_data <= 0;
+
+ // Handle register writes
+ if (ctrlport_general_req_wr) begin
+ case (ctrlport_general_req_addr)
+ REG_LOOPBACK_EN: begin
+ reg_loopback_en <= ctrlport_general_req_data[0];
+ ctrlport_general_resp_ack <= 1;
+ end
+ endcase
+ end
+
+ // Handle register reads
+ if (ctrlport_general_req_rd) begin
+ case (ctrlport_general_req_addr)
+ REG_LOOPBACK_EN: begin
+ ctrlport_general_resp_data <= 0;
+ ctrlport_general_resp_data[0] <= reg_loopback_en;
+ ctrlport_general_resp_ack <= 1;
+ end
+ REG_RADIO_WIDTH: begin
+ ctrlport_general_resp_data <= { SAMP_W[15:0], NSPC[15:0] };
+ ctrlport_general_resp_ack <= 1;
+ end
+ endcase
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Tx to Rx Loopback
+ //---------------------------------------------------------------------------
+
+ wire [SAMP_W*NSPC-1:0] radio_rx_data_mux;
+ wire radio_rx_stb_mux;
+
+ assign radio_rx_data_mux = reg_loopback_en ? radio_tx_data : radio_rx_data;
+ assign radio_rx_stb_mux = reg_loopback_en ? radio_tx_stb : radio_rx_stb;
+
+
+ //---------------------------------------------------------------------------
+ // Tx Core
+ //---------------------------------------------------------------------------
+
+ radio_tx_core #(
+ .SAMP_W (SAMP_W),
+ .NSPC (NSPC)
+ ) radio_tx_core_i (
+ .radio_clk (radio_clk),
+ .radio_rst (radio_rst),
+ .s_ctrlport_req_wr (ctrlport_tx_req_wr),
+ .s_ctrlport_req_rd (ctrlport_tx_req_rd),
+ .s_ctrlport_req_addr (ctrlport_tx_req_addr),
+ .s_ctrlport_req_data (ctrlport_tx_req_data),
+ .s_ctrlport_resp_ack (ctrlport_tx_resp_ack),
+ .s_ctrlport_resp_data (ctrlport_tx_resp_data),
+ .m_ctrlport_req_wr (ctrlport_err_tx_req_wr),
+ .m_ctrlport_req_addr (ctrlport_err_tx_req_addr),
+ .m_ctrlport_req_data (ctrlport_err_tx_req_data),
+ .m_ctrlport_req_has_time (ctrlport_err_tx_req_has_time),
+ .m_ctrlport_req_time (ctrlport_err_tx_req_time),
+ .m_ctrlport_req_portid (ctrlport_err_tx_req_portid),
+ .m_ctrlport_req_rem_epid (ctrlport_err_tx_req_rem_epid),
+ .m_ctrlport_req_rem_portid (ctrlport_err_tx_req_rem_portid),
+ .m_ctrlport_resp_ack (ctrlport_err_tx_resp_ack),
+ .radio_time (radio_time),
+ .radio_tx_data (radio_tx_data),
+ .radio_tx_stb (radio_tx_stb),
+ .radio_tx_running (radio_tx_running),
+ .s_axis_tdata (s_axis_tdata),
+ .s_axis_tlast (s_axis_tlast),
+ .s_axis_tvalid (s_axis_tvalid),
+ .s_axis_tready (s_axis_tready),
+ .s_axis_ttimestamp (s_axis_ttimestamp),
+ .s_axis_thas_time (s_axis_thas_time),
+ .s_axis_teob (s_axis_teob)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Rx Core
+ //---------------------------------------------------------------------------
+
+ radio_rx_core #(
+ .SAMP_W (SAMP_W),
+ .NSPC (NSPC)
+ ) radio_rx_core_i (
+ .radio_clk (radio_clk),
+ .radio_rst (radio_rst),
+ .s_ctrlport_req_wr (ctrlport_rx_req_wr),
+ .s_ctrlport_req_rd (ctrlport_rx_req_rd),
+ .s_ctrlport_req_addr (ctrlport_rx_req_addr),
+ .s_ctrlport_req_data (ctrlport_rx_req_data),
+ .s_ctrlport_resp_ack (ctrlport_rx_resp_ack),
+ .s_ctrlport_resp_data (ctrlport_rx_resp_data),
+ .m_ctrlport_req_wr (ctrlport_err_rx_req_wr),
+ .m_ctrlport_req_addr (ctrlport_err_rx_req_addr),
+ .m_ctrlport_req_data (ctrlport_err_rx_req_data),
+ .m_ctrlport_req_has_time (ctrlport_err_rx_req_has_time),
+ .m_ctrlport_req_time (ctrlport_err_rx_req_time),
+ .m_ctrlport_req_portid (ctrlport_err_rx_req_portid),
+ .m_ctrlport_req_rem_epid (ctrlport_err_rx_req_rem_epid),
+ .m_ctrlport_req_rem_portid (ctrlport_err_rx_req_rem_portid),
+ .m_ctrlport_resp_ack (ctrlport_err_rx_resp_ack),
+ .radio_time (radio_time),
+ .radio_rx_data (radio_rx_data_mux),
+ .radio_rx_stb (radio_rx_stb_mux),
+ .radio_rx_running (radio_rx_running),
+ .m_axis_tdata (m_axis_tdata),
+ .m_axis_tlast (m_axis_tlast),
+ .m_axis_tvalid (m_axis_tvalid),
+ .m_axis_tready (m_axis_tready),
+ .m_axis_ttimestamp (m_axis_ttimestamp),
+ .m_axis_thas_time (m_axis_thas_time),
+ .m_axis_teob (m_axis_teob)
+ );
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_rx_core.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_rx_core.v
new file mode 100644
index 000000000..ee7774fd7
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_rx_core.v
@@ -0,0 +1,521 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: radio_rx_core
+//
+// Description:
+//
+// This module contains the core Rx radio acquisition logic. It retrieves
+// sample data from the radio interface, as indicated by the radio's strobe
+// signal, and outputs the data via AXI-Stream.
+//
+// The receiver is operated by writing a time (optionally) to the
+// REG_RX_CMD_TIME_* registers and a number of words (optionally) to
+// REG_RX_CMD_NUM_WORDS_* registers followed by writing a command word to
+// REG_RX_CMD. The command word indicates whether it is a finite ("num samps
+// and done") or continuous acquisition and whether or not the acquisition
+// should start at the time indicated byREG_RX_CMD_TIME_*. A stop command will
+// stop any acquisition that's waiting to start or is in progress.
+//
+// The REG_RX_MAX_WORDS_PER_PKT and REG_RX_ERR_* registers should be
+// initialized prior to the first acquisition.
+//
+// Parameters:
+//
+// SAMP_W : Width of a radio sample
+// NSPC : Number of radio samples per radio clock cycle
+//
+`default_nettype none
+
+
+module radio_rx_core #(
+ parameter SAMP_W = 32,
+ parameter NSPC = 1
+) (
+ input wire radio_clk,
+ input wire radio_rst,
+
+
+ //---------------------------------------------------------------------------
+ // Control Interface
+ //---------------------------------------------------------------------------
+
+ // Slave (Register Reads and Writes)
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ output reg s_ctrlport_resp_ack = 1'b0,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ // Master (Error Reporting)
+ output reg m_ctrlport_req_wr = 1'b0,
+ output reg [19:0] m_ctrlport_req_addr,
+ output reg [31:0] m_ctrlport_req_data,
+ output wire m_ctrlport_req_has_time,
+ output reg [63:0] m_ctrlport_req_time,
+ output wire [ 9:0] m_ctrlport_req_portid,
+ output wire [15:0] m_ctrlport_req_rem_epid,
+ output wire [ 9:0] m_ctrlport_req_rem_portid,
+ input wire m_ctrlport_resp_ack,
+
+
+ //---------------------------------------------------------------------------
+ // Radio Interface
+ //---------------------------------------------------------------------------
+
+ input wire [63:0] radio_time,
+
+ input wire [SAMP_W*NSPC-1:0] radio_rx_data,
+ input wire radio_rx_stb,
+
+ // Status indicator (true when receiving)
+ output wire radio_rx_running,
+
+
+ //---------------------------------------------------------------------------
+ // AXI-Stream Data Output
+ //---------------------------------------------------------------------------
+
+ output wire [SAMP_W*NSPC-1:0] m_axis_tdata,
+ output wire m_axis_tlast,
+ output wire m_axis_tvalid,
+ input wire m_axis_tready,
+ // Sideband info
+ output wire [ 63:0] m_axis_ttimestamp,
+ output wire m_axis_thas_time,
+ output wire m_axis_teob
+);
+
+ `include "rfnoc_block_radio_regs.vh"
+ `include "../../core/rfnoc_chdr_utils.vh"
+
+ localparam NUM_WORDS_LEN = RX_CMD_NUM_WORDS_LEN;
+
+
+ //---------------------------------------------------------------------------
+ // Register Read/Write Logic
+ //---------------------------------------------------------------------------
+
+ reg reg_cmd_valid = 0; // Indicates when the CMD_FIFO has been written
+ reg [ RX_CMD_LEN-1:0] reg_cmd_word = 0; // Command to execute
+ reg [NUM_WORDS_LEN-1:0] reg_cmd_num_words = 0; // Number of words for the command
+ reg [ 63:0] reg_cmd_time = 0; // Time for the command
+ reg reg_cmd_timed = 0; // Indicates if this is a timed command
+ reg [ 31:0] reg_max_pkt_len = 64; // Maximum words per packet
+ reg [ 9:0] reg_error_portid = 0; // Port ID to use for error reporting
+ reg [ 15:0] reg_error_rem_epid = 0; // Remote EPID to use for error reporting
+ reg [ 9:0] reg_error_rem_portid = 0; // Remote port ID to use for error reporting
+ reg [ 19:0] reg_error_addr = 0; // Address to use for error reporting
+ reg reg_has_time = 1; // Whether or not to use timestamps on data
+
+ wire [15:0] cmd_fifo_space; // Empty space in the command FIFO
+ reg cmd_stop = 0; // Indicates a full stop request
+ wire cmd_stop_ack; // Acknowledgment that a stop has completed
+ reg clear_fifo = 0; // Signal to clear the command FIFO
+
+ assign m_axis_thas_time = reg_has_time;
+
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ s_ctrlport_resp_ack <= 0;
+ reg_cmd_valid <= 0;
+ reg_cmd_word <= 0;
+ reg_cmd_num_words <= 0;
+ reg_cmd_time <= 0;
+ reg_cmd_timed <= 0;
+ reg_max_pkt_len <= 64;
+ reg_error_portid <= 0;
+ reg_error_rem_epid <= 0;
+ reg_error_rem_portid <= 0;
+ reg_error_addr <= 0;
+ reg_has_time <= 1;
+ clear_fifo <= 0;
+ cmd_stop <= 0;
+ end else begin
+ // Default assignments
+ s_ctrlport_resp_ack <= 0;
+ s_ctrlport_resp_data <= 0;
+ reg_cmd_valid <= 0;
+ clear_fifo <= 0;
+
+ // Clear stop register when we enter the STOP state
+ if (cmd_stop_ack) cmd_stop <= 1'b0;
+
+ // Handle register writes
+ if (s_ctrlport_req_wr) begin
+ case (s_ctrlport_req_addr)
+ REG_RX_CMD: begin
+ // All commands go into the command FIFO except STOP
+ reg_cmd_valid <= (s_ctrlport_req_data[RX_CMD_LEN-1:0] != RX_CMD_STOP);
+ reg_cmd_word <= s_ctrlport_req_data[RX_CMD_LEN-1:0];
+ reg_cmd_timed <= s_ctrlport_req_data[RX_CMD_TIMED_POS];
+ s_ctrlport_resp_ack <= 1;
+
+ // cmd_stop must remain asserted until it has completed
+ if (!cmd_stop || cmd_stop_ack) begin
+ cmd_stop <= (s_ctrlport_req_data[RX_CMD_LEN-1:0] == RX_CMD_STOP);
+ end
+ clear_fifo <= (s_ctrlport_req_data[RX_CMD_LEN-1:0] == RX_CMD_STOP);
+ end
+ REG_RX_CMD_NUM_WORDS_LO: begin
+ reg_cmd_num_words[31:0] <= s_ctrlport_req_data;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_CMD_NUM_WORDS_HI: begin
+ reg_cmd_num_words[NUM_WORDS_LEN-1:32] <= s_ctrlport_req_data[NUM_WORDS_LEN-32-1:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_CMD_TIME_LO: begin
+ reg_cmd_time[31:0] <= s_ctrlport_req_data;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_CMD_TIME_HI: begin
+ reg_cmd_time[63:32] <= s_ctrlport_req_data;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_MAX_WORDS_PER_PKT: begin
+ reg_max_pkt_len <= s_ctrlport_req_data;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_ERR_PORT: begin
+ reg_error_portid <= s_ctrlport_req_data[9:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_ERR_REM_PORT: begin
+ reg_error_rem_portid <= s_ctrlport_req_data[9:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_ERR_REM_EPID: begin
+ reg_error_rem_epid <= s_ctrlport_req_data[15:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_ERR_ADDR: begin
+ reg_error_addr <= s_ctrlport_req_data[19:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_HAS_TIME: begin
+ reg_has_time <= s_ctrlport_req_data[0:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ endcase
+ end
+
+ // Handle register reads
+ if (s_ctrlport_req_rd) begin
+ case (s_ctrlport_req_addr)
+ REG_RX_STATUS: begin
+ s_ctrlport_resp_data[CMD_FIFO_SPACE_POS+:CMD_FIFO_SPACE_LEN]
+ <= cmd_fifo_space[CMD_FIFO_SPACE_LEN-1:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_CMD: begin
+ s_ctrlport_resp_data[RX_CMD_LEN-1:0] <= reg_cmd_word;
+ s_ctrlport_resp_data[RX_CMD_TIMED_POS] <= reg_cmd_timed;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_CMD_NUM_WORDS_LO: begin
+ s_ctrlport_resp_data <= reg_cmd_num_words[31:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_CMD_NUM_WORDS_HI: begin
+ s_ctrlport_resp_data[NUM_WORDS_LEN-32-1:0] <= reg_cmd_num_words[NUM_WORDS_LEN-1:32];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_CMD_TIME_LO: begin
+ s_ctrlport_resp_data <= reg_cmd_time[31:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_CMD_TIME_HI: begin
+ s_ctrlport_resp_data <= reg_cmd_time[63:32];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_MAX_WORDS_PER_PKT: begin
+ s_ctrlport_resp_data <= reg_max_pkt_len;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_ERR_PORT: begin
+ s_ctrlport_resp_data[9:0] <= reg_error_portid;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_ERR_REM_PORT: begin
+ s_ctrlport_resp_data[9:0] <= reg_error_rem_portid;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_ERR_REM_EPID: begin
+ s_ctrlport_resp_data[15:0] <= reg_error_rem_epid;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_ERR_ADDR: begin
+ s_ctrlport_resp_data[19:0] <= reg_error_addr;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_DATA: begin
+ s_ctrlport_resp_data <= radio_rx_data;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_RX_HAS_TIME: begin
+ s_ctrlport_resp_data[0] <= reg_has_time;
+ s_ctrlport_resp_ack <= 1;
+ end
+ endcase
+ end
+
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Command Queue
+ //---------------------------------------------------------------------------
+
+ wire [ 63:0] cmd_time; // Time for next start of command
+ wire cmd_timed; // Command is timed (use cmd_time)
+ wire [NUM_WORDS_LEN-1:0] cmd_num_words; // Number of words for next command
+ wire cmd_continuous; // Command is continuous (ignore cmd_num_words)
+ wire cmd_valid; // cmd_* is a valid command
+ wire cmd_done; // Command has completed and can be popped from FIFO
+
+ axi_fifo #(
+ .WIDTH (64 + 1 + NUM_WORDS_LEN + 1),
+ .SIZE (5) // Ideally, this size will lead to an SRL-based FIFO
+ ) cmd_fifo (
+ .clk (radio_clk),
+ .reset (radio_rst),
+ .clear (clear_fifo),
+ .i_tdata ({ reg_cmd_time, reg_cmd_timed, reg_cmd_num_words, (reg_cmd_word == RX_CMD_CONTINUOUS) }),
+ .i_tvalid (reg_cmd_valid),
+ .i_tready (),
+ .o_tdata ({ cmd_time, cmd_timed, cmd_num_words, cmd_continuous }),
+ .o_tvalid (cmd_valid),
+ .o_tready (cmd_done),
+ .space (cmd_fifo_space),
+ .occupied ()
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Receiver State Machine
+ //---------------------------------------------------------------------------
+
+ // FSM state values
+ localparam ST_IDLE = 0;
+ localparam ST_TIME_CHECK = 1;
+ localparam ST_RUNNING = 2;
+ localparam ST_STOP = 3;
+ localparam ST_REPORT_ERR = 4;
+ localparam ST_REPORT_ERR_WAIT = 5;
+
+ reg [ 2:0] state = ST_IDLE; // Current state
+ reg [NUM_WORDS_LEN-1:0] words_left; // Words left in current command
+ reg [ 31:0] words_left_pkt; // Words left in current packet
+ reg first_word = 1'b1; // Next word is first in packet
+ reg [ 15:0] seq_num = 0; // Sequence number (packet count)
+ reg [ 63:0] error_time; // Time at which overflow occurred
+ reg [ERR_RX_CODE_W-1:0] error_code; // Error code register
+
+ // Output FIFO signals
+ wire [ 15:0] out_fifo_space;
+ reg [SAMP_W*NSPC-1:0] out_fifo_tdata;
+ reg out_fifo_tlast;
+ reg out_fifo_tvalid = 1'b0;
+ reg [ 63:0] out_fifo_timestamp;
+ reg out_fifo_teob;
+ reg out_fifo_almost_full;
+
+ reg [63:0] radio_time_low_samp, radio_time_hi_samp;
+ reg time_now, time_past;
+
+ // All ctrlport requests have a time
+ assign m_ctrlport_req_has_time = 1'b1;
+
+ // Acknowledge STOP requests and pop the command FIFO in the STOP state
+ assign cmd_stop_ack = (state == ST_STOP);
+ assign cmd_done = (state == ST_STOP);
+
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ state <= ST_IDLE;
+ out_fifo_tvalid <= 1'b0;
+ seq_num <= 'd0;
+ m_ctrlport_req_wr <= 1'b0;
+ first_word <= 1'b1;
+ end else begin
+ // Default assignments
+ out_fifo_tvalid <= 1'b0;
+ out_fifo_tlast <= 1'b0;
+ out_fifo_teob <= 1'b0;
+ m_ctrlport_req_wr <= 1'b0;
+
+ if (radio_rx_stb) begin
+ // Get the time for the low sample and the high sample of the radio
+ // word (needed when NISPC > 1). Compensate for the delay required to
+ // check the time by adding 3 clock cycles worth of samples.
+ radio_time_low_samp <= (radio_time + 3*NSPC);
+ radio_time_hi_samp <= (radio_time + 3*NSPC + (NSPC-1));
+
+ // Register the time comparisons so they don't become the critical path
+ time_now <= (cmd_time >= radio_time_low_samp &&
+ cmd_time <= radio_time_hi_samp);
+ time_past <= (cmd_time < radio_time_low_samp);
+ end
+
+ case (state)
+ ST_IDLE : begin
+ // Wait for a new command to arrive and allow a cycle for the time
+ // comparisons to update.
+ if (cmd_valid && radio_rx_stb) begin
+ state <= ST_TIME_CHECK;
+ end else if (cmd_stop) begin
+ state <= ST_STOP;
+ end
+ first_word <= 1'b1;
+ end
+
+ ST_TIME_CHECK : begin
+ if (cmd_stop) begin
+ // Nothing to do but stop (timed STOP commands are not supported)
+ state <= ST_STOP;
+ end else if (cmd_timed && time_past && radio_rx_stb) begin
+ // Got this command later than its execution time
+ //synthesis translate_off
+ $display("WARNING: radio_rx_core: Late command error");
+ //synthesis translate_on
+ error_code <= ERR_RX_LATE_CMD;
+ error_time <= radio_time;
+ state <= ST_REPORT_ERR;
+ end else if (!cmd_timed || (time_now && radio_rx_stb)) begin
+ // Either it's time to run this command or it should run
+ // immediately.
+ words_left <= cmd_num_words;
+ words_left_pkt <= reg_max_pkt_len;
+ state <= ST_RUNNING;
+ end
+ end
+
+ ST_RUNNING : begin
+ if (radio_rx_stb) begin
+ // Output the next word
+ out_fifo_tvalid <= 1'b1;
+ out_fifo_tdata <= radio_rx_data;
+ if (first_word) begin
+ out_fifo_timestamp <= radio_time;
+ first_word <= 1'b0;
+ end
+
+ // Update word counters
+ words_left <= words_left - 1;
+ words_left_pkt <= words_left_pkt - 1;
+
+ if ((words_left == 1 && !cmd_continuous) || cmd_stop) begin
+ // This command has finished, or we've been asked to stop.
+ state <= ST_STOP;
+ out_fifo_tlast <= 1'b1;
+ out_fifo_teob <= 1'b1;
+ first_word <= 1'b1;
+ end else if (words_left_pkt == 1) begin
+ // We've finished building a packet
+ seq_num <= seq_num + 1;
+ words_left_pkt <= reg_max_pkt_len;
+ out_fifo_tlast <= 1'b1;
+ first_word <= 1'b1;
+ end
+
+ // Check for overflow. Note that we've left enough room in the
+ // output FIFO so that we can end the packet cleanly.
+ if (out_fifo_almost_full) begin
+ // End the command and terminate packet early
+ //synthesis translate_off
+ $display("WARNING: radio_rx_core: Overrun error");
+ //synthesis translate_on
+ out_fifo_tlast <= 1'b1;
+ out_fifo_teob <= 1'b1;
+ seq_num <= seq_num + 1;
+ error_time <= radio_time;
+ error_code <= ERR_RX_OVERRUN;
+ state <= ST_REPORT_ERR;
+ end
+
+ end
+ end
+
+ ST_STOP : begin
+ // This single-cycle state allows time for STOP to be acknowledged
+ // and for the command FIFO to be popped.
+ state <= ST_IDLE;
+ end
+
+ ST_REPORT_ERR : begin
+ // Setup write of error code
+ m_ctrlport_req_wr <= 1'b1;
+ m_ctrlport_req_data <= 0;
+ m_ctrlport_req_data[ERR_RX_CODE_W-1:0] <= error_code;
+ m_ctrlport_req_addr <= reg_error_addr;
+ m_ctrlport_req_time <= error_time;
+ state <= ST_REPORT_ERR_WAIT;
+ end
+
+ ST_REPORT_ERR_WAIT : begin
+ // Wait for write of error code and timestamp to complete
+ if (m_ctrlport_resp_ack) begin
+ state <= ST_STOP;
+ end
+ end
+
+ default : state <= ST_IDLE;
+ endcase
+ end
+ end
+
+
+ assign radio_rx_running = (state == ST_RUNNING); // We're actively acquiring
+
+ // Directly connect the port ID, remote port ID, and remote EPID since they
+ // are only used for error reporting.
+ assign m_ctrlport_req_portid = reg_error_portid;
+ assign m_ctrlport_req_rem_epid = reg_error_rem_epid;
+ assign m_ctrlport_req_rem_portid = reg_error_rem_portid;
+
+
+ //---------------------------------------------------------------------------
+ // Output FIFO
+ //---------------------------------------------------------------------------
+ //
+ // Here we buffer output samples and monitor FIFO fullness to be able to
+ // detect overflows.
+ //
+ //---------------------------------------------------------------------------
+
+ axi_fifo #(
+ .WIDTH (1+64+1+SAMP_W*NSPC),
+ .SIZE (5) // Ideally, this size will lead to an SRL-based FIFO
+ ) output_fifo (
+ .clk (radio_clk),
+ .reset (radio_rst),
+ .clear (1'b0),
+ .i_tdata ({out_fifo_teob, out_fifo_timestamp, out_fifo_tlast, out_fifo_tdata}),
+ .i_tvalid (out_fifo_tvalid),
+ .i_tready (),
+ .o_tdata ({m_axis_teob, m_axis_ttimestamp, m_axis_tlast, m_axis_tdata}),
+ .o_tvalid (m_axis_tvalid),
+ .o_tready (m_axis_tready),
+ .space (out_fifo_space),
+ .occupied ()
+ );
+
+ // Create a register to indicate if the output FIFO is about to overflow
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ out_fifo_almost_full <= 1'b0;
+ end else begin
+ out_fifo_almost_full <= (out_fifo_space < 5);
+ end
+ end
+
+
+endmodule
+
+
+`default_nettype wire
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_tx_core.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_tx_core.v
new file mode 100644
index 000000000..d40db5122
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/radio_tx_core.v
@@ -0,0 +1,417 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: radio_tx_core
+//
+// Description:
+//
+// This module contains the core Tx radio data-path logic. It receives samples
+// over AXI-Stream that it then sends to the radio interface coincident with a
+// strobe signal that must be provided by the radio interface.
+//
+// There are no registers for starting or stopping the transmitter. It is
+// operated simply by providing data packets via its AXI-Stream data interface.
+// The end-of-burst (EOB) signal is used to indicate when the transmitter is
+// allowed to stop transmitting. Packet timestamps can be used to indicate when
+// transmission should start.
+//
+// Care must be taken to provide data to the transmitter at a rate that is
+// faster than the radio needs it so that underflows do not occur. Similarly,
+// timed packets must be delivered before the timestamp expires. If a packet
+// arrives late, then it will be dropped and the error will be reported via the
+// CTRL port interface.
+//
+// Parameters:
+//
+// SAMP_W : Width of a radio sample
+// NSPC : Number of radio samples per radio clock cycle
+//
+
+
+module radio_tx_core #(
+ parameter SAMP_W = 32,
+ parameter NSPC = 1
+) (
+ input wire radio_clk,
+ input wire radio_rst,
+
+
+ //---------------------------------------------------------------------------
+ // Control Interface
+ //---------------------------------------------------------------------------
+
+ // Slave (Register Reads and Writes)
+ input wire s_ctrlport_req_wr,
+ input wire s_ctrlport_req_rd,
+ input wire [19:0] s_ctrlport_req_addr,
+ input wire [31:0] s_ctrlport_req_data,
+ output reg s_ctrlport_resp_ack = 1'b0,
+ output reg [31:0] s_ctrlport_resp_data,
+
+ // Master (Error Reporting)
+ output reg m_ctrlport_req_wr = 1'b0,
+ output reg [19:0] m_ctrlport_req_addr,
+ output reg [31:0] m_ctrlport_req_data,
+ output wire m_ctrlport_req_has_time,
+ output reg [63:0] m_ctrlport_req_time,
+ output wire [ 9:0] m_ctrlport_req_portid,
+ output wire [15:0] m_ctrlport_req_rem_epid,
+ output wire [ 9:0] m_ctrlport_req_rem_portid,
+ input wire m_ctrlport_resp_ack,
+
+
+ //---------------------------------------------------------------------------
+ // Radio Interface
+ //---------------------------------------------------------------------------
+
+ input wire [63:0] radio_time,
+
+ output wire [SAMP_W*NSPC-1:0] radio_tx_data,
+ input wire radio_tx_stb,
+
+ // Status indicator (true when transmitting)
+ output wire radio_tx_running,
+
+
+ //---------------------------------------------------------------------------
+ // AXI-Stream Data Input
+ //---------------------------------------------------------------------------
+
+ input wire [SAMP_W*NSPC-1:0] s_axis_tdata,
+ input wire s_axis_tlast,
+ input wire s_axis_tvalid,
+ output wire s_axis_tready,
+ // Sideband info
+ input wire [ 63:0] s_axis_ttimestamp,
+ input wire s_axis_thas_time,
+ input wire s_axis_teob
+);
+
+ `include "rfnoc_block_radio_regs.vh"
+ `include "../../core/rfnoc_chdr_utils.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Register Read/Write Logic
+ //---------------------------------------------------------------------------
+
+ reg [SAMP_W-1:0] reg_idle_value = 0; // Value to output when transmitter is idle
+ reg [ 9:0] reg_error_portid = 0; // Port ID to use for error reporting
+ reg [ 15:0] reg_error_rem_epid = 0; // Remote EPID to use for error reporting
+ reg [ 9:0] reg_error_rem_portid = 0; // Remote port ID to use for error reporting
+ reg [ 19:0] reg_error_addr = 0; // Address to use for error reporting
+
+ reg [TX_ERR_POLICY_LEN-1:0] reg_policy = TX_ERR_POLICY_PACKET;
+
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ s_ctrlport_resp_ack <= 0;
+ reg_idle_value <= 0;
+ reg_error_portid <= 0;
+ reg_error_rem_epid <= 0;
+ reg_error_rem_portid <= 0;
+ reg_error_addr <= 0;
+ reg_policy <= TX_ERR_POLICY_PACKET;
+ end else begin
+ // Default assignments
+ s_ctrlport_resp_ack <= 0;
+ s_ctrlport_resp_data <= 0;
+
+ // Handle register writes
+ if (s_ctrlport_req_wr) begin
+ case (s_ctrlport_req_addr)
+ REG_TX_IDLE_VALUE: begin
+ reg_idle_value <= s_ctrlport_req_data[SAMP_W-1:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERROR_POLICY: begin
+ // Only allow valid configurations
+ case (s_ctrlport_req_data[TX_ERR_POLICY_LEN-1:0])
+ TX_ERR_POLICY_PACKET : reg_policy <= TX_ERR_POLICY_PACKET;
+ TX_ERR_POLICY_BURST : reg_policy <= TX_ERR_POLICY_BURST;
+ default : reg_policy <= TX_ERR_POLICY_PACKET;
+ endcase
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERR_PORT: begin
+ reg_error_portid <= s_ctrlport_req_data[9:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERR_REM_PORT: begin
+ reg_error_rem_portid <= s_ctrlport_req_data[9:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERR_REM_EPID: begin
+ reg_error_rem_epid <= s_ctrlport_req_data[15:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERR_ADDR: begin
+ reg_error_addr <= s_ctrlport_req_data[19:0];
+ s_ctrlport_resp_ack <= 1;
+ end
+ endcase
+ end
+
+ // Handle register reads
+ if (s_ctrlport_req_rd) begin
+ case (s_ctrlport_req_addr)
+ REG_TX_IDLE_VALUE: begin
+ s_ctrlport_resp_data[SAMP_W-1:0] <= reg_idle_value;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERROR_POLICY: begin
+ s_ctrlport_resp_data[TX_ERR_POLICY_LEN-1:0] <= reg_policy;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERR_PORT: begin
+ s_ctrlport_resp_data[9:0] <= reg_error_portid;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERR_REM_PORT: begin
+ s_ctrlport_resp_data[9:0] <= reg_error_rem_portid;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERR_REM_EPID: begin
+ s_ctrlport_resp_data[15:0] <= reg_error_rem_epid;
+ s_ctrlport_resp_ack <= 1;
+ end
+ REG_TX_ERR_ADDR: begin
+ s_ctrlport_resp_data[19:0] <= reg_error_addr;
+ s_ctrlport_resp_ack <= 1;
+ end
+ endcase
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Transmitter State Machine
+ //---------------------------------------------------------------------------
+
+ // FSM state values
+ localparam ST_IDLE = 0;
+ localparam ST_TIME_CHECK = 1;
+ localparam ST_TRANSMIT = 2;
+ localparam ST_POLICY_WAIT = 3;
+
+ reg [1:0] state = ST_IDLE;
+
+ reg sop = 1'b1; // Start of packet
+
+ reg [ERR_TX_CODE_W-1:0] new_error_code;
+ reg [ 63:0] new_error_time;
+ reg new_error_valid = 1'b0;
+
+ reg time_now, time_past;
+
+
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ state <= ST_IDLE;
+ sop <= 1'b1;
+ new_error_valid <= 1'b0;
+ end else begin
+ new_error_valid <= 1'b0;
+
+ // Register time comparisons so they don't become the critical path
+ time_now <= (radio_time == s_axis_ttimestamp);
+ time_past <= (radio_time > s_axis_ttimestamp);
+
+ // Track if the next word will be the start of a packet (sop)
+ if (s_axis_tvalid && s_axis_tready) begin
+ sop <= s_axis_tlast;
+ end
+
+ case (state)
+ ST_IDLE : begin
+ // Wait for a new packet to arrive and allow a cycle for the time
+ // comparisons to update.
+ if (s_axis_tvalid) begin
+ state <= ST_TIME_CHECK;
+ end
+ end
+
+ ST_TIME_CHECK : begin
+ if (!s_axis_thas_time || time_now) begin
+ // We have a new packet without a timestamp, or a new packet
+ // whose time has arrived.
+ state <= ST_TRANSMIT;
+ end else if (time_past) begin
+ // We have a new packet with a timestamp, but the time has passed.
+ //synthesis translate off
+ $display("WARNING: radio_tx_core: Late data error");
+ //synthesis translate_on
+ new_error_code <= ERR_TX_LATE_DATA;
+ new_error_time <= radio_time;
+ new_error_valid <= 1'b1;
+ state <= ST_POLICY_WAIT;
+ end
+ end
+
+ ST_TRANSMIT : begin
+ if (radio_tx_stb) begin
+ if (!s_axis_tvalid) begin
+ // The radio strobed for new data but we don't have any to give
+ //synthesis translate off
+ $display("WARNING: radio_tx_core: Underrun error");
+ //synthesis translate_on
+ new_error_code <= ERR_TX_UNDERRUN;
+ new_error_time <= radio_time;
+ new_error_valid <= 1'b1;
+ state <= ST_POLICY_WAIT;
+ end else if (s_axis_tlast && s_axis_teob) begin
+ // We're done with this burst of packets, so acknowledge EOB and
+ // go back to idle.
+ new_error_code <= ERR_TX_EOB_ACK;
+ new_error_time <= radio_time;
+ new_error_valid <= 1'b1;
+ state <= ST_IDLE;
+ end
+ end
+ end
+
+ ST_POLICY_WAIT : begin
+ // If we came here from ST_TIME_CHECK or ST_TRANSMIT and we're in the
+ // middle of a packet then we just wait until we reach the end of the
+ // packet.
+ if (s_axis_tvalid && s_axis_tlast) begin
+ // We're either at the end of a packet or between packets
+ if (reg_policy == TX_ERR_POLICY_PACKET ||
+ (reg_policy == TX_ERR_POLICY_BURST && s_axis_teob)) begin
+ state <= ST_IDLE;
+ end
+
+ // If we came from ST_TRANSMIT and we happen to already be between
+ // packets (i.e., we underflowed while waiting for the next packet).
+ end else if (!s_axis_tvalid && sop) begin
+ if (reg_policy == TX_ERR_POLICY_PACKET) state <= ST_IDLE;
+ end
+ end
+
+ default : state <= ST_IDLE;
+ endcase
+ end
+ end
+
+
+ // Output the current sample whenever we're transmitting and the sample is
+ // valid. Otherwise, output the idle value.
+ assign radio_tx_data = (s_axis_tvalid && state == ST_TRANSMIT) ?
+ s_axis_tdata :
+ {NSPC{reg_idle_value[SAMP_W-1:0]}};
+
+ // Read packet in the transmit state or dump it in the error state
+ assign s_axis_tready = (radio_tx_stb && (state == ST_TRANSMIT)) ||
+ (state == ST_POLICY_WAIT);
+
+ // Indicate whether Tx interface is actively transmitting
+ assign radio_tx_running = (state == ST_TRANSMIT);
+
+
+ //---------------------------------------------------------------------------
+ // Error FIFO
+ //---------------------------------------------------------------------------
+ //
+ // This FIFO queues up errors in case we get multiple errors in a row faster
+ // than they can be reported. If the FIFO fills then new errors will be
+ // ignored.
+ //
+ //---------------------------------------------------------------------------
+
+ // Error information
+ wire [ERR_TX_CODE_W-1:0] next_error_code;
+ wire [ 63:0] next_error_time;
+ wire next_error_valid;
+ reg next_error_ready = 1'b0;
+
+ wire new_error_ready;
+
+ axi_fifo_short #(
+ .WIDTH (64 + ERR_TX_CODE_W)
+ ) error_fifo (
+ .clk (radio_clk),
+ .reset (radio_rst),
+ .clear (1'b0),
+ .i_tdata ({new_error_time, new_error_code}),
+ .i_tvalid (new_error_valid & new_error_ready), // Mask with ready to prevent FIFO corruption
+ .i_tready (new_error_ready),
+ .o_tdata ({next_error_time, next_error_code}),
+ .o_tvalid (next_error_valid),
+ .o_tready (next_error_ready),
+ .space (),
+ .occupied ()
+ );
+
+ //synthesis translate_off
+ // Output a message if the error FIFO overflows
+ always @(posedge radio_clk) begin
+ if (new_error_valid && !new_error_ready) begin
+ $display("WARNING: Tx error report dropped!");
+ end
+ end
+ //synthesis translate_on
+
+
+ //---------------------------------------------------------------------------
+ // Error Reporting State Machine
+ //---------------------------------------------------------------------------
+ //
+ // This state machine reports errors that have been queued up in the error
+ // FIFO.
+ //
+ //---------------------------------------------------------------------------
+
+ localparam ST_ERR_IDLE = 0;
+ localparam ST_ERR_CODE = 1;
+
+ reg [0:0] err_state = ST_ERR_IDLE;
+
+ // All ctrlport requests have a time
+ assign m_ctrlport_req_has_time = 1'b1;
+
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ m_ctrlport_req_wr <= 1'b0;
+ err_state <= ST_ERR_IDLE;
+ next_error_ready <= 1'b0;
+ end else begin
+ m_ctrlport_req_wr <= 1'b0;
+ next_error_ready <= 1'b0;
+
+ case (err_state)
+ ST_ERR_IDLE : begin
+ if (next_error_valid) begin
+ // Setup write of error code
+ m_ctrlport_req_wr <= 1'b1;
+ m_ctrlport_req_addr <= reg_error_addr;
+ m_ctrlport_req_data <= {{(32-ERR_TX_CODE_W){1'b0}}, next_error_code};
+ m_ctrlport_req_time <= next_error_time;
+ next_error_ready <= 1'b1;
+ err_state <= ST_ERR_CODE;
+ end
+ end
+
+ ST_ERR_CODE : begin
+ // Wait for write of error code and timestamp
+ if (m_ctrlport_resp_ack) begin
+ err_state <= ST_ERR_IDLE;
+ end
+ end
+
+ default : err_state <= ST_ERR_IDLE;
+ endcase
+ end
+ end
+
+
+ // Directly connect the port ID, remote port ID, remote EPID since they are
+ // only used for error reporting.
+ assign m_ctrlport_req_portid = reg_error_portid;
+ assign m_ctrlport_req_rem_epid = reg_error_rem_epid;
+ assign m_ctrlport_req_rem_portid = reg_error_rem_portid;
+
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio.v
new file mode 100644
index 000000000..a97b141c0
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio.v
@@ -0,0 +1,546 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_radio
+//
+// Description: This is the top-level file for the RFNoC radio block.
+//
+// Parameters:
+//
+// THIS_PORTID : CTRL port ID to which this block is connected
+// CHDR_W : CHDR AXI-Stream data bus width
+// NIPC : Number of radio samples per radio clock cycle
+// ITEM_W : Radio sample width
+// NUM_PORTS : Number of radio channels (RX/TX pairs)
+// MTU : Maximum transmission unit (i.e., maximum packet size)
+// in CHDR words is 2**MTU.
+// CTRL_FIFO_SIZE : Size of the Control Port slave FIFO. This affects the
+// number of outstanding commands that can be pending.
+// PERIPH_BASE_ADDR : CTRL port peripheral window base address
+// PERIPH_ADDR_W : CTRL port peripheral address space = 2**PERIPH_ADDR_W
+//
+
+
+module rfnoc_block_radio #(
+ parameter THIS_PORTID = 0,
+ parameter CHDR_W = 64,
+ parameter NIPC = 1,
+ parameter ITEM_W = 32,
+ parameter NUM_PORTS = 2,
+ parameter MTU = 10,
+ parameter CTRL_FIFO_SIZE = 9,
+ parameter PERIPH_BASE_ADDR = 20'h80000,
+ parameter PERIPH_ADDR_W = 19
+) (
+ //---------------------------------------------------------------------------
+ // AXIS CHDR Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_chdr_clk,
+
+ // CHDR inputs from framework
+ input wire [CHDR_W*NUM_PORTS-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tready,
+
+ // CHDR outputs to framework
+ output wire [CHDR_W*NUM_PORTS-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
+
+ // Backend interface
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+
+
+ //---------------------------------------------------------------------------
+ // AXIS CTRL Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_ctrl_clk,
+
+ // CTRL port requests from framework
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+
+ // CTRL port requests to framework
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready,
+
+
+ //---------------------------------------------------------------------------
+ // CTRL Port Peripheral Interface
+ //---------------------------------------------------------------------------
+
+ output wire m_ctrlport_req_wr,
+ output wire m_ctrlport_req_rd,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [31:0] m_ctrlport_req_data,
+ output wire [ 3:0] m_ctrlport_req_byte_en,
+ output wire m_ctrlport_req_has_time,
+ output wire [63:0] m_ctrlport_req_time,
+ input wire m_ctrlport_resp_ack,
+ input wire [ 1:0] m_ctrlport_resp_status,
+ input wire [31:0] m_ctrlport_resp_data,
+
+
+ //---------------------------------------------------------------------------
+ // Radio Interface
+ //---------------------------------------------------------------------------
+
+ input wire radio_clk,
+
+ // Timekeeper interface
+ input wire [63:0] radio_time,
+
+ // Radio Rx interface
+ input wire [(ITEM_W*NIPC)*NUM_PORTS-1:0] radio_rx_data,
+ input wire [ NUM_PORTS-1:0] radio_rx_stb,
+ output wire [ NUM_PORTS-1:0] radio_rx_running,
+
+ // Radio Tx interface
+ output wire [(ITEM_W*NIPC)*NUM_PORTS-1:0] radio_tx_data,
+ input wire [ NUM_PORTS-1:0] radio_tx_stb,
+ output wire [ NUM_PORTS-1:0] radio_tx_running
+);
+
+ `include "rfnoc_block_radio_regs.vh"
+ `include "../../core/rfnoc_axis_ctrl_utils.vh"
+
+ localparam NOC_ID = 32'h12AD1000;
+ localparam RADIO_W = NIPC*ITEM_W;
+
+
+ // Radio Tx data stream
+ wire [RADIO_W*NUM_PORTS-1:0] axis_tx_tdata;
+ wire [ NUM_PORTS-1:0] axis_tx_tlast;
+ wire [ NUM_PORTS-1:0] axis_tx_tvalid;
+ wire [ NUM_PORTS-1:0] axis_tx_tready;
+ wire [ 64*NUM_PORTS-1:0] axis_tx_ttimestamp;
+ wire [ NUM_PORTS-1:0] axis_tx_thas_time;
+ wire [ NUM_PORTS-1:0] axis_tx_teob;
+
+ // Radio Rx data stream
+ wire [RADIO_W*NUM_PORTS-1:0] axis_rx_tdata;
+ wire [ NUM_PORTS-1:0] axis_rx_tlast;
+ wire [ NUM_PORTS-1:0] axis_rx_tvalid;
+ wire [ NUM_PORTS-1:0] axis_rx_tready;
+ wire [ 64*NUM_PORTS-1:0] axis_rx_ttimestamp;
+ wire [ NUM_PORTS-1:0] axis_rx_thas_time;
+ wire [ NUM_PORTS-1:0] axis_rx_teob;
+
+ // Control port signals used for register access (NoC shell masters user logic)
+ wire ctrlport_reg_req_wr;
+ wire ctrlport_reg_req_rd;
+ wire [19:0] ctrlport_reg_req_addr;
+ wire ctrlport_reg_has_time;
+ wire [63:0] ctrlport_reg_time;
+ wire [31:0] ctrlport_reg_req_data;
+ wire [31:0] ctrlport_reg_resp_data;
+ wire ctrlport_reg_resp_ack;
+
+ // Control port signals used for error reporting (user logic masters to NoC shell)
+ wire ctrlport_err_req_wr;
+ wire [19:0] ctrlport_err_req_addr;
+ wire [ 9:0] ctrlport_err_req_portid;
+ wire [15:0] ctrlport_err_req_rem_epid;
+ wire [ 9:0] ctrlport_err_req_rem_portid;
+ wire [31:0] ctrlport_err_req_data;
+ wire ctrlport_err_req_has_time;
+ wire [63:0] ctrlport_err_req_time;
+ wire ctrlport_err_resp_ack;
+
+
+ //---------------------------------------------------------------------------
+ // NoC Shell
+ //---------------------------------------------------------------------------
+
+ wire rfnoc_chdr_rst;
+ wire radio_rst;
+
+ noc_shell_radio #(
+ .NOC_ID (NOC_ID),
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .CTRLPORT_SLV_EN (1),
+ .CTRLPORT_MST_EN (1),
+ .CTRL_FIFO_SIZE (CTRL_FIFO_SIZE),
+ .NUM_DATA_I (NUM_PORTS),
+ .NUM_DATA_O (NUM_PORTS),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .PYLD_FIFO_SIZE (MTU),
+ .MTU (MTU)
+ ) noc_shell_radio_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_ctrl_rst (),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
+ .ctrlport_clk (radio_clk),
+ .ctrlport_rst (radio_rst),
+ .m_ctrlport_req_wr (ctrlport_reg_req_wr),
+ .m_ctrlport_req_rd (ctrlport_reg_req_rd),
+ .m_ctrlport_req_addr (ctrlport_reg_req_addr),
+ .m_ctrlport_req_data (ctrlport_reg_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (ctrlport_reg_has_time),
+ .m_ctrlport_req_time (ctrlport_reg_time),
+ .m_ctrlport_resp_ack (ctrlport_reg_resp_ack),
+ .m_ctrlport_resp_status (AXIS_CTRL_STS_OKAY),
+ .m_ctrlport_resp_data (ctrlport_reg_resp_data),
+ .s_ctrlport_req_wr (ctrlport_err_req_wr),
+ .s_ctrlport_req_rd (1'b0),
+ .s_ctrlport_req_addr (ctrlport_err_req_addr),
+ .s_ctrlport_req_portid (ctrlport_err_req_portid),
+ .s_ctrlport_req_rem_epid (ctrlport_err_req_rem_epid),
+ .s_ctrlport_req_rem_portid (ctrlport_err_req_rem_portid),
+ .s_ctrlport_req_data (ctrlport_err_req_data),
+ .s_ctrlport_req_byte_en (4'hF),
+ .s_ctrlport_req_has_time (ctrlport_err_req_has_time),
+ .s_ctrlport_req_time (ctrlport_err_req_time),
+ .s_ctrlport_resp_ack (ctrlport_err_resp_ack),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (),
+ .axis_data_clk (radio_clk),
+ .axis_data_rst (radio_rst),
+ .m_axis_tdata (axis_tx_tdata),
+ .m_axis_tkeep (), // Radio only transmits full words
+ .m_axis_tlast (axis_tx_tlast),
+ .m_axis_tvalid (axis_tx_tvalid),
+ .m_axis_tready (axis_tx_tready),
+ .m_axis_ttimestamp (axis_tx_ttimestamp),
+ .m_axis_thas_time (axis_tx_thas_time),
+ .m_axis_teov (),
+ .m_axis_teob (axis_tx_teob),
+ .s_axis_tdata (axis_rx_tdata),
+ .s_axis_tkeep ({NUM_PORTS*NIPC{1'b1}}), // Radio only receives full words
+ .s_axis_tlast (axis_rx_tlast),
+ .s_axis_tvalid (axis_rx_tvalid),
+ .s_axis_tready (axis_rx_tready),
+ .s_axis_ttimestamp (axis_rx_ttimestamp),
+ .s_axis_thas_time (axis_rx_thas_time),
+ .s_axis_teov ({NUM_PORTS{1'b0}}),
+ .s_axis_teob (axis_rx_teob)
+ );
+
+ // Cross the CHDR reset to the radio_clk domain
+ pulse_synchronizer #(
+ .MODE ("POSEDGE")
+ ) ctrl_rst_sync_i (
+ .clk_a (rfnoc_chdr_clk),
+ .rst_a (1'b0),
+ .pulse_a (rfnoc_chdr_rst),
+ .busy_a (),
+ .clk_b (radio_clk),
+ .pulse_b (radio_rst)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Decode Control Port Addresses
+ //---------------------------------------------------------------------------
+ //
+ // This block splits the NoC shell's single master control port interface
+ // into three masters, connected to the shared registers, radio cores, and
+ // the external CTRL port peripheral interface. The responses from each of
+ // these are merged into a single response and sent back to the NoC shell.
+ //
+ //---------------------------------------------------------------------------
+
+ wire ctrlport_shared_req_wr;
+ wire ctrlport_shared_req_rd;
+ wire [19:0] ctrlport_shared_req_addr;
+ wire [31:0] ctrlport_shared_req_data;
+ wire [ 3:0] ctrlport_shared_req_byte_en;
+ wire ctrlport_shared_req_has_time;
+ wire [63:0] ctrlport_shared_req_time;
+ reg ctrlport_shared_resp_ack = 1'b0;
+ reg [31:0] ctrlport_shared_resp_data = 0;
+
+ wire ctrlport_core_req_wr;
+ wire ctrlport_core_req_rd;
+ wire [19:0] ctrlport_core_req_addr;
+ wire [31:0] ctrlport_core_req_data;
+ wire [ 3:0] ctrlport_core_req_byte_en;
+ wire ctrlport_core_req_has_time;
+ wire [63:0] ctrlport_core_req_time;
+ wire ctrlport_core_resp_ack;
+ wire [31:0] ctrlport_core_resp_data;
+
+ ctrlport_decoder_param #(
+ .NUM_SLAVES (3),
+ .PORT_BASE ({PERIPH_BASE_ADDR, RADIO_BASE_ADDR, SHARED_BASE_ADDR}),
+ .PORT_ADDR_W({PERIPH_ADDR_W, RADIO_ADDR_W + $clog2(NUM_PORTS), SHARED_ADDR_W})
+ ) ctrlport_decoder_param_i (
+ .ctrlport_clk (radio_clk),
+ .ctrlport_rst (radio_rst),
+ .s_ctrlport_req_wr (ctrlport_reg_req_wr),
+ .s_ctrlport_req_rd (ctrlport_reg_req_rd),
+ .s_ctrlport_req_addr (ctrlport_reg_req_addr),
+ .s_ctrlport_req_data (ctrlport_reg_req_data),
+ .s_ctrlport_req_byte_en (4'b0),
+ .s_ctrlport_req_has_time (ctrlport_reg_has_time),
+ .s_ctrlport_req_time (ctrlport_reg_time),
+ .s_ctrlport_resp_ack (ctrlport_reg_resp_ack),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (ctrlport_reg_resp_data),
+ .m_ctrlport_req_wr ({m_ctrlport_req_wr,
+ ctrlport_core_req_wr,
+ ctrlport_shared_req_wr}),
+ .m_ctrlport_req_rd ({m_ctrlport_req_rd,
+ ctrlport_core_req_rd,
+ ctrlport_shared_req_rd}),
+ .m_ctrlport_req_addr ({m_ctrlport_req_addr,
+ ctrlport_core_req_addr,
+ ctrlport_shared_req_addr}),
+ .m_ctrlport_req_data ({m_ctrlport_req_data,
+ ctrlport_core_req_data,
+ ctrlport_shared_req_data}),
+ .m_ctrlport_req_byte_en ({m_ctrlport_req_byte_en,
+ ctrlport_core_req_byte_en,
+ ctrlport_shared_req_byte_en}),
+ .m_ctrlport_req_has_time ({m_ctrlport_req_has_time,
+ ctrlport_core_req_has_time,
+ ctrlport_shared_req_has_time}),
+ .m_ctrlport_req_time ({m_ctrlport_req_time,
+ ctrlport_core_req_time,
+ ctrlport_shared_req_time}),
+ .m_ctrlport_resp_ack ({m_ctrlport_resp_ack,
+ ctrlport_core_resp_ack,
+ ctrlport_shared_resp_ack}),
+ .m_ctrlport_resp_status ({m_ctrlport_resp_status,
+ 2'b00,
+ 2'b00}),
+ .m_ctrlport_resp_data ({m_ctrlport_resp_data,
+ ctrlport_core_resp_data,
+ ctrlport_shared_resp_data
+ })
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Split Radio Control Port Interfaces
+ //---------------------------------------------------------------------------
+
+ wire [ NUM_PORTS-1:0] ctrlport_radios_req_wr;
+ wire [ NUM_PORTS-1:0] ctrlport_radios_req_rd;
+ wire [20*NUM_PORTS-1:0] ctrlport_radios_req_addr;
+ wire [32*NUM_PORTS-1:0] ctrlport_radios_req_data;
+ wire [ NUM_PORTS-1:0] ctrlport_radios_resp_ack;
+ wire [32*NUM_PORTS-1:0] ctrlport_radios_resp_data;
+
+ ctrlport_decoder #(
+ .NUM_SLAVES (NUM_PORTS),
+ .BASE_ADDR (0),
+ .SLAVE_ADDR_W (RADIO_ADDR_W)
+ ) ctrlport_decoder_i (
+ .ctrlport_clk (radio_clk),
+ .ctrlport_rst (radio_rst),
+ .s_ctrlport_req_wr (ctrlport_core_req_wr),
+ .s_ctrlport_req_rd (ctrlport_core_req_rd),
+ .s_ctrlport_req_addr (ctrlport_core_req_addr),
+ .s_ctrlport_req_data (ctrlport_core_req_data),
+ .s_ctrlport_req_byte_en (4'b0),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (ctrlport_core_resp_ack),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (ctrlport_core_resp_data),
+ .m_ctrlport_req_wr (ctrlport_radios_req_wr),
+ .m_ctrlport_req_rd (ctrlport_radios_req_rd),
+ .m_ctrlport_req_addr (ctrlport_radios_req_addr),
+ .m_ctrlport_req_data (ctrlport_radios_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (),
+ .m_ctrlport_req_time (),
+ .m_ctrlport_resp_ack (ctrlport_radios_resp_ack),
+ .m_ctrlport_resp_status ({NUM_PORTS{2'b00}}),
+ .m_ctrlport_resp_data (ctrlport_radios_resp_data)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Merge Control Port Interfaces
+ //---------------------------------------------------------------------------
+ //
+ // This block merges the master control port interfaces of all radio_cores
+ // into a single master for the NoC shell.
+ //
+ //---------------------------------------------------------------------------
+
+ wire [ NUM_PORTS-1:0] ctrlport_err_radio_req_wr;
+ wire [20*NUM_PORTS-1:0] ctrlport_err_radio_req_addr;
+ wire [10*NUM_PORTS-1:0] ctrlport_err_radio_req_portid;
+ wire [16*NUM_PORTS-1:0] ctrlport_err_radio_req_rem_epid;
+ wire [10*NUM_PORTS-1:0] ctrlport_err_radio_req_rem_portid;
+ wire [32*NUM_PORTS-1:0] ctrlport_err_radio_req_data;
+ wire [ NUM_PORTS-1:0] ctrlport_err_radio_req_has_time;
+ wire [64*NUM_PORTS-1:0] ctrlport_err_radio_req_time;
+ wire [ NUM_PORTS-1:0] ctrlport_err_radio_resp_ack;
+
+ ctrlport_combiner #(
+ .NUM_MASTERS (NUM_PORTS),
+ .PRIORITY (0)
+ ) ctrlport_combiner_i (
+ .ctrlport_clk (radio_clk),
+ .ctrlport_rst (radio_rst),
+ .s_ctrlport_req_wr (ctrlport_err_radio_req_wr),
+ .s_ctrlport_req_rd ({NUM_PORTS{1'b0}}),
+ .s_ctrlport_req_addr (ctrlport_err_radio_req_addr),
+ .s_ctrlport_req_portid (ctrlport_err_radio_req_portid),
+ .s_ctrlport_req_rem_epid (ctrlport_err_radio_req_rem_epid),
+ .s_ctrlport_req_rem_portid (ctrlport_err_radio_req_rem_portid),
+ .s_ctrlport_req_data (ctrlport_err_radio_req_data),
+ .s_ctrlport_req_byte_en ({4*NUM_PORTS{1'b1}}),
+ .s_ctrlport_req_has_time (ctrlport_err_radio_req_has_time),
+ .s_ctrlport_req_time (ctrlport_err_radio_req_time),
+ .s_ctrlport_resp_ack (ctrlport_err_radio_resp_ack),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (),
+ .m_ctrlport_req_wr (ctrlport_err_req_wr),
+ .m_ctrlport_req_rd (),
+ .m_ctrlport_req_addr (ctrlport_err_req_addr),
+ .m_ctrlport_req_portid (ctrlport_err_req_portid),
+ .m_ctrlport_req_rem_epid (ctrlport_err_req_rem_epid),
+ .m_ctrlport_req_rem_portid (ctrlport_err_req_rem_portid),
+ .m_ctrlport_req_data (ctrlport_err_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (ctrlport_err_req_has_time),
+ .m_ctrlport_req_time (ctrlport_err_req_time),
+ .m_ctrlport_resp_ack (ctrlport_err_resp_ack),
+ .m_ctrlport_resp_status (2'b0),
+ .m_ctrlport_resp_data (32'b0)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Shared Registers
+ //---------------------------------------------------------------------------
+ //
+ // These registers are shared by all radio channels.
+ //
+ //---------------------------------------------------------------------------
+
+ localparam [15:0] compat_major = 16'd0;
+ localparam [15:0] compat_minor = 16'd0;
+
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ ctrlport_shared_resp_ack <= 0;
+ ctrlport_shared_resp_data <= 0;
+ end else begin
+ // Default assignments
+ ctrlport_shared_resp_ack <= 0;
+ ctrlport_shared_resp_data <= 0;
+
+ // Handle register reads
+ if (ctrlport_shared_req_rd) begin
+ case (ctrlport_shared_req_addr)
+ REG_COMPAT_NUM: begin
+ ctrlport_shared_resp_ack <= 1;
+ ctrlport_shared_resp_data <= { compat_major, compat_minor };
+ end
+ endcase
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Radio Cores
+ //---------------------------------------------------------------------------
+ //
+ // This generate block instantiates one radio core for each channel that is
+ // requested by NUM_PORTS.
+ //
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ generate
+ for (i = 0; i < NUM_PORTS; i = i+1) begin : radio_core_gen
+
+ // The radio core contains all the logic related to a single radio channel.
+ radio_core #(
+ .SAMP_W (ITEM_W),
+ .NSPC (NIPC)
+ ) radio_core_i (
+ .radio_clk (radio_clk),
+ .radio_rst (radio_rst),
+
+ // Slave Control Port (Register Access)
+ .s_ctrlport_req_wr (ctrlport_radios_req_wr[i]),
+ .s_ctrlport_req_rd (ctrlport_radios_req_rd[i]),
+ .s_ctrlport_req_addr (ctrlport_radios_req_addr[i*20 +: 20]),
+ .s_ctrlport_req_data (ctrlport_radios_req_data[i*32 +: 32]),
+ .s_ctrlport_resp_ack (ctrlport_radios_resp_ack[i]),
+ .s_ctrlport_resp_data (ctrlport_radios_resp_data[i*32 +: 32]),
+
+ // Master Control Port (Error Reporting)
+ .m_ctrlport_req_wr (ctrlport_err_radio_req_wr[i]),
+ .m_ctrlport_req_addr (ctrlport_err_radio_req_addr[i*20 +: 20]),
+ .m_ctrlport_req_portid (ctrlport_err_radio_req_portid[i*10 +: 10]),
+ .m_ctrlport_req_rem_epid (ctrlport_err_radio_req_rem_epid[i*16 +: 16]),
+ .m_ctrlport_req_rem_portid (ctrlport_err_radio_req_rem_portid[i*10 +: 10]),
+ .m_ctrlport_req_data (ctrlport_err_radio_req_data[i*32 +: 32]),
+ .m_ctrlport_req_has_time (ctrlport_err_radio_req_has_time[i]),
+ .m_ctrlport_req_time (ctrlport_err_radio_req_time[i*64 +: 64]),
+ .m_ctrlport_resp_ack (ctrlport_err_radio_resp_ack[i]),
+
+ // Tx Data Stream
+ .s_axis_tdata (axis_tx_tdata[RADIO_W*i +: RADIO_W]),
+ .s_axis_tlast (axis_tx_tlast[i]),
+ .s_axis_tvalid (axis_tx_tvalid[i]),
+ .s_axis_tready (axis_tx_tready[i]),
+ // Sideband Info
+ .s_axis_ttimestamp (axis_tx_ttimestamp[i*64 +: 64]),
+ .s_axis_thas_time (axis_tx_thas_time[i]),
+ .s_axis_teob (axis_tx_teob[i]),
+
+ // Rx Data Stream
+ .m_axis_tdata (axis_rx_tdata[RADIO_W*i +: RADIO_W]),
+ .m_axis_tlast (axis_rx_tlast[i]),
+ .m_axis_tvalid (axis_rx_tvalid[i]),
+ .m_axis_tready (axis_rx_tready[i]),
+ // Sideband Info
+ .m_axis_ttimestamp (axis_rx_ttimestamp[i*64 +: 64]),
+ .m_axis_thas_time (axis_rx_thas_time[i]),
+ .m_axis_teob (axis_rx_teob[i]),
+
+ // Radio Data
+ .radio_time (radio_time),
+ .radio_rx_data (radio_rx_data[(RADIO_W)*i +: (RADIO_W)]),
+ .radio_rx_stb (radio_rx_stb[i]),
+ .radio_rx_running (radio_rx_running[i]),
+ .radio_tx_data (radio_tx_data[(RADIO_W)*i +: (RADIO_W)]),
+ .radio_tx_stb (radio_tx_stb[i]),
+ .radio_tx_running (radio_tx_running[i])
+ );
+ end
+ endgenerate
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_all_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_all_tb.sv
new file mode 100644
index 000000000..ea99692bb
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_all_tb.sv
@@ -0,0 +1,68 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_radio_all_tb
+//
+// Description: This is the testbench for rfnoc_block_radio that instantiates
+// several variations of rfnoc_block_radio_tb to test different configurations.
+//
+
+
+module rfnoc_block_radio_all_tb;
+
+ timeunit 1ns;
+ timeprecision 1ps;
+
+ import PkgTestExec::*;
+
+
+ //---------------------------------------------------------------------------
+ // Test Definitions
+ //---------------------------------------------------------------------------
+
+ typedef struct {
+ int CHDR_W;
+ int ITEM_W;
+ int NIPC;
+ int NUM_PORTS;
+ int STALL_PROB;
+ int STB_PROB;
+ bit TEST_REGS;
+ } test_config_t;
+
+ localparam NUM_TESTS = 9;
+
+ localparam test_config_t test[NUM_TESTS] = '{
+ '{CHDR_W: 64, ITEM_W: 16, NIPC: 1, NUM_PORTS: 3, STALL_PROB: 10, STB_PROB: 100, TEST_REGS: 1 },
+ '{CHDR_W: 64, ITEM_W: 16, NIPC: 1, NUM_PORTS: 2, STALL_PROB: 25, STB_PROB: 80, TEST_REGS: 1 },
+ '{CHDR_W: 64, ITEM_W: 16, NIPC: 2, NUM_PORTS: 1, STALL_PROB: 25, STB_PROB: 80, TEST_REGS: 0 },
+ '{CHDR_W: 64, ITEM_W: 32, NIPC: 1, NUM_PORTS: 1, STALL_PROB: 25, STB_PROB: 80, TEST_REGS: 0 },
+ '{CHDR_W: 64, ITEM_W: 32, NIPC: 2, NUM_PORTS: 1, STALL_PROB: 10, STB_PROB: 80, TEST_REGS: 0 },
+ '{CHDR_W: 128, ITEM_W: 32, NIPC: 1, NUM_PORTS: 3, STALL_PROB: 10, STB_PROB: 100, TEST_REGS: 1 },
+ '{CHDR_W: 128, ITEM_W: 32, NIPC: 1, NUM_PORTS: 2, STALL_PROB: 25, STB_PROB: 80, TEST_REGS: 0 },
+ '{CHDR_W: 128, ITEM_W: 32, NIPC: 2, NUM_PORTS: 1, STALL_PROB: 25, STB_PROB: 80, TEST_REGS: 0 },
+ '{CHDR_W: 128, ITEM_W: 32, NIPC: 4, NUM_PORTS: 1, STALL_PROB: 10, STB_PROB: 80, TEST_REGS: 0 }
+ };
+
+
+ //---------------------------------------------------------------------------
+ // DUT Instances
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ for (i = 0; i < NUM_TESTS; i++) begin : gen_test_config
+ rfnoc_block_radio_tb #(
+ .CHDR_W (test[i].CHDR_W ),
+ .ITEM_W (test[i].ITEM_W ),
+ .NIPC (test[i].NIPC ),
+ .NUM_PORTS (test[i].NUM_PORTS ),
+ .STALL_PROB (test[i].STALL_PROB),
+ .STB_PROB (test[i].STB_PROB ),
+ .TEST_REGS (test[i].TEST_REGS )
+ ) rfnoc_block_radio_tb_i ();
+ end : gen_test_config
+
+
+endmodule : rfnoc_block_radio_all_tb
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_regs.vh
new file mode 100644
index 000000000..41f9a144e
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_regs.vh
@@ -0,0 +1,125 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_radio_regs (Header)
+//
+// Description: Header file for RFNoC radio functionality. This includes
+// register offsets, bitfields and constants for the radio components.
+//
+
+
+//-----------------------------------------------------------------------------
+// Shared Register Offsets (One Set Per Radio NoC Block)
+//-----------------------------------------------------------------------------
+
+localparam SHARED_BASE_ADDR = 20'h00; // Base address for shared radio registers
+localparam SHARED_ADDR_W = 4; // Address space size for shared registers
+
+localparam REG_COMPAT_NUM = 'h00; // Compatibility number register offset
+
+
+//-----------------------------------------------------------------------------
+// Radio Core Register Offsets (One Set Per Radio Port)
+//-----------------------------------------------------------------------------
+//
+// These registers are replicated depending on the number of radio channels
+// requested. They start at BASE_ADDR_RADIO and repeat every RADIO_ADDR_SPACE
+// bytes.
+//
+// WARNING: All registers larger than a single 32-bit word must be read and
+// written least significant word first to guarantee coherency.
+//
+//-----------------------------------------------------------------------------
+
+localparam RADIO_BASE_ADDR = 20'h1000; // Base address of first radio. Choose a
+ // nice big power of 2 so we can just pass
+ // the lower bits to the radio cores.
+localparam RADIO_ADDR_W = 7; // Address space size per radio
+
+// General Radio Registers
+localparam REG_LOOPBACK_EN = 'h00; // Loopback enable (connect Tx output to Rx input)
+localparam REG_RADIO_WIDTH = 'h04; // Upper 16 bits is sample width, lower 16 bits is NSPC
+
+// RX Control Registers
+localparam REG_RX_STATUS = 'h10; // Status of Rx radio
+localparam REG_RX_CMD = 'h14; // The next radio command to execute
+localparam REG_RX_CMD_NUM_WORDS_LO = 'h18; // Number of radio words for the next command (low word)
+localparam REG_RX_CMD_NUM_WORDS_HI = 'h1C; // Number of radio words for the next command (high word)
+localparam REG_RX_CMD_TIME_LO = 'h20; // Time for the next command (low word)
+localparam REG_RX_CMD_TIME_HI = 'h24; // Time for the next command (high word)
+localparam REG_RX_MAX_WORDS_PER_PKT = 'h28; // Maximum packet length to build from Rx data
+localparam REG_RX_ERR_PORT = 'h2C; // Port ID for error reporting
+localparam REG_RX_ERR_REM_PORT = 'h30; // Remote port ID for error reporting
+localparam REG_RX_ERR_REM_EPID = 'h34; // Remote EPID (endpoint ID) for error reporting
+localparam REG_RX_ERR_ADDR = 'h38; // Offset to write error code to
+localparam REG_RX_DATA = 'h3C; // Read the current Rx output of the radio
+localparam REG_RX_HAS_TIME = 'h70; // Controls whether or not a channel has timestamps
+
+// TX Control Registers
+localparam REG_TX_IDLE_VALUE = 'h40; // Value to output when transmitter is idle
+localparam REG_TX_ERROR_POLICY = 'h44; // Tx error policy
+localparam REG_TX_ERR_PORT = 'h48; // Port ID for error reporting
+localparam REG_TX_ERR_REM_PORT = 'h4C; // Remote port ID for error reporting
+localparam REG_TX_ERR_REM_EPID = 'h50; // Remote EPID (endpoint ID) for error reporting
+localparam REG_TX_ERR_ADDR = 'h54; // Offset to write error code to
+
+
+//-----------------------------------------------------------------------------
+// Register Bit Fields
+//-----------------------------------------------------------------------------
+
+// REG_RX_CMD bit fields
+localparam RX_CMD_POS = 0; // Location of the command bit field
+localparam RX_CMD_LEN = 2; // Bit length of the command bit field
+localparam RX_CMD_TIMED_POS = 31; // Location of the bit indicating if this is
+ // a timed command or not.
+
+// REG_RX_CMD_NUM_WORDS_HI/LO length field
+localparam RX_CMD_NUM_WORDS_LEN = 48; // Number of bits that are used in the 64-bit
+ // NUM_WORDS register (must be in range [33:64]).
+
+// REG_RX_STATUS bit fields
+localparam CMD_FIFO_SPACE_POS = 0; // Indicates if radio is busy executing a command.
+localparam CMD_FIFO_SPACE_LEN = 6; // Length of the FIFO_SPACE field
+localparam CMD_FIFO_SPACE_MAX = 32; // Size of command FIFO
+
+// REG_TX_ERROR_POLICY bit fields
+localparam TX_ERR_POLICY_LEN = 2; // Length of error policy bit field
+
+
+//-----------------------------------------------------------------------------
+// Rx Radio Commands
+//-----------------------------------------------------------------------------
+
+localparam [RX_CMD_LEN-1:0] RX_CMD_STOP = 0; // Stop acquiring at end of next packet
+localparam [RX_CMD_LEN-1:0] RX_CMD_FINITE = 1; // Acquire NUM_SAMPS then stop
+localparam [RX_CMD_LEN-1:0] RX_CMD_CONTINUOUS = 2; // Acquire until stopped
+
+
+//-----------------------------------------------------------------------------
+// Tx Error Policies
+//-----------------------------------------------------------------------------
+
+localparam TX_ERR_POLICY_PACKET = 1; // Wait for end of packet after error
+localparam TX_ERR_POLICY_BURST = 2; // Wait for end of burst after error
+
+
+//-----------------------------------------------------------------------------
+// Error Codes
+//-----------------------------------------------------------------------------
+
+// Rx Error Codes
+localparam ERR_RX_CODE_W = 2; // Bit width of error code values
+//
+localparam ERR_RX_LATE_CMD = 1; // Late command (arrived after indicated time)
+localparam ERR_RX_OVERRUN = 2; // FIFO overflow
+
+
+// Tx Error Codes
+localparam ERR_TX_CODE_W = 2; // Bit width of error code values
+//
+localparam ERR_TX_UNDERRUN = 1; // Data underflow (data not available when needed)
+localparam ERR_TX_LATE_DATA = 2; // Late data (arrived after indicated time)
+localparam ERR_TX_EOB_ACK = 3; // Acknowledge end-of-burst (this is not an error)
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_tb.sv
new file mode 100644
index 000000000..706e0f185
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_tb.sv
@@ -0,0 +1,1382 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_radio_tb
+//
+// Description: This is the testbench for rfnoc_block_radio.
+//
+
+
+module rfnoc_block_radio_tb #(
+ parameter int CHDR_W = 128, // CHDR bus width
+ parameter int ITEM_W = 32, // Sample width
+ parameter int NIPC = 2, // Number of samples per radio clock cycle
+ parameter int NUM_PORTS = 2, // Number of radio channels
+ parameter int STALL_PROB = 25, // Probability of AXI BFM stall
+ parameter int STB_PROB = 80, // Probability of radio STB asserting
+ parameter bit TEST_REGS = 1 // Do register tests
+);
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+ import PkgAxisCtrlBfm::*;
+ import PkgChdrBfm::*;
+ import PkgRfnocItemUtils::*;
+
+ // Pull in radio register offsets and constants
+ `include "rfnoc_block_radio_regs.vh"
+
+
+ // Simulation Parameters
+ localparam logic [ 9:0] THIS_PORTID = 10'h17;
+ localparam logic [15:0] THIS_EPID = 16'hDEAD;
+ localparam int MTU = 8;
+ localparam int RADIO_W = NIPC * ITEM_W; // Radio word size
+ localparam int SPP = 64; // Samples per packet
+ localparam int WPP = SPP*ITEM_W/RADIO_W; // Radio words per packet
+ localparam int CHDR_CLK_PER = 5; // rfnoc_chdr_clk period in ns
+ localparam int CTRL_CLK_PER = 25; // rfnoc_ctrl_clk period in ns
+ localparam int RADIO_CLK_PER = 10; // radio_clk_per period in ns
+
+ // Amount of time to wait for a packet to be fully acquired
+ localparam realtime MAX_PKT_WAIT = 4*WPP*(RADIO_CLK_PER+CTRL_CLK_PER)*1ns;
+
+ // Error reporting values to use
+ localparam bit [ 9:0] TX_ERR_DST_PORT = 10'h2B5;
+ localparam bit [ 9:0] TX_ERR_REM_DST_PORT = 10'h14C;
+ localparam bit [15:0] TX_ERR_REM_DST_EPID = 16'hA18E;
+ localparam bit [19:0] TX_ERR_ADDRESS = 20'hA31D3;
+
+
+
+ //---------------------------------------------------------------------------
+ // Clocks and Resets
+ //---------------------------------------------------------------------------
+
+ bit rfnoc_chdr_clk;
+ bit rfnoc_ctrl_clk;
+ bit radio_clk;
+
+ // Don't start the clocks automatically (AUTOSTART=0), since we expect
+ // multiple instances of this testbench to run in sequence. They will be
+ // started before the first test.
+ sim_clock_gen #(.PERIOD(CHDR_CLK_PER), .AUTOSTART(0))
+ rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
+ sim_clock_gen #(.PERIOD(CTRL_CLK_PER), .AUTOSTART(0))
+ rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst());
+ sim_clock_gen #(.PERIOD(RADIO_CLK_PER), .AUTOSTART(0))
+ radio_clk_gen (.clk(radio_clk), .rst());
+
+
+
+ //---------------------------------------------------------------------------
+ // Bus Functional Models
+ //---------------------------------------------------------------------------
+
+ // Connections to DUT as interfaces:
+ RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk);
+ AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+
+ // Bus functional model for a software block controller
+ RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl;
+
+
+
+ //---------------------------------------------------------------------------
+ // Radio Data Model
+ //---------------------------------------------------------------------------
+
+ bit [NUM_PORTS*RADIO_W-1:0] radio_rx_data;
+ bit [ NUM_PORTS-1:0] radio_rx_stb;
+
+ bit [63:0] radio_time;
+ bit radio_pps;
+
+ // Radio data generation
+ sim_radio_gen #(
+ .NSPC (NIPC),
+ .SAMP_W (ITEM_W),
+ .NUM_CHANNELS (NUM_PORTS),
+ .STB_PROB (STB_PROB),
+ .INCREMENT (NIPC),
+ .PPS_PERIOD (NIPC * 250)
+ ) radio_gen (
+ .radio_clk (radio_clk),
+ .radio_rst (1'b0),
+ .radio_rx_data (radio_rx_data),
+ .radio_rx_stb (radio_rx_stb),
+ .radio_time (radio_time),
+ .radio_pps (radio_pps)
+ );
+
+
+
+ //---------------------------------------------------------------------------
+ // DUT
+ //---------------------------------------------------------------------------
+
+ logic [NUM_PORTS-1:0] radio_rx_running;
+
+ logic [NUM_PORTS*RADIO_W-1:0] radio_tx_data;
+ logic [ NUM_PORTS-1:0] radio_tx_stb;
+ logic [ NUM_PORTS-1:0] radio_tx_running;
+
+ logic [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata_flat;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast_flat;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid_flat;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tready_flat;
+
+ logic [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata_flat;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast_flat;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid_flat;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tready_flat;
+
+ semaphore port_sem = new(0);
+
+ // Use the same strobe for both Rx and Tx
+ assign radio_tx_stb = radio_rx_stb;
+
+
+ // Flatten the data stream arrays into concatenated vectors
+ genvar i;
+ for (i = 0; i < NUM_PORTS; i++) begin : gen_radio_connections
+ assign s_rfnoc_chdr_tdata_flat[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata;
+ assign s_rfnoc_chdr_tlast_flat[i] = m_chdr[i].tlast;
+ assign s_rfnoc_chdr_tvalid_flat[i] = m_chdr[i].tvalid;
+ assign m_chdr[i].tready = s_rfnoc_chdr_tready_flat[i];
+
+ assign s_chdr[i].tdata = m_rfnoc_chdr_tdata_flat[CHDR_W*i+:CHDR_W];
+ assign s_chdr[i].tlast = m_rfnoc_chdr_tlast_flat[i];
+ assign s_chdr[i].tvalid = m_rfnoc_chdr_tvalid_flat[i];
+ assign m_rfnoc_chdr_tready_flat[i] = s_chdr[i].tready;
+
+ // Connect each interface to the BFM. This is done in a generate block
+ // since the interface indices must be constant in SystemVerilog :(
+ initial begin
+ // Get the port number (plus 1) from the semaphore. This will block until
+ // the semaphore is incremented to this port number (plus 1).
+ port_sem.get(i+1);
+ // Connect the master and slave interfaces to the BFM
+ void'(blk_ctrl.add_master_data_port(m_chdr[i]));
+ void'(blk_ctrl.add_slave_data_port(s_chdr[i]));
+ // Put the port number to communicate that we're done
+ port_sem.put(i+1);
+ end
+ end
+
+
+ rfnoc_block_radio #(
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .NIPC (NIPC),
+ .ITEM_W (ITEM_W),
+ .NUM_PORTS (NUM_PORTS),
+ .MTU (MTU)
+ ) rfnoc_block_radio_i (
+ .rfnoc_chdr_clk (backend.chdr_clk),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata_flat),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast_flat),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid_flat),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready_flat),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata_flat),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast_flat),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid_flat),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready_flat),
+ .rfnoc_core_config (backend.cfg),
+ .rfnoc_core_status (backend.sts),
+ .rfnoc_ctrl_clk (backend.ctrl_clk),
+ .s_rfnoc_ctrl_tdata (m_ctrl.tdata),
+ .s_rfnoc_ctrl_tlast (m_ctrl.tlast),
+ .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid),
+ .s_rfnoc_ctrl_tready (m_ctrl.tready),
+ .m_rfnoc_ctrl_tdata (s_ctrl.tdata),
+ .m_rfnoc_ctrl_tlast (s_ctrl.tlast),
+ .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid),
+ .m_rfnoc_ctrl_tready (s_ctrl.tready),
+ .m_ctrlport_req_wr (),
+ .m_ctrlport_req_rd (),
+ .m_ctrlport_req_addr (),
+ .m_ctrlport_req_data (),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (),
+ .m_ctrlport_req_time (),
+ .m_ctrlport_resp_ack (1'b0),
+ .m_ctrlport_resp_status (2'b0),
+ .m_ctrlport_resp_data (32'b0),
+ .radio_clk (radio_clk),
+ .radio_time (radio_time),
+ .radio_rx_data (radio_rx_data),
+ .radio_rx_stb (radio_rx_stb),
+ .radio_rx_running (radio_rx_running),
+ .radio_tx_data (radio_tx_data),
+ .radio_tx_stb (radio_tx_stb),
+ .radio_tx_running (radio_tx_running)
+ );
+
+
+
+ //---------------------------------------------------------------------------
+ // Helper Tasks
+ //---------------------------------------------------------------------------
+
+ // Read a 32-bit register at offset "addr" from shared radio registers
+ task automatic read_shared(logic [19:0] addr, output logic [31:0] data);
+ addr = addr + SHARED_BASE_ADDR;
+ blk_ctrl.reg_read(addr, data);
+ endtask : read_shared
+
+ // Write a 32-bit register at offset "addr" in shared radio registers
+ task automatic write_shared(logic [19:0] addr, logic [31:0] data);
+ addr = addr + SHARED_BASE_ADDR;
+ blk_ctrl.reg_write(addr, data);
+ endtask : write_shared
+
+ // Read a 32-bit register at offset "addr" from radio "radio_num"
+ task automatic read_radio(int radio_num, logic [19:0] addr, output logic [31:0] data);
+ addr = addr + RADIO_BASE_ADDR + (radio_num * 2**RADIO_ADDR_W);
+ blk_ctrl.reg_read(addr, data);
+ endtask : read_radio
+
+ // Read a 64-bit register at offset "addr" from radio "radio_num"
+ task automatic read_radio_64(int radio_num, logic [19:0] addr, output logic [63:0] data);
+ addr = addr + RADIO_BASE_ADDR + (radio_num * 2**RADIO_ADDR_W);
+ blk_ctrl.reg_read(addr, data[31:0]);
+ blk_ctrl.reg_read(addr+4, data[63:32]);
+ endtask : read_radio_64
+
+ // Write a 32-bit register at offset "addr" in radio "radio_num"
+ task automatic write_radio(int radio_num, logic [19:0] addr, logic [31:0] data);
+ addr = addr + RADIO_BASE_ADDR + (radio_num * 2**RADIO_ADDR_W);
+ blk_ctrl.reg_write(addr, data);
+ endtask : write_radio
+
+ // Write a 64-bit register at offset "addr" in radio "radio_num"
+ task automatic write_radio_64(int radio_num, logic [19:0] addr, logic [63:0] data);
+ addr = addr + RADIO_BASE_ADDR + (radio_num * 2**RADIO_ADDR_W);
+ blk_ctrl.reg_write(addr, data[31:0]);
+ blk_ctrl.reg_write(addr+4, data[63:32]);
+ endtask : write_radio_64
+
+
+ // Start an Rx acquisition
+ task automatic start_rx (
+ int radio_num, // Radio channel to use
+ bit [63:0] num_words = 0 // Number of radio words
+ );
+ logic [31:0] cmd;
+
+ if (num_words == 0) begin
+ // Do a continuous acquisition
+ $display("Radio %0d: Start RX, continuous receive", radio_num);
+ cmd = RX_CMD_CONTINUOUS;
+ end else begin
+ // Do a finite acquisition (num samps and done)
+ $display("Radio %0d: Start RX, receive %0d words", radio_num, num_words);
+ write_radio_64(radio_num, REG_RX_CMD_NUM_WORDS_LO, num_words);
+ cmd = RX_CMD_FINITE;
+ end
+
+ // Write command to radio
+ write_radio(radio_num, REG_RX_CMD, cmd);
+ endtask : start_rx
+
+
+ // Start an Rx acquisition at a specific time
+ task automatic start_rx_timed (
+ int radio_num, // Radio channel to use
+ bit [63:0] num_words = 0, // Number of radio words
+ bit [63:0] start_time
+ );
+ logic [31:0] cmd;
+
+ if (num_words == 0) begin
+ // Do a continuous acquisition
+ $display("Radio %0d: Start RX, continuous receive (timed)", radio_num);
+ cmd = RX_CMD_CONTINUOUS;
+ end else begin
+ // Do a finite acquisition (num samps and done)
+ $display("Radio %0d: Start RX, receive %0d words (timed)", radio_num, num_words);
+ write_radio_64(radio_num, REG_RX_CMD_NUM_WORDS_LO, num_words);
+ cmd = RX_CMD_FINITE;
+ end
+
+ // Mark that this is a timed command
+ cmd[RX_CMD_TIMED_POS] = 1'b1;
+
+ // Set start time for command
+ write_radio_64(radio_num, REG_RX_CMD_TIME_LO, start_time);
+
+ // Write command to radio
+ write_radio(radio_num, REG_RX_CMD, cmd);
+ endtask : start_rx_timed
+
+
+ // Send the Rx stop command to the indicated radio channel
+ task automatic stop_rx(int radio_num);
+ $display("Radio %0d: Stop RX", radio_num);
+ write_radio(radio_num, REG_RX_CMD, RX_CMD_STOP);
+ endtask : stop_rx
+
+
+ // Receive num_words from the indicated radio channel and verify that it's
+ // sequential and contiguous data aligned on packet boundaries.
+ task automatic check_rx(
+ int radio_num, // Radio to receive from and check
+ int num_words // Number of radio words to expect
+ );
+ int sample_count; // Counter to track number of samples generated
+ bit [ITEM_W-1:0] sample_val; // Value of the next sample
+ chdr_word_t data[$]; // Array of data for the received packet
+ int num_samples; // Number of samples to send
+ int byte_length; // Number of data bytes in next packet
+ int expected_length; // Expected byte length of the next packet
+ int valid_words; // Number of valid chdr_word_t in next packet
+
+ num_samples = num_words * NIPC;
+
+ sample_count = 0;
+ while (sample_count < num_samples) begin
+ // Fetch the next packet
+ blk_ctrl.recv(radio_num, data, byte_length);
+
+ // Take the first sample as a starting count for the remaining samples
+ if (sample_count == 0) begin
+ sample_val = data[0][ITEM_W-1:0];
+ end
+
+ // Calculate expected length in bytes
+ if (num_samples - sample_count >= SPP) begin
+ // Expecting a full packet
+ expected_length = SPP*ITEM_W/8;
+ end else begin
+ // Expecting partial packet
+ expected_length = (num_samples - sample_count) * ITEM_W/8;
+ end
+
+ // Check that the length matches our expectation
+ `ASSERT_ERROR(
+ byte_length == expected_length,
+ "Received packet didn't have expected length."
+ );
+
+ // Loop over the packet, one chdr_word_t at a time
+ valid_words = int'($ceil(real'(byte_length) / ($bits(chdr_word_t)/8)));
+ for (int i = 0; i < valid_words; i++) begin
+ // Check each sample of the next chdr_word_t value
+ for (int sub_sample = 0; sub_sample < $bits(chdr_word_t)/ITEM_W; sub_sample++) begin
+ chdr_word_t word;
+ word = data[i][ITEM_W*sub_sample +: ITEM_W]; // Work around Vivado 2018.3 issue
+ `ASSERT_ERROR(
+ word == sample_val,
+ $sformatf(
+ "Sample %0d (0x%X) didn't match expected value (0x%X)",
+ sample_count, data[i][ITEM_W*sub_sample +: ITEM_W], sample_val
+ )
+ );
+ sample_val++;
+ sample_count++;
+
+ // Check if the word is only partially full
+ if (sample_count >= num_samples) break;
+ end
+ end
+ end
+ endtask : check_rx
+
+
+ // Send num_words to the indicated radio for transmission at the given time.
+ task automatic start_tx_timed (
+ int radio_num, // Radio channel to transmit on
+ bit [63:0] num_words, // Number of radio words to transmit
+ logic [63:0] start_time = 'X, // Time at which to begin transmit
+ bit [ITEM_W-1:0] start_val = 1, // Initial sample value
+ bit eob = 1 // Set EOB flag at the end
+ );
+ int sample_count; // Counter to track number of samples generated
+ bit [ITEM_W-1:0] sample_val; // Value of the next sample
+ chdr_word_t data[$]; // Array of data for the packet
+ int num_samples; // Number of samples to send
+ int byte_length; // Number of bytes for next packet
+ chdr_word_t chdr_word; // Next word to send to BFM
+ packet_info_t pkt_info = 0; // Flags/timestamp for next packet
+
+ $display("Radio %0d: Start TX, send %0d words", radio_num, num_words);
+
+ num_samples = num_words * NIPC;
+
+ if (!$isunknown(start_time)) pkt_info.has_time = 1;
+
+ sample_val = start_val;
+ sample_count = 0;
+ while (sample_count < num_samples) begin
+ // Calculate timestamp for this packet
+ if (pkt_info.has_time) begin
+ pkt_info.timestamp = start_time + sample_count;
+ end
+
+ // Clear the payload
+ data = {};
+
+ // Loop until we've built up a packet
+ forever begin
+ // Generate the next word
+ for (int sub_sample = 0; sub_sample < $bits(chdr_word_t)/ITEM_W; sub_sample++) begin
+ chdr_word[ITEM_W*sub_sample +: ITEM_W] = sample_val;
+ sample_val++;
+ sample_count++;
+ end
+
+ // Add next word to the queue
+ data.push_back(chdr_word);
+
+ // Send the packet if we're at a packet boundary
+ if (sample_count % SPP == 0) begin
+ pkt_info.eob = (sample_count == num_samples && eob) ? 1 : 0;
+ byte_length = SPP * ITEM_W/8;
+ blk_ctrl.send(radio_num, data, byte_length, {}, pkt_info);
+ break;
+ end else if (sample_count >= num_samples) begin
+ pkt_info.eob = eob;
+ byte_length = (sample_count % SPP) * ITEM_W/8;
+ blk_ctrl.send(radio_num, data, byte_length, {}, pkt_info);
+ break;
+ end
+ end
+ end
+ endtask : start_tx_timed
+
+
+ // Send num_words to the indicated radio for transmission.
+ task automatic start_tx (
+ int radio_num, // Radio channel to transmit on
+ bit [63:0] num_words, // Number of radio words to transmit
+ bit [ITEM_W-1:0] start_val = 1, // Initial sample value
+ bit eob = 1 // Set EOB flag at the end
+ );
+ // Passing 'X tells the underlying BFM to not insert a timestamp
+ start_tx_timed(radio_num, num_words, 'X, start_val, eob);
+ endtask : start_tx
+
+
+ // Verify the output of a packet, expecting it at a specific time
+ task automatic check_tx_timed (
+ int radio_num, // Radio channel to transmit on
+ bit [63:0] num_words, // Number of radio words to expect
+ logic [63:0] start_time = 'X, // Expected start time
+ bit [ITEM_W-1:0] start_val = 1 // Initial sample value
+ );
+ int sample_val; // Expected value of next sample
+
+ sample_val = start_val;
+
+ // Wait for the packet to start
+ wait(radio_tx_data[radio_num*RADIO_W +: ITEM_W] == start_val);
+
+ // Check the time
+ if (!$isunknown(start_time)) begin
+ `ASSERT_ERROR(
+ radio_time - start_time <= NIPC*2,
+ $sformatf("Packet transmitted at radio time 0x%0X but expected 0x%0X", radio_time, start_time)
+ );
+ end
+
+ // Verify output one word at a time
+ for (int word_count = 0; word_count < num_words; word_count++) begin
+ // Wait for the next radio word to be output
+ do begin
+ @(posedge radio_clk);
+ end while (radio_tx_stb[radio_num] == 0);
+
+ // Check each sample of the radio word
+ for (int sub_sample = 0; sub_sample < NIPC; sub_sample++) begin
+ `ASSERT_ERROR(
+ radio_tx_data[radio_num*RADIO_W + ITEM_W*sub_sample +: ITEM_W] == sample_val,
+ "Radio output doesn't match expected value"
+ );
+ sample_val++;
+ end
+ end
+ endtask : check_tx_timed
+
+
+ // Verify the output of a packet
+ task automatic check_tx (
+ int radio_num, // Radio to transmit on
+ bit [63:0] num_words, // Number of radio words to expect
+ bit [ITEM_W-1:0] start_val = 1 // Initial sample value
+ );
+ check_tx_timed(radio_num, num_words, 'X, start_val);
+ endtask : check_tx
+
+
+ // When we expect and error, this task will check that control packets were
+ // received and that they have the expected values.
+ task check_error (int error);
+ AxisCtrlPacket ctrl_packet;
+ chdr_word_t word;
+
+ // Get error code
+ blk_ctrl.get_ctrl_bfm().get_ctrl(ctrl_packet);
+ word = ctrl_packet.data[0]; // Work around Vivado 2018.3 issue
+ `ASSERT_ERROR(
+ word == error &&
+ ctrl_packet.op_word.op_code == CTRL_OP_WRITE &&
+ ctrl_packet.op_word.address == TX_ERR_ADDRESS &&
+ ctrl_packet.header.is_ack == 1'b0 &&
+ ctrl_packet.header.has_time == 1'b1 &&
+ ctrl_packet.header.num_data == 1 &&
+ ctrl_packet.header.dst_port == TX_ERR_DST_PORT &&
+ ctrl_packet.header.rem_dst_port == TX_ERR_REM_DST_PORT &&
+ ctrl_packet.header.rem_dst_epid == TX_ERR_REM_DST_EPID,
+ "Unexpected error code response");
+
+ // Send acknowledgment
+ ctrl_packet.header = 0;
+ ctrl_packet.header.is_ack = 1;
+ blk_ctrl.get_ctrl_bfm().put_ctrl(ctrl_packet);
+ endtask : check_error
+
+
+
+ //---------------------------------------------------------------------------
+ // Test Procedures
+ //---------------------------------------------------------------------------
+
+ task automatic test_block_info();
+ test.start_test("Verify Block Info", 2us);
+
+ // Get static block info and validate it
+ `ASSERT_ERROR(blk_ctrl.get_noc_id() == rfnoc_block_radio_i.NOC_ID, "Incorrect noc_id Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect num_data_i Value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect num_data_o Value");
+ `ASSERT_ERROR(blk_ctrl.get_ctrl_fifosize() == rfnoc_block_radio_i.noc_shell_radio_i.CTRL_FIFO_SIZE,
+ "Incorrect ctrl_fifosize Value");
+ `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect mtu Value");
+
+ test.end_test();
+ endtask : test_block_info
+
+
+
+ task automatic test_shared_registers();
+ logic [31:0] val;
+ test.start_test("Shared Registers", 10us);
+
+ // Compatibility number
+ read_shared(REG_COMPAT_NUM, val);
+ `ASSERT_ERROR(
+ val == {
+ rfnoc_block_radio_i.compat_major,
+ rfnoc_block_radio_i.compat_minor
+ },
+ "REG_COMPAT_NUM didn't read correctly"
+ );
+ test.end_test();
+ endtask : test_shared_registers
+
+
+
+ task automatic test_general_registers(int radio_num);
+ logic [31:0] val;
+ test.start_test("General Registers", 10us);
+
+ // Test loopback enable register (read/write)
+ read_radio(radio_num, REG_LOOPBACK_EN, val);
+ `ASSERT_ERROR(val == 0, "Initial value of REG_LOOPBACK_EN is incorrect");
+ write_radio(radio_num, REG_LOOPBACK_EN, 32'hFFFFFFFF);
+ read_radio(radio_num, REG_LOOPBACK_EN, val);
+ `ASSERT_ERROR(val == 1, "REG_LOOPBACK_EN didn't update correctly");
+ write_radio(radio_num, REG_LOOPBACK_EN, 0);
+
+ // Read ITEM_W and NIPC (read only)
+ read_radio(radio_num, REG_RADIO_WIDTH, val);
+ `ASSERT_ERROR(val[15:0] == NIPC, "Value of NIPC register is incorrect");
+ `ASSERT_ERROR(val[31:16] == ITEM_W, "Value of ITEM_W register is incorrect");
+
+ test.end_test();
+ endtask : test_general_registers
+
+
+
+ task test_rx_registers(int radio_num);
+ logic [63:0] val, temp, expected;
+ localparam int num_words_len = RX_CMD_NUM_WORDS_LEN;
+
+ test.start_test("Rx Registers", 50us);
+
+ // REG_RX_CMD_STATUS (read only)
+ expected = CMD_FIFO_SPACE_MAX;
+ read_radio(radio_num, REG_RX_STATUS, val);
+ `ASSERT_ERROR(val == expected, "REG_RX_STATUS not initially CMD_FIFO_SPACE_MAX");
+
+ // REG_RX_CMD (read/write). Test a bogus timed stop command just to check
+ // read/write of the register.
+ expected = 0;
+ expected[RX_CMD_POS +: RX_CMD_LEN] = RX_CMD_STOP;
+ expected[RX_CMD_TIMED_POS] = 1'b1;
+ write_radio(radio_num, REG_RX_CMD, expected);
+ read_radio(radio_num, REG_RX_CMD, val);
+ `ASSERT_ERROR(val == expected, "REG_RX_CMD didn't update correctly");
+
+ // REG_RX_CMD_NUM_WORDS (read/write)
+ read_radio_64(radio_num, REG_RX_CMD_NUM_WORDS_LO, val);
+ `ASSERT_ERROR(val == 0, "REG_RX_CMD_NUM_WORDS not initially 0");
+ expected = 64'hFEDCBA9876543210;
+ write_radio_64(radio_num, REG_RX_CMD_NUM_WORDS_LO, expected);
+ read_radio_64(radio_num, REG_RX_CMD_NUM_WORDS_LO, val);
+ `ASSERT_ERROR(
+ val == expected[num_words_len-1:0],
+ "REG_RX_CMD_NUM_WORDS didn't update correctly"
+ );
+
+ // REG_RX_CMD_TIME (read/write)
+ read_radio_64(radio_num, REG_RX_CMD_TIME_LO, val);
+ `ASSERT_ERROR(val == 0, "REG_RX_CMD_TIME not initially 0");
+ expected = 64'hBEADFEED0123F1FE;
+ write_radio_64(radio_num, REG_RX_CMD_TIME_LO, expected);
+ read_radio_64(radio_num, REG_RX_CMD_TIME_LO, val);
+ `ASSERT_ERROR(val == expected, "REG_RX_CMD_TIME didn't update correctly");
+
+ // REG_RX_MAX_WORDS_PER_PKT (read/write)
+ read_radio(radio_num, REG_RX_MAX_WORDS_PER_PKT, val);
+ `ASSERT_ERROR(val == 64, "REG_RX_MAX_WORDS_PER_PKT not initially 64");
+ expected = 32'hABBEC001;
+ write_radio(radio_num, REG_RX_MAX_WORDS_PER_PKT, expected);
+ read_radio(radio_num, REG_RX_MAX_WORDS_PER_PKT, val);
+ `ASSERT_ERROR(val == expected, "REG_RX_MAX_WORDS_PER_PKT didn't update correctly");
+
+ // REG_RX_ERR_PORT (read/write)
+ read_radio(radio_num, REG_RX_ERR_PORT, val);
+ `ASSERT_ERROR(val == 0, "REG_RX_ERR_PORT not initially 0");
+ expected = $urandom() & 32'h000001FF;
+ write_radio(radio_num, REG_RX_ERR_PORT, expected);
+ read_radio(radio_num, REG_RX_ERR_PORT, val);
+ `ASSERT_ERROR(val == expected, "REG_RX_ERR_PORT didn't update correctly");
+
+ // REG_RX_ERR_REM_PORT (read/write)
+ read_radio(radio_num, REG_RX_ERR_REM_PORT, val);
+ `ASSERT_ERROR(val == 0, "REG_RX_ERR_REM_PORT not initially 0");
+ expected = $urandom() & 32'h000001FF;
+ write_radio(radio_num, REG_RX_ERR_REM_PORT, expected);
+ read_radio(radio_num, REG_RX_ERR_REM_PORT, val);
+ `ASSERT_ERROR(val == expected, "REG_RX_ERR_REM_PORT didn't update correctly");
+
+ // REG_RX_ERR_REM_EPID (read/write)
+ read_radio(radio_num, REG_RX_ERR_REM_EPID, val);
+ `ASSERT_ERROR(val == 0, "REG_RX_ERR_REM_EPID not initially 0");
+ expected = $urandom() & 32'h0000FFFF;
+ write_radio(radio_num, REG_RX_ERR_REM_EPID, expected);
+ read_radio(radio_num, REG_RX_ERR_REM_EPID, val);
+ `ASSERT_ERROR(val == expected, "REG_RX_ERR_REM_EPID didn't update correctly");
+
+ // REG_RX_ERR_ADDR (read/write)
+ read_radio(radio_num, REG_RX_ERR_ADDR, val);
+ `ASSERT_ERROR(val == 0, "REG_RX_ERR_ADDR not initially 0");
+ expected = $urandom() & 32'h000FFFFF;
+ write_radio(radio_num, REG_RX_ERR_ADDR, expected);
+ read_radio(radio_num, REG_RX_ERR_ADDR, val);
+ `ASSERT_ERROR(val == expected, "REG_RX_ERR_ADDR didn't update correctly");
+
+ // REG_RX_DATA (read-only)
+ temp = radio_tx_data[RADIO_W*radio_num +: RADIO_W];
+ read_radio(radio_num, REG_RX_DATA, val);
+ `ASSERT_ERROR(
+ radio_rx_data[RADIO_W*radio_num +: RADIO_W] >= val && val >= temp,
+ "REG_RX_DATA wasn't in the expected range");
+ read_radio(radio_num, REG_RX_DATA, temp);
+ `ASSERT_ERROR(temp != val, "REG_RX_DATA didn't update");
+
+ test.end_test();
+ endtask : test_rx_registers
+
+
+
+ task automatic test_tx_registers(int radio_num);
+ logic [31:0] val, expected;
+
+ test.start_test("Tx Registers", 50us);
+
+ // REG_TX_IDLE_VALUE (read/write)
+ read_radio(radio_num, REG_TX_IDLE_VALUE, val);
+ `ASSERT_ERROR(val == 0, "REG_TX_IDLE_VALUE not initially 0");
+ expected = $urandom() & {ITEM_W{1'b1}};
+ write_radio(radio_num, REG_TX_IDLE_VALUE, expected);
+ read_radio(radio_num, REG_TX_IDLE_VALUE, val);
+ `ASSERT_ERROR(val == expected, "REG_TX_IDLE_VALUE didn't update correctly");
+
+ // REG_TX_ERROR_POLICY (read/write)
+ read_radio(radio_num, REG_TX_ERROR_POLICY, val);
+ expected = TX_ERR_POLICY_PACKET;
+ `ASSERT_ERROR(val == expected, "REG_TX_ERROR_POLICY not initially 'PACKET'");
+ expected = TX_ERR_POLICY_BURST;
+ write_radio(radio_num, REG_TX_ERROR_POLICY, expected);
+ read_radio(radio_num, REG_TX_ERROR_POLICY, val);
+ `ASSERT_ERROR(val == expected, "REG_TX_ERROR_POLICY didn't update to 'BURST'");
+ expected = TX_ERR_POLICY_PACKET;
+ write_radio(radio_num, REG_TX_ERROR_POLICY, 32'h03); // Try to set both bits!
+ read_radio(radio_num, REG_TX_ERROR_POLICY, val);
+ `ASSERT_ERROR(val == expected, "REG_TX_ERROR_POLICY didn't revert to 'PACKET'");
+
+ // REG_TX_ERR_PORT (read/write)
+ read_radio(radio_num, REG_TX_ERR_PORT, val);
+ `ASSERT_ERROR(val == 0, "REG_TX_ERR_PORT not initially 0");
+ expected = $urandom() & 32'h000001FF;
+ write_radio(radio_num, REG_TX_ERR_PORT, expected);
+ read_radio(radio_num, REG_TX_ERR_PORT, val);
+ `ASSERT_ERROR(val == expected, "REG_TX_ERR_PORT didn't update correctly");
+
+ // REG_TX_ERR_REM_PORT (read/write)
+ read_radio(radio_num, REG_TX_ERR_REM_PORT, val);
+ `ASSERT_ERROR(val == 0, "REG_TX_ERR_REM_PORT not initially 0");
+ expected = $urandom() & 32'h000001FF;
+ write_radio(radio_num, REG_TX_ERR_REM_PORT, expected);
+ read_radio(radio_num, REG_TX_ERR_REM_PORT, val);
+ `ASSERT_ERROR(val == expected, "REG_TX_ERR_REM_PORT didn't update correctly");
+
+ // REG_TX_ERR_REM_EPID (read/write)
+ read_radio(radio_num, REG_TX_ERR_REM_EPID, val);
+ `ASSERT_ERROR(val == 0, "REG_TX_ERR_REM_EPID not initially 0");
+ expected = $urandom() & 32'h0000FFFF;
+ write_radio(radio_num, REG_TX_ERR_REM_EPID, expected);
+ read_radio(radio_num, REG_TX_ERR_REM_EPID, val);
+ `ASSERT_ERROR(val == expected, "REG_TX_ERR_REM_EPID didn't update correctly");
+
+ // REG_TX_ERR_ADDR (read/write)
+ read_radio(radio_num, REG_TX_ERR_ADDR, val);
+ `ASSERT_ERROR(val == 0, "REG_TX_ERR_ADDR not initially 0");
+ expected = $urandom() & 32'h000FFFFF;
+ write_radio(radio_num, REG_TX_ERR_ADDR, expected);
+ read_radio(radio_num, REG_TX_ERR_ADDR, val);
+ `ASSERT_ERROR(val == expected, "REG_TX_ERR_ADDR didn't update correctly");
+
+ test.end_test();
+ endtask : test_tx_registers
+
+
+
+ task automatic test_rx(int radio_num);
+
+ //---------------------
+ // Finite Acquisitions
+ //---------------------
+
+ test.start_test("Rx (finite)", 50us);
+
+ // Set packet length
+ write_radio(radio_num, REG_RX_MAX_WORDS_PER_PKT, WPP);
+
+ // Grab and verify a partial packet
+ start_rx(radio_num, WPP/2);
+ check_rx(radio_num, WPP/2);
+
+ // Grab a minimally-sized packet
+ start_rx(radio_num, 1);
+ check_rx(radio_num, 1);
+
+ // Grab and verify several packets
+ start_rx(radio_num, WPP*15/2);
+ check_rx(radio_num, WPP*15/2);
+
+ // Wait long enough to receive another packet and then make sure we didn't
+ // receive anything. That is, make sure Rx actually stopped.
+ #MAX_PKT_WAIT;
+ `ASSERT_ERROR(
+ blk_ctrl.num_received(radio_num) == 0,
+ "Received more packets than expected"
+ );
+
+ test.end_test();
+
+
+ //-------------------------
+ // Continuous Acquisitions
+ //-------------------------
+
+ test.start_test("Rx (continuous)", 100us);
+
+ start_rx(radio_num);
+
+ // Grab and verify several packets
+ check_rx(radio_num, WPP*7);
+ stop_rx(radio_num);
+
+ // Grab and discard any remaining packets
+ do begin
+ while (blk_ctrl.num_received(radio_num) != 0) begin
+ ChdrPacket #(CHDR_W) chdr_packet;
+ blk_ctrl.get_chdr(radio_num, chdr_packet);
+ end
+ #MAX_PKT_WAIT;
+ end while (blk_ctrl.num_received(radio_num) != 0);
+
+ test.end_test();
+
+
+ //--------------------------
+ // Finite Timed Acquisition
+ //--------------------------
+
+ begin
+ ChdrPacket #(CHDR_W) chdr_packet;
+ chdr_word_t expected_time;
+
+ test.start_test("Rx (finite, timed)", 100us);
+
+ // Send Rx command with time in the future
+ expected_time = radio_time + 2000;
+ start_rx_timed(radio_num, WPP, expected_time);
+
+ // Take a peak at the timestamp in the received packet to check it
+ blk_ctrl.peek_chdr(radio_num, chdr_packet);
+ `ASSERT_ERROR(
+ chdr_packet.timestamp == expected_time,
+ "Received packet didn't have expected timestamp"
+ );
+
+ // Verify the packet data
+ check_rx(radio_num, WPP);
+ test.end_test();
+ end
+
+
+ //------------------------------
+ // Continuous Timed Acquisition
+ //------------------------------
+
+ begin
+ ChdrPacket #(CHDR_W) chdr_packet;
+ chdr_word_t expected_time;
+
+ test.start_test("Rx (continuous, timed)", 100us);
+
+ // Send Rx command with time in the future
+ expected_time = radio_time + 2000;
+ start_rx_timed(radio_num, 0, expected_time);
+
+ // Take a peak at the timestamp in the received packet to check it
+ blk_ctrl.peek_chdr(radio_num, chdr_packet);
+ `ASSERT_ERROR(
+ chdr_packet.timestamp == expected_time,
+ "Received packet didn't have expected timestamp"
+ );
+
+ // Verify a few packets
+ check_rx(radio_num, WPP*3);
+ stop_rx(radio_num);
+
+ // Grab and discard any remaining packets
+ do begin
+ while (blk_ctrl.num_received(radio_num) != 0) begin
+ ChdrPacket #(CHDR_W) chdr_packet;
+ blk_ctrl.get_chdr(radio_num, chdr_packet);
+ end
+ #(MAX_PKT_WAIT);
+ end while (blk_ctrl.num_received(radio_num) != 0);
+
+ test.end_test();
+ end
+
+
+ //-------------
+ // Rx Overflow
+ //-------------
+ begin
+ logic [31:0] val;
+
+ test.start_test("Rx (now, overflow)", 200us);
+
+ // Configure the error reporting registers
+ write_radio(radio_num, REG_RX_ERR_PORT, TX_ERR_DST_PORT);
+ write_radio(radio_num, REG_RX_ERR_REM_PORT, TX_ERR_REM_DST_PORT);
+ write_radio(radio_num, REG_RX_ERR_REM_EPID, TX_ERR_REM_DST_EPID);
+ write_radio(radio_num, REG_RX_ERR_ADDR, TX_ERR_ADDRESS);
+
+ // Stall the BFM to force a backup of data
+ blk_ctrl.set_slave_stall_prob(radio_num, 100);
+
+ // Acquire continuously until we get an error
+ start_rx(radio_num);
+
+ // Check that we're acquiring
+ read_radio(radio_num, REG_RX_STATUS, val);
+ `ASSERT_ERROR(
+ val[CMD_FIFO_SPACE_POS +: CMD_FIFO_SPACE_LEN] != CMD_FIFO_SPACE_MAX,
+ "Rx radio reports that it is not busy"
+ );
+
+ // Verify that we receive an error
+ check_error(ERR_RX_OVERRUN);
+
+ // Restore the BFM stall probability
+ blk_ctrl.set_slave_stall_prob(radio_num, STALL_PROB);
+
+ // Verify that Rx stopped
+ read_radio(radio_num, REG_RX_STATUS, val);
+ `ASSERT_ERROR(
+ val[CMD_FIFO_SPACE_POS +: CMD_FIFO_SPACE_LEN] == CMD_FIFO_SPACE_MAX,
+ "Rx radio reports that it is still busy after overflow"
+ );
+
+ // Discard any packets we received. Rx should eventually stop
+ // automatically after an overflow.
+ do begin
+ while (blk_ctrl.num_received(radio_num) != 0) begin
+ ChdrPacket #(CHDR_W) chdr_packet;
+ blk_ctrl.get_chdr(radio_num, chdr_packet);
+ end
+ #(MAX_PKT_WAIT);
+ end while (blk_ctrl.num_received(radio_num) != 0);
+
+ test.end_test();
+ end
+
+
+ //--------------
+ // Late Command
+ //--------------
+
+ test.start_test("Rx (timed, late)", 100us);
+
+ start_rx_timed(radio_num, WPP, radio_time);
+ check_error(ERR_RX_LATE_CMD);
+
+ // Late command should be ignored. Make sure we didn't receive any packets.
+ begin
+ ChdrPacket #(CHDR_W) chdr_packet;
+ #(MAX_PKT_WAIT);
+ `ASSERT_ERROR(
+ blk_ctrl.num_received(radio_num) == 0,
+ "Packets received for late Rx command"
+ );
+
+ // Discard any remaining packets
+ while (blk_ctrl.num_received(radio_num)) blk_ctrl.get_chdr(radio_num, chdr_packet);
+ end
+
+ test.end_test();
+
+
+ //---------------
+ // Command Queue
+ //---------------
+
+ test.start_test("Rx (queue multiple commands)");
+
+ begin
+ logic [31:0] expected, val;
+
+ // Send one continuous command and verify the queue fullness
+ start_rx(radio_num);
+ expected = CMD_FIFO_SPACE_MAX-1;
+ read_radio(radio_num, REG_RX_STATUS, val);
+ `ASSERT_ERROR(
+ val[CMD_FIFO_SPACE_POS+:CMD_FIFO_SPACE_LEN] == expected,
+ "CMD_FIFO_SPACE did not decrement"
+ );
+
+ // Fill the command FIFO, going one over
+ for (int i = 0; i < CMD_FIFO_SPACE_MAX; i++) begin
+ start_rx(radio_num, WPP);
+ end
+ expected = 0;
+ read_radio(radio_num, REG_RX_STATUS, val);
+ `ASSERT_ERROR(
+ val[CMD_FIFO_SPACE_POS+:CMD_FIFO_SPACE_LEN] == expected,
+ "CMD_FIFO_SPACE did not reach 0"
+ );
+
+ // Issue stop command and verify that the FIFO empties
+ stop_rx(radio_num);
+ expected = CMD_FIFO_SPACE_MAX;
+ read_radio(radio_num, REG_RX_STATUS, val);
+ `ASSERT_ERROR(
+ val[CMD_FIFO_SPACE_POS+:CMD_FIFO_SPACE_LEN] == expected,
+ "CMD_FIFO_SPACE did not return to max"
+ );
+
+ // Grab and discard any remaining packets
+ do begin
+ while (blk_ctrl.num_received(radio_num) != 0) begin
+ ChdrPacket #(CHDR_W) chdr_packet;
+ blk_ctrl.get_chdr(radio_num, chdr_packet);
+ end
+ #MAX_PKT_WAIT;
+ end while (blk_ctrl.num_received(radio_num) != 0);
+
+ // Queue several long commands back-to-back and make sure they all
+ // complete. The lengths are unique to ensure we execute the right
+ // commands in the expected order.
+ for (int i = 0; i < 3; i++) start_rx(radio_num, WPP*20+i);
+ for (int i = 0; i < 3; i++) check_rx(radio_num, WPP*20+i);
+
+ // Make sure we don't get any more data
+ do begin
+ while (blk_ctrl.num_received(radio_num) != 0) begin
+ `ASSERT_ERROR(0, "Received unexpected packets");
+ end
+ #MAX_PKT_WAIT;
+ end while (blk_ctrl.num_received(radio_num) != 0);
+ end
+
+ test.end_test();
+
+ endtask : test_rx
+
+
+
+ task automatic test_tx(int radio_num);
+ logic [RADIO_W-1:0] radio_data;
+ enum { WAIT_FOR_EOP, WAIT_FOR_EOB } policy;
+
+ //-------
+ // Setup
+ //-------
+
+ test.start_test("Tx Init", 50us);
+
+ // Configure the error reporting registers
+ write_radio(radio_num, REG_TX_ERR_PORT, TX_ERR_DST_PORT);
+ write_radio(radio_num, REG_TX_ERR_REM_PORT, TX_ERR_REM_DST_PORT);
+ write_radio(radio_num, REG_TX_ERR_REM_EPID, TX_ERR_REM_DST_EPID);
+ write_radio(radio_num, REG_TX_ERR_ADDR, TX_ERR_ADDRESS);
+
+ test.end_test();
+
+
+ //---------------
+ // Test Tx (now)
+ //---------------
+
+ test.start_test("Tx (now)", 50us);
+
+ // Grab and verify a partial packet
+ start_tx(radio_num, WPP*3/4);
+ check_tx(radio_num, WPP*3/4);
+ check_error(ERR_TX_EOB_ACK);
+
+ // Grab and verify multiple packets
+ start_tx(radio_num, WPP*3/2);
+ check_tx(radio_num, WPP*3/2);
+ check_error(ERR_TX_EOB_ACK);
+
+ // Test a minimally-sized packet
+ start_tx(radio_num, 1);
+ check_tx(radio_num, 1);
+ check_error(ERR_TX_EOB_ACK);
+
+ test.end_test();
+
+
+ //---------------------
+ // Test Tx (underflow)
+ //---------------------
+
+ test.start_test("Tx (now, underflow)", 50us);
+
+ // Send some bursts without EOB
+ start_tx(radio_num, WPP*3/4, 1, 0); // Skip EOB
+ check_tx(radio_num, WPP*3/4);
+ check_error(ERR_TX_UNDERRUN);
+
+ start_tx(radio_num, WPP*2, 1, 0); // Skip EOB
+ check_tx(radio_num, WPP*2);
+ check_error(ERR_TX_UNDERRUN);
+
+ test.end_test();
+
+
+ //-----------------
+ // Test Tx (timed)
+ //-----------------
+
+ test.start_test("Tx (timed)", 50us);
+
+ // Grab and verify a partial packet
+ start_tx_timed(radio_num, WPP*3/4, radio_time + 200);
+ check_tx_timed(radio_num, WPP*3/4, radio_time + 200);
+ check_error(ERR_TX_EOB_ACK);
+
+ // Grab and verify whole packets
+ start_tx_timed(radio_num, WPP*2, radio_time + 200);
+ check_tx_timed(radio_num, WPP*2, radio_time + 200);
+ check_error(ERR_TX_EOB_ACK);
+
+ test.end_test();
+
+
+ //-----------------
+ // Test Tx (timed, underflow)
+ //-----------------
+
+ test.start_test("Tx (timed, underflow)", 50us);
+
+ // Send some bursts without EOB
+ start_tx_timed(radio_num, WPP*3/4, radio_time + 200, 1, 0);
+ check_tx_timed(radio_num, WPP*3/4, radio_time + 200);
+ check_error(ERR_TX_UNDERRUN);
+
+ start_tx_timed(radio_num, WPP*2, radio_time + 200, 1, 0);
+ check_tx_timed(radio_num, WPP*2, radio_time + 200);
+ check_error(ERR_TX_UNDERRUN);
+
+ test.end_test();
+
+
+ //---------------------------
+ // Test Tx (timed, late)
+ //---------------------------
+
+ test.start_test("Tx (timed, late)", 50us);
+
+ // Test each error policy
+ policy = policy.first();
+ do begin
+ // Set the policy
+ if (policy == WAIT_FOR_EOP) begin
+ write_radio(radio_num, REG_TX_ERROR_POLICY, TX_ERR_POLICY_PACKET);
+ end else if (policy == WAIT_FOR_EOB) begin
+ write_radio(radio_num, REG_TX_ERROR_POLICY, TX_ERR_POLICY_BURST);
+ end
+
+// Commenting out the fork code for now due to Vivado 2018.3 bug.
+// radio_data = radio_tx_data[radio_num];
+// fork : tx_fork
+ // In this branch of the fork, we send the packets
+ repeat (2) begin
+ // Send late packets with random start value
+ start_tx_timed(radio_num, WPP*3, 0, $urandom());
+
+ if (policy == WAIT_FOR_EOP) begin
+ // We should get three errors, one for each packet
+ repeat (3) check_error(ERR_TX_LATE_DATA);
+ end else if (policy == WAIT_FOR_EOB) begin
+ // We should get one error for the entire burst
+ check_error(ERR_TX_LATE_DATA);
+ end
+ end
+
+// // The packets sent in the above branch of the fork should be
+// // dropped. In this branch of the fork we make sure that the Tx
+// // output doesn't change.
+// begin
+// forever begin
+// @(posedge radio_clk)
+// `ASSERT_ERROR(
+// radio_data === radio_tx_data[radio_num],
+// "Radio Tx output changed when late Tx packet should have been ignored"
+// );
+// end
+// end
+// join_any
+//
+// // Stop checking the output
+// disable tx_fork;
+
+ policy = policy.next();
+ end while (policy != policy.first());
+
+ // Make sure good transmissions can go through now.
+ start_tx_timed(radio_num, WPP, radio_time + 200);
+ check_tx_timed(radio_num, WPP, radio_time + 200);
+ check_error(ERR_TX_EOB_ACK);
+
+ test.end_test();
+
+ endtask : test_tx
+
+
+
+ // Test internal loopback and idle value
+ task automatic test_loopback_and_idle(int radio_num);
+ int byte_length;
+ chdr_word_t data[$];
+ bit [ITEM_W-1:0] idle;
+
+ //----------------------------
+ // Use IDLE value to loopback
+ //----------------------------
+
+ test.start_test("Idle Loopback", 50us);
+
+ // Turn on loopback
+ write_radio(radio_num, REG_LOOPBACK_EN, 1);
+
+ // This test ensures we get the Tx output on Rx and not the TB's simulated
+ // radio data. It also tests updating the idle value. Run the test twice to
+ // make sure the IDLE value updates.
+ repeat (2) begin
+ // Set idle value
+ idle = $urandom();
+ write_radio(radio_num, REG_TX_IDLE_VALUE, idle);
+
+ // Grab a radio word and check that it equals the IDLE value
+ write_radio_64(radio_num, REG_RX_CMD_NUM_WORDS_LO, 1);
+ write_radio(radio_num, REG_RX_CMD, RX_CMD_FINITE);
+ blk_ctrl.recv(radio_num, data, byte_length);
+
+ // Check the length
+ `ASSERT_ERROR(byte_length == RADIO_W/8, "Didn't receive expected length");
+
+ // Check the payload
+ foreach (data[i]) begin
+ chdr_word_t word;
+ word = data[i]; // Work around Vivado 2018.3 issue
+ `ASSERT_ERROR(
+ word == {$bits(chdr_word_t)/ITEM_W{idle}},
+ "Loopback data didn't match expected"
+ );
+ end
+ end
+
+ test.end_test();
+
+
+ //---------------------
+ // Loopback Tx packets
+ //---------------------
+
+ test.start_test("Tx Loopback", 50us);
+
+ // This test ensures that loopback isn't reordering words or anything else
+ // unexpected.
+
+ // Configure the Tx error reporting registers
+ write_radio(radio_num, REG_TX_ERR_PORT, TX_ERR_DST_PORT);
+ write_radio(radio_num, REG_TX_ERR_REM_PORT, TX_ERR_REM_DST_PORT);
+ write_radio(radio_num, REG_TX_ERR_REM_EPID, TX_ERR_REM_DST_EPID);
+ write_radio(radio_num, REG_TX_ERR_ADDR, TX_ERR_ADDRESS);
+
+ // Set packet length
+ write_radio(radio_num, REG_RX_MAX_WORDS_PER_PKT, WPP);
+
+ // Loopback a few packets, back-to-back. This code has a race condition
+ // since there's a delay between when we start Tx and when Rx starts, due
+ // to how long it takes to write the Rx registers. Therefore, we transmit a
+ // lot more packets than we receive to ensure we're still transmitting by
+ // the time we receive.
+ start_tx(radio_num, WPP*16);
+ start_rx(radio_num, WPP*2);
+
+ // Check the results
+ check_rx(radio_num, WPP*2);
+ check_error(ERR_TX_EOB_ACK);
+
+ // Turn off loopback
+ write_radio(radio_num, REG_LOOPBACK_EN, 0);
+
+ test.end_test();
+ endtask : test_loopback_and_idle;
+
+
+
+ //---------------------------------------------------------------------------
+ // Test Process
+ //---------------------------------------------------------------------------
+
+ timeout_t timeout;
+
+ initial begin : main
+ string tb_name;
+
+ //-------------------------------------------------------------------------
+ // Initialization
+ //-------------------------------------------------------------------------
+
+ // Generate a string for the name of this instance of the testbench
+ tb_name = $sformatf(
+ "rfnoc_block_radio_tb\nCHDR_W = %0D, ITEM_W = %0D, NIPC = %0D, NUM_PORTS = %0D, STALL_PROB = %0D, STB_PROB = %0D, TEST_REGS = %0D",
+ CHDR_W, ITEM_W, NIPC, NUM_PORTS, STALL_PROB, STB_PROB, TEST_REGS
+ );
+
+ test.start_tb(tb_name);
+
+ // Don't start the clocks until after start_tb() returns. This ensures that
+ // the clocks aren't toggling while other instances of this testbench are
+ // running, which speeds up simulation time.
+ rfnoc_chdr_clk_gen.start();
+ rfnoc_ctrl_clk_gen.start();
+ radio_clk_gen.start();
+
+ // Setup and start the stream endpoint BFM
+ blk_ctrl = new(backend, m_ctrl, s_ctrl);
+ for (int i = 0; i < NUM_PORTS; i++) begin
+ // I'd love to do this:
+ // void'(blk_ctrl.add_master_data_port(m_chdr[i]));
+ // void'(blk_ctrl.add_slave_data_port(s_chdr[i]));
+ // But interface indices must be constant. So instead, we use a semaphore
+ // to trigger port initialization and control the order of initialization
+ // in the generate block gen_radio_connections.
+
+ // Put the port number in the semaphore to cause its initializer to run
+ port_sem.put(i+1);
+ // Delay to allow gen_radio_connections to run
+ #0;
+ // Get the port number again to know when it's done
+ port_sem.get(i+1);
+
+ // Set the CHDR BFM stall probability
+ blk_ctrl.set_master_stall_prob(i, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(i, STALL_PROB);
+ end
+ blk_ctrl.run();
+
+
+ //-------------------------------------------------------------------------
+ // Reset
+ //-------------------------------------------------------------------------
+
+ test.start_test("Flush block then reset it", 10us);
+ blk_ctrl.flush_and_reset();
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Test Sequences
+ //-------------------------------------------------------------------------
+
+ // Run register tests first, since they check that initial values are
+ // correct.
+
+ test_block_info();
+ if (TEST_REGS) test_shared_registers();
+
+ for (int radio_num = 0; radio_num < NUM_PORTS; radio_num++) begin
+ $display("************************************************************");
+ $display("Testing Radio Channel %0d", radio_num);
+ $display("************************************************************");
+ if (TEST_REGS) begin
+ test_general_registers(radio_num);
+ test_rx_registers(radio_num);
+ test_tx_registers(radio_num);
+ end
+ test_rx(radio_num);
+ test_tx(radio_num);
+ test_loopback_and_idle(radio_num);
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Finish
+ //-------------------------------------------------------------------------
+
+ // End the TB, but don't $finish, since we don't want to kill other
+ // instances of this testbench that may be running.
+ test.end_tb(0);
+
+ // Kill the clocks to end this instance of the testbench
+ rfnoc_chdr_clk_gen.kill();
+ rfnoc_ctrl_clk_gen.kill();
+ radio_clk_gen.kill();
+
+ end : main
+
+endmodule : rfnoc_block_radio_tb
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v
new file mode 100644
index 000000000..54529136b
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rx_frontend_gen3.v
@@ -0,0 +1,246 @@
+//
+// Copyright 2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module rx_frontend_gen3 #(
+ parameter SR_MAG_CORRECTION = 0,
+ parameter SR_PHASE_CORRECTION = 1,
+ parameter SR_OFFSET_I = 2,
+ parameter SR_OFFSET_Q = 3,
+ parameter SR_IQ_MAPPING = 4,
+ parameter SR_HET_PHASE_INCR = 5,
+ parameter BYPASS_DC_OFFSET_CORR = 0,
+ parameter BYPASS_IQ_COMP = 0,
+ parameter BYPASS_REALMODE_DSP = 0,
+ parameter DEVICE = "7SERIES"
+)(
+ input clk, input reset, input sync_in,
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+ input adc_stb, input [15:0] adc_i, input [15:0] adc_q,
+ output rx_stb, output [15:0] rx_i, output [15:0] rx_q
+);
+
+ wire realmode;
+ wire swap_iq;
+ wire invert_i;
+ wire invert_q;
+ wire realmode_decim;
+ wire bypass_all;
+ wire [1:0] iq_map_reserved;
+ wire [17:0] mag_corr, phase_corr;
+ wire phase_dir;
+ wire phase_sync;
+
+ reg [23:0] adc_i_mux, adc_q_mux;
+ reg adc_mux_stb;
+ wire [23:0] adc_i_ofs, adc_q_ofs, adc_i_comp, adc_q_comp;
+ reg [23:0] adc_i_ofs_dly, adc_q_ofs_dly;
+ wire adc_ofs_stb, adc_comp_stb;
+ reg [1:0] adc_ofs_stb_dly;
+ wire [23:0] adc_i_dsp, adc_q_dsp;
+ wire adc_dsp_stb;
+ wire [35:0] corr_i, corr_q;
+ wire [15:0] rx_i_out, rx_q_out;
+
+ /********************************************************
+ ** Settings Bus Registers
+ ********************************************************/
+ setting_reg #(.my_addr(SR_MAG_CORRECTION),.width(18)) sr_mag_corr (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(mag_corr),.changed());
+
+ setting_reg #(.my_addr(SR_PHASE_CORRECTION),.width(18)) sr_phase_corr (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(phase_corr),.changed());
+
+ setting_reg #(.my_addr(SR_IQ_MAPPING), .width(8)) sr_mux_sel (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out({bypass_all,iq_map_reserved,realmode_decim,invert_i,invert_q,realmode,swap_iq}),.changed());
+
+ // Setting reg: 1 bit to set phase direction: default to 0:
+ // direction bit == 0: the phase is increased by pi/2 (counter clockwise)
+ // direction bit == 1: the phase is increased by -pi/2 (clockwise)
+ setting_reg #(.my_addr(SR_HET_PHASE_INCR), .width(1)) sr_phase_dir (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(phase_dir),.changed(phase_sync));
+
+ /********************************************************
+ ** IQ Mapping (swapping, inversion, real-mode)
+ ********************************************************/
+ // MUX so we can do realmode signals on either input
+ always @(posedge clk) begin
+ if (swap_iq) begin
+ adc_i_mux[23:8] <= invert_q ? ~adc_q : adc_q;
+ adc_q_mux[23:8] <= realmode ? 16'd0 : invert_i ? ~adc_i : adc_i;
+ end else begin
+ adc_i_mux[23:8] <= invert_i ? ~adc_i : adc_i;
+ adc_q_mux[23:8] <= realmode ? 16'd0 : invert_q ? ~adc_q : adc_q;
+ end
+ adc_mux_stb <= adc_stb;
+ adc_i_mux[7:0] <= 8'd0;
+ adc_q_mux[7:0] <= 8'd0;
+ end
+
+ /********************************************************
+ ** DC offset Correction
+ ********************************************************/
+ generate
+ if (BYPASS_DC_OFFSET_CORR == 0) begin
+
+ rx_dcoffset #(.WIDTH(24),.ADDR(SR_OFFSET_I)) rx_dcoffset_i (
+ .clk(clk),.rst(reset),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
+ .in_stb(adc_mux_stb),.in(adc_i_mux),
+ .out_stb(adc_ofs_stb),.out(adc_i_ofs));
+ rx_dcoffset #(.WIDTH(24),.ADDR(SR_OFFSET_Q)) rx_dcoffset_q (
+ .clk(clk),.rst(reset),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
+ .in_stb(adc_mux_stb),.in(adc_q_mux),
+ .out_stb(),.out(adc_q_ofs));
+
+ end else begin
+ assign adc_ofs_stb = adc_mux_stb;
+ assign adc_i_ofs = adc_i_mux;
+ assign adc_q_ofs = adc_q_mux;
+ end
+ endgenerate
+
+ /********************************************************
+ ** IQ Imbalance Compensation
+ ********************************************************/
+ generate
+ if (BYPASS_IQ_COMP == 0) begin
+
+ mult_add_clip #(
+ .WIDTH_A(18),
+ .BIN_PT_A(17),
+ .WIDTH_B(18),
+ .BIN_PT_B(17),
+ .WIDTH_C(24),
+ .BIN_PT_C(23),
+ .WIDTH_O(24),
+ .BIN_PT_O(23),
+ .LATENCY(2)
+ ) mult_i (
+ .clk(clk),
+ .reset(reset),
+ .CE(1'b1),
+ .A(adc_i_ofs[23:6]),
+ .B(mag_corr),
+ .C(adc_i_ofs),
+ .O(adc_i_comp)
+ );
+
+ mult_add_clip #(
+ .WIDTH_A(18),
+ .BIN_PT_A(17),
+ .WIDTH_B(18),
+ .BIN_PT_B(17),
+ .WIDTH_C(24),
+ .BIN_PT_C(23),
+ .WIDTH_O(24),
+ .BIN_PT_O(23),
+ .LATENCY(2)
+ ) mult_q (
+ .clk(clk),
+ .reset(reset),
+ .CE(1'b1),
+ .A(adc_i_ofs[23:6]),
+ .B(phase_corr),
+ .C(adc_q_ofs),
+ .O(adc_q_comp)
+ );
+
+ // Delay to match path latencies
+ always @(posedge clk) begin
+ if (reset) begin
+ adc_ofs_stb_dly <= 2'b0;
+ end else begin
+ adc_ofs_stb_dly <= {adc_ofs_stb_dly[0], adc_ofs_stb};
+ end
+ end
+
+ assign adc_comp_stb = adc_ofs_stb_dly[1];
+
+ end else begin
+ assign adc_comp_stb = adc_ofs_stb;
+ assign adc_i_comp = adc_i_ofs;
+ assign adc_q_comp = adc_q_ofs;
+ end
+ endgenerate
+
+ /********************************************************
+ ** Realmode DSP:
+ * - Heterodyne frequency translation
+ * - Realmode decimation (by 2)
+ ********************************************************/
+ generate
+ if (BYPASS_REALMODE_DSP == 0) begin
+
+ wire [24:0] adc_i_dsp_cout, adc_q_dsp_cout;
+ wire [23:0] adc_i_cclip, adc_q_cclip;
+ wire [23:0] adc_i_hb, adc_q_hb;
+ wire [23:0] adc_i_dec, adc_q_dec;
+ wire adc_dsp_cout_stb;
+ wire adc_cclip_stb;
+ wire adc_hb_stb;
+
+ wire valid_hbf0;
+ wire valid_hbf1;
+ wire valid_dec0;
+ wire valid_dec1;
+
+ // 90 degree mixer
+ quarter_rate_downconverter #(.WIDTH(24)) qr_dc_i(
+ .clk(clk), .reset(reset || sync_in), .phase_sync(phase_sync),
+ .i_tdata({adc_i_comp, adc_q_comp}), .i_tlast(1'b1), .i_tvalid(adc_comp_stb), .i_tready(),
+ .o_tdata({adc_i_dsp_cout, adc_q_dsp_cout}), .o_tlast(), .o_tvalid(adc_dsp_cout_stb), .o_tready(1'b1),
+ .dirctn(phase_dir));
+
+ // Double FIR and decimator block
+ localparam HB_COEFS = {-18'd62, 18'd0, 18'd194, 18'd0, -18'd440, 18'd0, 18'd855, 18'd0, -18'd1505, 18'd0, 18'd2478, 18'd0,
+ -18'd3900, 18'd0, 18'd5990, 18'd0, -18'd9187, 18'd0, 18'd14632, 18'd0, -18'd26536, 18'd0, 18'd83009, 18'd131071, 18'd83009,
+ 18'd0, -18'd26536, 18'd0, 18'd14632, 18'd0, -18'd9187, 18'd0, 18'd5990, 18'd0, -18'd3900, 18'd0, 18'd2478, 18'd0, -18'd1505,
+ 18'd0, 18'd855, 18'd0, -18'd440, 18'd0, 18'd194, 18'd0, -18'd62};
+
+ axi_fir_filter_dec #(
+ .WIDTH(24),
+ .COEFF_WIDTH(18),
+ .NUM_COEFFS(47),
+ .COEFFS_VEC(HB_COEFS),
+ .BLANK_OUTPUT(0)
+ ) ffd0 (
+ .clk(clk), .reset(reset || sync_in),
+
+ .i_tdata({adc_i_dsp_cout, adc_q_dsp_cout}),
+ .i_tlast(1'b1),
+ .i_tvalid(adc_dsp_cout_stb),
+ .i_tready(),
+
+ .o_tdata({adc_i_dec, adc_q_dec}),
+ .o_tlast(),
+ .o_tvalid(adc_hb_stb),
+ .o_tready(1'b1));
+
+ assign adc_dsp_stb = realmode_decim ? adc_hb_stb : adc_comp_stb;
+ assign adc_i_dsp = realmode_decim ? adc_i_dec : adc_i_comp;
+ assign adc_q_dsp = realmode_decim ? adc_q_dec : adc_q_comp;
+
+ end else begin
+ assign adc_dsp_stb = adc_comp_stb;
+ assign adc_i_dsp = adc_i_comp;
+ assign adc_q_dsp = adc_q_comp;
+ end
+ endgenerate
+
+ // Round to short complex (sc16)
+ round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_i (
+ .clk(clk),.reset(reset), .in(adc_i_dsp),.strobe_in(adc_dsp_stb), .out(rx_i_out), .strobe_out(rx_stb));
+ round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_q (
+ .clk(clk),.reset(reset), .in(adc_q_dsp),.strobe_in(adc_dsp_stb), .out(rx_q_out), .strobe_out());
+
+ assign rx_i = bypass_all ? adc_i : rx_i_out;
+ assign rx_q = bypass_all ? adc_q : rx_q_out;
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/sim_radio_gen.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/sim_radio_gen.sv
new file mode 100644
index 000000000..a6f827f8f
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/sim_radio_gen.sv
@@ -0,0 +1,104 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: sim_radio_gen
+//
+// Description: Generate radio data for simulation purposes. The strobe pattern
+// is random, which is not like a normal radio but covers every possibility.
+// The data pattern is an incrementing sequence of samples, with each channel
+// starting at a different value to differentiate them. Strobe and time are
+// common between channels.
+//
+
+module sim_radio_gen #(
+ parameter int NSPC = 1, // Number of samples per clock cycle
+ parameter int SAMP_W = 32, // Length of each radio sample
+ parameter int NUM_CHANNELS = 1, // Number of radio RX ports
+ parameter int STB_PROB = 50, // Probability of STB being asserted on each clock cycle
+ parameter int INCREMENT = 2, // Amount by which to increment
+ parameter int PPS_PERIOD = 50 // Period of the PPS output
+) (
+ input bit radio_clk,
+ input bit radio_rst,
+ output bit [NUM_CHANNELS*SAMP_W*NSPC-1:0] radio_rx_data,
+ output bit [ NUM_CHANNELS-1:0] radio_rx_stb,
+ output bit [ 63:0] radio_time,
+ output bit radio_pps
+);
+
+ localparam int RADIO_W = SAMP_W*NSPC;
+ typedef bit [RADIO_W-1:0] radio_t; // Radio output word
+ typedef bit [SAMP_W-1:0] sample_t; // Single sample
+
+ initial assert (PPS_PERIOD % INCREMENT == 0) else
+ $fatal(1, "PPS_PERIOD must be a multiple of INCREMENT");
+
+
+ // Generate an initial value all radio channels
+ function radio_t [NUM_CHANNELS-1:0] radio_init();
+ radio_t [NUM_CHANNELS-1:0] ret_val;
+
+ for (int n = 0; n < NUM_CHANNELS; n++) begin
+ sample_t sample;
+
+ // Calculate the value of first sample in this radio channel
+ sample = sample_t'((2.0 ** SAMP_W) / NUM_CHANNELS * n);
+
+ // Calculate the value of subsequent samples in the channel
+ for (int s = 0; s < NSPC; s++) begin
+ ret_val[n][s*SAMP_W +: SAMP_W] = sample + s;
+ end
+ end
+
+ return ret_val;
+ endfunction : radio_init
+
+
+ //---------------------------------------------------------------------------
+ // Radio Data Generation
+ //---------------------------------------------------------------------------
+
+ radio_t [NUM_CHANNELS-1:0] data = radio_init();
+
+ assign radio_rx_data = data;
+
+ always @(posedge radio_clk) begin : radio_data_count_reg
+ if (radio_rst) begin
+ data <= radio_init();
+ radio_rx_stb <= '0;
+ end else begin
+ radio_rx_stb <= '0;
+ if ($urandom_range(100) < STB_PROB) begin
+ for (int n = 0; n < NUM_CHANNELS; n++) begin
+ for (int s = 0; s < NSPC; s++) begin
+ data[n][s*SAMP_W +: SAMP_W] <= data[n][s*SAMP_W +: SAMP_W] + NSPC;
+ end
+ end
+ radio_rx_stb <= '1;
+ end
+ end
+ end : radio_data_count_reg
+
+
+ //---------------------------------------------------------------------------
+ // Radio Time
+ //---------------------------------------------------------------------------
+
+ always @(posedge radio_clk) begin
+ if (radio_rst) begin
+ radio_time <= 64'b0;
+ radio_pps <= 1'b0;
+ end else begin
+ radio_pps <= 1'b0;
+ if (radio_rx_stb[0]) begin
+ radio_time <= radio_time + INCREMENT;
+ if (radio_time % PPS_PERIOD == 0 && radio_time != 0) begin
+ radio_pps <= 1'b1;
+ end
+ end
+ end
+ end
+
+endmodule : sim_radio_gen \ No newline at end of file
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/tx_frontend_gen3.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/tx_frontend_gen3.v
new file mode 100644
index 000000000..f5435787d
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/tx_frontend_gen3.v
@@ -0,0 +1,173 @@
+//
+// Copyright 2015 Ettus Research LLC
+// Copyright 2018 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+module tx_frontend_gen3 #(
+ parameter SR_OFFSET_I = 0,
+ parameter SR_OFFSET_Q = 1,
+ parameter SR_MAG_CORRECTION = 2,
+ parameter SR_PHASE_CORRECTION = 3,
+ parameter SR_MUX = 4,
+ parameter BYPASS_DC_OFFSET_CORR = 0,
+ parameter BYPASS_IQ_COMP = 0,
+ parameter DEVICE = "7SERIES"
+)(
+ input clk, input reset,
+ input set_stb, input [7:0] set_addr, input [31:0] set_data,
+ input tx_stb, input [15:0] tx_i, input [15:0] tx_q,
+ output reg dac_stb, output reg [15:0] dac_i, output reg [15:0] dac_q
+);
+
+ wire [23:0] i_dco, q_dco;
+ wire [7:0] mux_ctrl;
+ wire [17:0] mag_corr, phase_corr;
+
+ wire [35:0] corr_i, corr_q;
+ reg [1:0] tx_stb_dly;
+ reg [23:0] tx_i_dly, tx_q_dly;
+ wire tx_comp_stb, tx_ofs_stb;
+ wire [23:0] tx_i_comp, tx_q_comp, tx_i_ofs, tx_q_ofs;
+ wire tx_round_stb;
+ wire [15:0] tx_i_round, tx_q_round;
+
+ /********************************************************
+ ** Settings Registers
+ ********************************************************/
+ setting_reg #(.my_addr(SR_OFFSET_I), .width(24)) sr_i_dc_offset (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(i_dco),.changed());
+
+ setting_reg #(.my_addr(SR_OFFSET_Q), .width(24)) sr_q_dc_offset (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(q_dco),.changed());
+
+ setting_reg #(.my_addr(SR_MAG_CORRECTION),.width(18)) sr_mag_corr (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(mag_corr),.changed());
+
+ setting_reg #(.my_addr(SR_PHASE_CORRECTION),.width(18)) sr_phase_corr (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(phase_corr),.changed());
+
+ setting_reg #(.my_addr(SR_MUX), .width(8), .at_reset(8'h10)) sr_mux_ctrl (
+ .clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
+ .in(set_data),.out(mux_ctrl),.changed());
+
+ /********************************************************
+ ** DSP
+ ********************************************************/
+ // I/Q compensation with option to bypass
+ generate
+ if (BYPASS_IQ_COMP == 0) begin
+
+ mult_add_clip #(
+ .WIDTH_A(16),
+ .BIN_PT_A(15),
+ .WIDTH_B(18),
+ .BIN_PT_B(17),
+ .WIDTH_C(16),
+ .BIN_PT_C(15),
+ .WIDTH_O(24),
+ .BIN_PT_O(23),
+ .LATENCY(2)
+ ) mult_i (
+ .clk(clk),
+ .reset(reset),
+ .CE(1'b1),
+ .A(tx_i),
+ .B(mag_corr),
+ .C(tx_i),
+ .O(tx_i_comp)
+ );
+
+ mult_add_clip #(
+ .WIDTH_A(16),
+ .BIN_PT_A(15),
+ .WIDTH_B(18),
+ .BIN_PT_B(17),
+ .WIDTH_C(16),
+ .BIN_PT_C(15),
+ .WIDTH_O(24),
+ .BIN_PT_O(23),
+ .LATENCY(2)
+ ) mult_q (
+ .clk(clk),
+ .reset(reset),
+ .CE(1'b1),
+ .A(tx_i),
+ .B(phase_corr),
+ .C(tx_q),
+ .O(tx_q_comp)
+ );
+
+ // Delay to match path latencies
+ always @(posedge clk) begin
+ if (reset) begin
+ tx_stb_dly <= 2'b0;
+ end else begin
+ tx_stb_dly <= {tx_stb_dly[0], tx_stb};
+ end
+ end
+
+ assign tx_comp_stb = tx_stb_dly[1];
+
+ end else begin
+ assign tx_comp_stb = tx_stb;
+ assign tx_i_comp = {tx_i,8'd0};
+ assign tx_q_comp = {tx_q,8'd0};
+ end
+ endgenerate
+
+ // DC offset correction
+ generate
+ if (BYPASS_DC_OFFSET_CORR == 0) begin
+ add2_and_clip_reg #(.WIDTH(24)) add_dco_i (
+ .clk(clk), .rst(reset), .in1(i_dco), .in2(tx_i_comp), .strobe_in(tx_comp_stb), .sum(tx_i_ofs), .strobe_out(tx_ofs_stb));
+ add2_and_clip_reg #(.WIDTH(24)) add_dco_q (
+ .clk(clk), .rst(reset), .in1(q_dco), .in2(tx_q_comp), .strobe_in(tx_comp_stb), .sum(tx_q_ofs), .strobe_out());
+ end else begin
+ assign tx_ofs_stb = tx_comp_stb;
+ assign tx_i_ofs = tx_i_comp;
+ assign tx_q_ofs = tx_q_comp;
+ end
+ endgenerate
+
+ // Round to short complex (sc16)
+ round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_i (
+ .clk(clk),.reset(reset), .in(tx_i_ofs),.strobe_in(tx_ofs_stb), .out(tx_i_round), .strobe_out(tx_round_stb));
+ round_sd #(.WIDTH_IN(24),.WIDTH_OUT(16)) round_q (
+ .clk(clk),.reset(reset), .in(tx_q_ofs),.strobe_in(tx_ofs_stb), .out(tx_q_round), .strobe_out());
+
+ // Mux
+ // Muxing logic matches that in tx_frontend.v, and what tx_frontend_core_200.cpp expects.
+ //
+ // mux_ctrl ! 0+0 ! 0+16 ! 1+0 ! 1+16
+ // =========!======!======!======!========
+ // DAC_I ! tx_i ! tx_i ! tx_q ! tx_q
+ // DAC_Q ! tx_i ! tx_q ! tx_i ! tx_q
+ //
+ // Most daughterboards will thus use 0x01 or 0x10 as the mux_ctrl value.
+ always @(posedge clk) begin
+ if (reset) begin
+ dac_stb <= 1'b0;
+ dac_i <= 16'd0;
+ dac_q <= 16'd0;
+ end else begin
+ dac_stb <= tx_round_stb;
+ case(mux_ctrl[3:0])
+ 0 : dac_i <= tx_i_round;
+ 1 : dac_i <= tx_q_round;
+ default : dac_i <= 0;
+ endcase
+ case(mux_ctrl[7:4])
+ 0 : dac_q <= tx_i_round;
+ 1 : dac_q <= tx_q_round;
+ default : dac_q <= 0;
+ endcase
+ end
+ end
+
+endmodule