diff options
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft')
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 |