aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft')
-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
5 files changed, 1188 insertions, 0 deletions
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