aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/blocks
diff options
context:
space:
mode:
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