diff options
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc')
6 files changed, 1203 insertions, 0 deletions
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 |