aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc')
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile67
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile.srcs11
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc.v387
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_regs.vh25
-rw-r--r--fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv387
5 files changed, 877 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile
new file mode 100644
index 000000000..6d1da3d60
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile
@@ -0,0 +1,67 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../../../top)
+# Include viv_sim_preamble after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# IP Specific
+#-------------------------------------------------
+# If simulation contains IP, define the IP_DIR and point
+# it to the base level IP directory
+LIB_IP_DIR = $(BASE_DIR)/../lib/ip
+
+# Include makefiles and sources for all IP components
+# *after* defining the LIB_IP_DIR
+include $(LIB_IP_DIR)/axi_hb47/Makefile.inc
+include $(LIB_IP_DIR)/complex_multiplier_dds/Makefile.inc
+include $(LIB_IP_DIR)/dds_sin_cos_lut_only/Makefile.inc
+include $(BASE_DIR)/x300/coregen_dsp/Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(LIB_IP_AXI_HB47_SRCS) \
+$(LIB_IP_COMPLEX_MULTIPLIER_DDS_SRCS) \
+$(LIB_IP_DDS_SIN_COS_LUT_ONLY_SRCS) \
+$(COREGEN_DSP_SRCS) \
+)
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
+include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
+include Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_BLOCK_DUC_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = rfnoc_block_duc_tb
+
+# Add test bench, user design under test, and
+# additional user created files
+SIM_SRCS = \
+$(abspath rfnoc_block_duc_tb.sv)
+
+#-------------------------------------------------
+# Bottom-of-Makefile
+#-------------------------------------------------
+# Include all simulator specific makefiles here
+# Each should define a unique target to simulate
+# e.g. xsim, vsim, etc and a common "clean" target
+include $(BASE_DIR)/../tools/make/viv_simulator.mak
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile.srcs
new file mode 100644
index 000000000..69b6eaece
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/Makefile.srcs
@@ -0,0 +1,11 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Company
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+RFNOC_BLOCK_DUC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_duc/, \
+../rfnoc_block_ddc/noc_shell_ddc.v \
+rfnoc_block_duc_regs.vh \
+rfnoc_block_duc.v \
+))
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc.v
new file mode 100644
index 000000000..400e9d270
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc.v
@@ -0,0 +1,387 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_duc
+//
+// Description: An digital up-converter block for RFNoC.
+//
+// Parameters:
+//
+// THIS_PORTID : Control crossbar port to which this block is connected
+// CHDR_W : AXIS CHDR interface data width
+// NUM_PORTS : Number of DUC signal processing chains
+// MTU : Maximum transmission unit (i.e., maximum packet size) in
+// CHDR words is 2**MTU.
+// CTRL_FIFO_SIZE : Size of the Control Port slave FIFO. This affects the
+// number of outstanding commands that can be pending.
+// NUM_HB : Number of half-band filter blocks to include (0-3)
+// CIC_MAX_INTERP : Maximum interpolation to support in the CIC filter
+//
+
+module rfnoc_block_duc #(
+ parameter THIS_PORTID = 0,
+ parameter CHDR_W = 64,
+ parameter NUM_PORTS = 2,
+ parameter MTU = 10,
+ parameter CTRL_FIFO_SIZE = 6,
+ parameter NUM_HB = 2,
+ parameter CIC_MAX_INTERP = 128
+) (
+ //---------------------------------------------------------------------------
+ // AXIS CHDR Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_chdr_clk,
+ input wire ce_clk,
+
+ // CHDR inputs from framework
+ input wire [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast,
+ input wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid,
+ output wire [ NUM_PORTS-1:0] s_rfnoc_chdr_tready,
+
+ // CHDR outputs to framework
+ output wire [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
+ output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
+ input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
+
+ // Backend interface
+ input wire [511:0] rfnoc_core_config,
+ output wire [511:0] rfnoc_core_status,
+
+ //---------------------------------------------------------------------------
+ // AXIS CTRL Port
+ //---------------------------------------------------------------------------
+
+ input wire rfnoc_ctrl_clk,
+
+ // CTRL port requests from framework
+ input wire [31:0] s_rfnoc_ctrl_tdata,
+ input wire s_rfnoc_ctrl_tlast,
+ input wire s_rfnoc_ctrl_tvalid,
+ output wire s_rfnoc_ctrl_tready,
+
+ // CTRL port requests to framework
+ output wire [31:0] m_rfnoc_ctrl_tdata,
+ output wire m_rfnoc_ctrl_tlast,
+ output wire m_rfnoc_ctrl_tvalid,
+ input wire m_rfnoc_ctrl_tready
+);
+
+ // These are the only supported values for now
+ localparam ITEM_W = 32;
+ localparam NIPC = 1;
+
+ localparam NOC_ID = 'hD0C0_0000;
+
+ localparam COMPAT_MAJOR = 16'h0;
+ localparam COMPAT_MINOR = 16'h0;
+
+ `include "rfnoc_block_duc_regs.vh"
+ `include "../../core/rfnoc_axis_ctrl_utils.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Signal Declarations
+ //---------------------------------------------------------------------------
+
+ wire rfnoc_chdr_rst;
+
+ wire ctrlport_req_wr;
+ wire ctrlport_req_rd;
+ wire [19:0] ctrlport_req_addr;
+ wire [31:0] ctrlport_req_data;
+ wire ctrlport_req_has_time;
+ wire [63:0] ctrlport_req_time;
+ wire ctrlport_resp_ack;
+ wire [31:0] ctrlport_resp_data;
+
+ wire [NUM_PORTS*ITEM_W-1:0] m_axis_data_tdata;
+ wire [ NUM_PORTS-1:0] m_axis_data_tlast;
+ wire [ NUM_PORTS-1:0] m_axis_data_tvalid;
+ wire [ NUM_PORTS-1:0] m_axis_data_tready;
+ wire [ NUM_PORTS*64-1:0] m_axis_data_ttimestamp;
+ wire [ NUM_PORTS-1:0] m_axis_data_thas_time;
+ wire [ NUM_PORTS*16-1:0] m_axis_data_tlength;
+ wire [ NUM_PORTS-1:0] m_axis_data_teob;
+ wire [ NUM_PORTS*128-1:0] m_axis_data_tuser;
+
+ wire [NUM_PORTS*ITEM_W-1:0] s_axis_data_tdata;
+ wire [ NUM_PORTS-1:0] s_axis_data_tlast;
+ wire [ NUM_PORTS-1:0] s_axis_data_tvalid;
+ wire [ NUM_PORTS-1:0] s_axis_data_tready;
+ wire [ NUM_PORTS*128-1:0] s_axis_data_tuser;
+ wire [ NUM_PORTS-1:0] s_axis_data_teob;
+ wire [ NUM_PORTS*64-1:0] s_axis_data_ttimestamp;
+ wire [ NUM_PORTS-1:0] s_axis_data_thas_time;
+
+ wire duc_rst;
+
+ // Cross the CHDR reset to the ce_clk domain
+ synchronizer duc_rst_sync_i (
+ .clk (ce_clk),
+ .rst (1'b0),
+ .in (rfnoc_chdr_rst),
+ .out (duc_rst)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // NoC Shell
+ //---------------------------------------------------------------------------
+
+ // TODO: Replace noc_shell_radio with a customized block
+ noc_shell_ddc #(
+ .NOC_ID (NOC_ID),
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .CTRLPORT_SLV_EN (0),
+ .CTRLPORT_MST_EN (1),
+ .CTRL_FIFO_SIZE (CTRL_FIFO_SIZE),
+ .NUM_DATA_I (NUM_PORTS),
+ .NUM_DATA_O (NUM_PORTS),
+ .ITEM_W (ITEM_W),
+ .NIPC (NIPC),
+ .PYLD_FIFO_SIZE (MTU),
+ .MTU (MTU)
+ ) noc_shell_ddc_i (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_chdr_rst (rfnoc_chdr_rst),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_ctrl_rst (),
+ .rfnoc_core_config (rfnoc_core_config),
+ .rfnoc_core_status (rfnoc_core_status),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (duc_rst),
+ .m_ctrlport_req_wr (ctrlport_req_wr),
+ .m_ctrlport_req_rd (ctrlport_req_rd),
+ .m_ctrlport_req_addr (ctrlport_req_addr),
+ .m_ctrlport_req_data (ctrlport_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (ctrlport_req_has_time),
+ .m_ctrlport_req_time (ctrlport_req_time),
+ .m_ctrlport_resp_ack (ctrlport_resp_ack),
+ .m_ctrlport_resp_status (AXIS_CTRL_STS_OKAY),
+ .m_ctrlport_resp_data (ctrlport_resp_data),
+ .s_ctrlport_req_wr (1'b0),
+ .s_ctrlport_req_rd (1'b0),
+ .s_ctrlport_req_addr (20'b0),
+ .s_ctrlport_req_portid (10'b0),
+ .s_ctrlport_req_rem_epid (16'b0),
+ .s_ctrlport_req_rem_portid (10'b0),
+ .s_ctrlport_req_data (32'b0),
+ .s_ctrlport_req_byte_en (4'b0),
+ .s_ctrlport_req_has_time (1'b0),
+ .s_ctrlport_req_time (64'b0),
+ .s_ctrlport_resp_ack (),
+ .s_ctrlport_resp_status (),
+ .s_ctrlport_resp_data (),
+ .axis_data_clk (ce_clk),
+ .axis_data_rst (duc_rst),
+ .m_axis_tdata (m_axis_data_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (m_axis_data_tlast),
+ .m_axis_tvalid (m_axis_data_tvalid),
+ .m_axis_tready (m_axis_data_tready),
+ .m_axis_ttimestamp (m_axis_data_ttimestamp),
+ .m_axis_thas_time (m_axis_data_thas_time),
+ .m_axis_tlength (m_axis_data_tlength),
+ .m_axis_teov (),
+ .m_axis_teob (m_axis_data_teob),
+ .s_axis_tdata (s_axis_data_tdata),
+ .s_axis_tkeep ({NUM_PORTS*NIPC{1'b1}}),
+ .s_axis_tlast (s_axis_data_tlast),
+ .s_axis_tvalid (s_axis_data_tvalid),
+ .s_axis_tready (s_axis_data_tready),
+ .s_axis_ttimestamp (s_axis_data_ttimestamp),
+ .s_axis_thas_time (s_axis_data_thas_time),
+ .s_axis_teov ({NUM_PORTS{1'b0}}),
+ .s_axis_teob (s_axis_data_teob)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Register Translation
+ //---------------------------------------------------------------------------
+ //
+ // Each DUC block is allocated an address spaces. This block translates CTRL
+ // port transactions in that space to settings bus.
+ //
+ //---------------------------------------------------------------------------
+
+ wire [ 8*NUM_PORTS-1:0] set_addr;
+ wire [32*NUM_PORTS-1:0] set_data;
+ wire [ NUM_PORTS-1:0] set_has_time;
+ wire [ NUM_PORTS-1:0] set_stb;
+ wire [64*NUM_PORTS-1:0] set_time;
+ wire [ 8*NUM_PORTS-1:0] rb_addr;
+ reg [64*NUM_PORTS-1:0] rb_data;
+
+ ctrlport_to_settings_bus # (
+ .NUM_PORTS (NUM_PORTS)
+ ) ctrlport_to_settings_bus_i (
+ .ctrlport_clk (ce_clk),
+ .ctrlport_rst (duc_rst),
+ .s_ctrlport_req_wr (ctrlport_req_wr),
+ .s_ctrlport_req_rd (ctrlport_req_rd),
+ .s_ctrlport_req_addr (ctrlport_req_addr),
+ .s_ctrlport_req_data (ctrlport_req_data),
+ .s_ctrlport_req_has_time (ctrlport_req_has_time),
+ .s_ctrlport_req_time (ctrlport_req_time),
+ .s_ctrlport_resp_ack (ctrlport_resp_ack),
+ .s_ctrlport_resp_data (ctrlport_resp_data),
+ .set_data (set_data),
+ .set_addr (set_addr),
+ .set_stb (set_stb),
+ .set_time (set_time),
+ .set_has_time (set_has_time),
+ .rb_stb ({NUM_PORTS{1'b1}}),
+ .rb_addr (rb_addr),
+ .rb_data (rb_data));
+
+
+ //---------------------------------------------------------------------------
+ // DUC Implementation
+ //---------------------------------------------------------------------------
+
+ // Unused signals
+ wire [ NUM_PORTS-1:0] clear_tx_seqnum = 0;
+ wire [16*NUM_PORTS-1:0] src_sid = 0;
+ wire [16*NUM_PORTS-1:0] next_dst_sid = 0;
+
+ localparam MAX_M = CIC_MAX_INTERP * 2<<(NUM_HB-1);
+
+ genvar i;
+ generate
+ for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_duc_chains
+ wire clear_user;
+ wire clear_duc = clear_tx_seqnum[i] | clear_user;
+
+ wire set_stb_int = set_stb[i];
+ wire [7:0] set_addr_int = set_addr[8*i+7:8*i];
+ wire [31:0] set_data_int = set_data[32*i+31:32*i];
+ wire [63:0] set_time_int = set_time[64*i+63:64*i];
+ wire set_has_time_int = set_has_time[i];
+
+ // Build the expected tuser CHDR header
+ cvita_hdr_encoder cvita_hdr_encoder_i (
+ .pkt_type (2'b0),
+ .eob (m_axis_data_teob[i]),
+ .has_time (m_axis_data_thas_time[i]),
+ .seqnum (12'b0),
+ .payload_length (m_axis_data_tlength[16*i +: 16]),
+ .src_sid (16'b0),
+ .dst_sid (16'b0),
+ .vita_time (m_axis_data_ttimestamp[64*i +: 64]),
+ .header (m_axis_data_tuser[128*i+:128])
+ );
+
+ // Extract bit fields from outgoing tuser CHDR header
+ assign s_axis_data_teob[i] = s_axis_data_tuser[128*i+124 +: 1];
+ assign s_axis_data_thas_time[i] = s_axis_data_tuser[128*i+125 +: 1];
+ assign s_axis_data_ttimestamp[64*i+:64] = s_axis_data_tuser[128*i+ 0 +: 64];
+
+ // TODO Readback register for number of FIR filter taps
+ always @(*) begin
+ case(rb_addr[i*8+7:i*8])
+ RB_COMPAT_NUM : rb_data[i*64+63:i*64] <= {COMPAT_MAJOR, COMPAT_MINOR};
+ RB_NUM_HB : rb_data[i*64+63:i*64] <= NUM_HB;
+ RB_CIC_MAX_INTERP : rb_data[i*64+63:i*64] <= CIC_MAX_INTERP;
+ default : rb_data[i*64+63:i*64] <= 64'h0BADC0DE0BADC0DE;
+ endcase
+ end
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Timed CORDIC
+ // - Implements timed cordic tunes. Placed between AXI Wrapper
+ // and AXI Rate Change due to it needing access to the
+ // vita time of the samples.
+ //
+ ////////////////////////////////////////////////////////////
+ wire [31:0] m_axis_rc_tdata;
+ wire m_axis_rc_tlast;
+ wire m_axis_rc_tvalid;
+ wire m_axis_rc_tready;
+ wire [127:0] m_axis_rc_tuser;
+
+ dds_timed #(
+ .SR_FREQ_ADDR(SR_FREQ_ADDR),
+ .SR_SCALE_IQ_ADDR(SR_SCALE_IQ_ADDR))
+ dds_timed (
+ .clk(ce_clk), .reset(duc_rst), .clear(clear_tx_seqnum[i]),
+ .timed_cmd_fifo_full(),
+ .set_stb(set_stb_int), .set_addr(set_addr_int), .set_data(set_data_int),
+ .set_time(set_time_int), .set_has_time(set_has_time_int),
+ .i_tdata(m_axis_rc_tdata), .i_tlast(m_axis_rc_tlast), .i_tvalid(m_axis_rc_tvalid),
+ .i_tready(m_axis_rc_tready), .i_tuser(m_axis_rc_tuser),
+ .o_tdata(s_axis_data_tdata[ITEM_W*i+:ITEM_W]), .o_tlast(s_axis_data_tlast[i]), .o_tvalid(s_axis_data_tvalid[i]),
+ .o_tready(s_axis_data_tready[i]), .o_tuser(s_axis_data_tuser[128*i+:128]));
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Increase Rate
+ //
+ ////////////////////////////////////////////////////////////
+ wire [31:0] sample_tdata, sample_duc_tdata;
+ wire sample_tvalid, sample_tready;
+ wire sample_duc_tvalid, sample_duc_tready;
+ axi_rate_change #(
+ .WIDTH(32),
+ .MAX_N(1),
+ .MAX_M(MAX_M),
+ .SR_N_ADDR(SR_N_ADDR),
+ .SR_M_ADDR(SR_M_ADDR),
+ .SR_CONFIG_ADDR(SR_CONFIG_ADDR))
+ axi_rate_change (
+ .clk(ce_clk), .reset(duc_rst), .clear(clear_tx_seqnum[i]), .clear_user(clear_user),
+ .src_sid(src_sid[16*i+15:16*i]), .dst_sid(next_dst_sid[16*i+15:16*i]),
+ .set_stb(set_stb_int), .set_addr(set_addr_int), .set_data(set_data_int),
+ .i_tdata(m_axis_data_tdata[ITEM_W*i+:ITEM_W]), .i_tlast(m_axis_data_tlast[i]), .i_tvalid(m_axis_data_tvalid[i]),
+ .i_tready(m_axis_data_tready[i]), .i_tuser(m_axis_data_tuser[128*i+:128]),
+ .o_tdata(m_axis_rc_tdata), .o_tlast(m_axis_rc_tlast), .o_tvalid(m_axis_rc_tvalid),
+ .o_tready(m_axis_rc_tready), .o_tuser(m_axis_rc_tuser),
+ .m_axis_data_tdata({sample_tdata}), .m_axis_data_tlast(), .m_axis_data_tvalid(sample_tvalid),
+ .m_axis_data_tready(sample_tready),
+ .s_axis_data_tdata(sample_duc_tdata), .s_axis_data_tlast(1'b0), .s_axis_data_tvalid(sample_duc_tvalid),
+ .s_axis_data_tready(sample_duc_tready),
+ .warning_long_throttle(), .error_extra_outputs(), .error_drop_pkt_lockup());
+
+ ////////////////////////////////////////////////////////////
+ //
+ // Digital Up Converter
+ //
+ ////////////////////////////////////////////////////////////
+ duc #(
+ .SR_INTERP_ADDR(SR_INTERP_ADDR),
+ .NUM_HB(NUM_HB),
+ .CIC_MAX_INTERP(CIC_MAX_INTERP))
+ duc (
+ .clk(ce_clk), .reset(duc_rst), .clear(clear_duc),
+ .set_stb(set_stb_int), .set_addr(set_addr_int), .set_data(set_data_int),
+ .i_tdata(sample_tdata), .i_tuser(128'b0), .i_tvalid(sample_tvalid), .i_tready(sample_tready),
+ .o_tdata(sample_duc_tdata), .o_tuser(), .o_tvalid(sample_duc_tvalid), .o_tready(sample_duc_tready));
+
+ end
+ endgenerate
+
+endmodule
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_regs.vh b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_regs.vh
new file mode 100644
index 000000000..fa239857e
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_regs.vh
@@ -0,0 +1,25 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_duc_regs (Header)
+//
+// Description: Header file for RFNoC DUC functionality. This includes
+// register offsets, bitfields and constants for the radio components.
+//
+
+// For now, these offsets match the original DUC
+localparam DUC_BASE_ADDR = 'h00;
+localparam DUC_ADDR_W = 8;
+
+localparam RB_COMPAT_NUM = 0;
+localparam RB_NUM_HB = 1;
+localparam RB_CIC_MAX_INTERP = 2;
+localparam SR_N_ADDR = 128;
+localparam SR_M_ADDR = 129;
+localparam SR_CONFIG_ADDR = 130;
+localparam SR_INTERP_ADDR = 131;
+localparam SR_FREQ_ADDR = 132;
+localparam SR_SCALE_IQ_ADDR = 133;
+
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv
new file mode 100644
index 000000000..5bca3f03b
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv
@@ -0,0 +1,387 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_duc_tb
+//
+// Description: Testbench for rfnoc_block_duc
+//
+
+
+module rfnoc_block_duc_tb();
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+
+ `include "rfnoc_block_duc_regs.vh"
+
+
+ //---------------------------------------------------------------------------
+ // Local Parameters
+ //---------------------------------------------------------------------------
+
+ // Simulation parameters
+ localparam real CHDR_CLK_PER = 5.0; // CHDR clock rate
+ localparam real DUC_CLK_PER = 4.0; // DUC IP clock rate
+ localparam int EXTENDED_TEST = 0; // Perform a longer test
+ localparam int SPP = 128; // Samples per packet
+ localparam int PKT_SIZE_BYTES = SPP*4; // Bytes per packet
+ localparam int STALL_PROB = 25; // BFM stall probability
+
+ // Block configuration
+ localparam int CHDR_W = 64;
+ localparam int THIS_PORTID = 'h123;
+ localparam int MTU = 8;
+ localparam int NUM_PORTS = 1;
+ localparam int NUM_HB = 3;
+ localparam int CIC_MAX_INTERP = 128;
+
+
+ //---------------------------------------------------------------------------
+ // Clocks
+ //---------------------------------------------------------------------------
+
+ bit rfnoc_chdr_clk;
+ bit rfnoc_ctrl_clk;
+
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
+ sim_clock_gen #(CHDR_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst());
+ sim_clock_gen #(DUC_CLK_PER) duc_clk_gen (.clk(ce_clk), .rst());
+
+
+ //---------------------------------------------------------------------------
+ // Bus Functional Models
+ //---------------------------------------------------------------------------
+
+ RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk);
+ AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+ AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
+
+ // Bus functional model for a software block controller
+ RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
+ new(backend, m_ctrl, s_ctrl);
+
+ // Connect block controller to BFMs
+ for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
+ initial begin
+ blk_ctrl.connect_master_data_port(i, m_chdr[i], PKT_SIZE_BYTES);
+ blk_ctrl.connect_slave_data_port(i, s_chdr[i]);
+ blk_ctrl.set_master_stall_prob(i, STALL_PROB);
+ blk_ctrl.set_slave_stall_prob(i, STALL_PROB);
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // DUT
+ //---------------------------------------------------------------------------
+
+ logic [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tready;
+
+ logic [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid;
+ logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tready;
+
+ // Map the array of BFMs to a flat vector for the DUT
+ genvar i;
+ for (i = 0; i < NUM_PORTS; i++) begin : gen_dut_connections
+ // Connect BFM master to DUT slave port
+ assign s_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata;
+ assign s_rfnoc_chdr_tlast[i] = m_chdr[i].tlast;
+ assign s_rfnoc_chdr_tvalid[i] = m_chdr[i].tvalid;
+ assign m_chdr[i].tready = s_rfnoc_chdr_tready[i];
+
+ // Connect BFM slave to DUT master port
+ assign s_chdr[i].tdata = m_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W];
+ assign s_chdr[i].tlast = m_rfnoc_chdr_tlast[i];
+ assign s_chdr[i].tvalid = m_rfnoc_chdr_tvalid[i];
+ assign m_rfnoc_chdr_tready[i] = s_chdr[i].tready;
+ end
+
+ rfnoc_block_duc #(
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .NUM_PORTS (NUM_PORTS),
+ .MTU (MTU),
+ .NUM_HB (NUM_HB),
+ .CIC_MAX_INTERP (CIC_MAX_INTERP)
+ ) rfnoc_block_duc_i (
+ .rfnoc_chdr_clk (backend.chdr_clk),
+ .ce_clk (ce_clk),
+ .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
+ .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
+ .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
+ .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
+ .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
+ .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
+ .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
+ .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
+ .rfnoc_core_config (backend.cfg),
+ .rfnoc_core_status (backend.sts),
+ .rfnoc_ctrl_clk (backend.ctrl_clk),
+ .s_rfnoc_ctrl_tdata (m_ctrl.tdata),
+ .s_rfnoc_ctrl_tlast (m_ctrl.tlast),
+ .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid),
+ .s_rfnoc_ctrl_tready (m_ctrl.tready),
+ .m_rfnoc_ctrl_tdata (s_ctrl.tdata),
+ .m_rfnoc_ctrl_tlast (s_ctrl.tlast),
+ .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid),
+ .m_rfnoc_ctrl_tready (s_ctrl.tready)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Helper Tasks
+ //---------------------------------------------------------------------------
+
+ // Translate the desired register access to a ctrlport write request.
+ task automatic write_reg(int port, byte unsigned addr, bit [31:0] value);
+ blk_ctrl.reg_write(256*8*port + addr*8, value);
+ endtask : write_reg
+
+
+ // Translate the desired register access to a ctrlport read request.
+ task automatic read_user_reg(int port, byte unsigned addr, output logic [63:0] value);
+ blk_ctrl.reg_read(256*8*port + addr*8 + 0, value[31: 0]);
+ blk_ctrl.reg_read(256*8*port + addr*8 + 4, value[63:32]);
+ endtask : read_user_reg
+
+
+ // Set the interpolation rate
+ task automatic set_interp_rate(int port, int interp_rate);
+ begin
+ logic [7:0] cic_rate = 8'd0;
+ logic [7:0] hb_enables = 2'b0;
+
+ int _interp_rate = interp_rate;
+
+ // Calculate which half bands to enable and whatever is left over set the CIC
+ while ((_interp_rate[0] == 0) && (hb_enables < NUM_HB)) begin
+ hb_enables += 1'b1;
+ _interp_rate = _interp_rate >> 1;
+ end
+
+ // CIC rate cannot be set to 0
+ cic_rate = (_interp_rate[7:0] == 8'd0) ? 8'd1 : _interp_rate[7:0];
+ `ASSERT_ERROR(hb_enables <= NUM_HB, "Enabled halfbands may not exceed total number of half bands.");
+ `ASSERT_ERROR(cic_rate > 0 && cic_rate <= CIC_MAX_INTERP,
+ "CIC Interpolation rate must be positive, not exceed the max cic interpolation rate, and cannot equal 0!");
+
+ // Setup DUC
+ $display("Set interpolation to %0d", interp_rate);
+ $display("- Number of enabled HBs: %0d", hb_enables);
+ $display("- CIC Rate: %0d", cic_rate);
+ write_reg(port, SR_M_ADDR, interp_rate); // Set interpolation rate in AXI rate change
+ write_reg(port, SR_INTERP_ADDR, {hb_enables, cic_rate}); // Enable HBs, set CIC rate
+ end
+ endtask
+
+
+ // Test sending packets of ones
+ task automatic send_ones(int port, int interp_rate, bit has_time);
+ begin
+ const bit [63:0] start_time = 64'h0123456789ABCDEF;
+
+ set_interp_rate(port, interp_rate);
+
+ // Setup DUC
+ write_reg(port, SR_CONFIG_ADDR, 32'd1); // Enable clear EOB
+ write_reg(port, SR_FREQ_ADDR, 32'd0); // CORDIC phase increment
+ write_reg(port, SR_SCALE_IQ_ADDR, (1 << 14)); // Scaling, set to 1
+
+ fork
+ begin
+ chdr_word_t send_payload[$];
+ packet_info_t pkt_info;
+
+ $display("Send ones");
+
+ // Generate a payload of all ones
+ send_payload = {};
+ for (int i = 0; i < PKT_SIZE_BYTES/8; i++) begin
+ send_payload.push_back({16'hffff, 16'hffff, 16'hffff, 16'hffff});
+ end
+
+ // Send two packets with EOB on the second packet
+ pkt_info = 0;
+ pkt_info.has_time = has_time;
+ pkt_info.timestamp = start_time;
+ blk_ctrl.send_packets(port, send_payload, /*data_bytes*/, /*metadata*/, pkt_info);
+ pkt_info.timestamp = start_time + SPP;
+ pkt_info.eob = 1;
+ blk_ctrl.send_packets(port, send_payload, /*data_bytes*/, /*metadata*/, pkt_info);
+
+ $display("Send ones complete");
+ end
+ begin
+ string s;
+ chdr_word_t samples;
+ int data_bytes;
+ chdr_word_t recv_payload[$];
+ chdr_word_t metadata[$];
+ packet_info_t pkt_info;
+
+ $display("Check incoming samples");
+ for (int i = 0; i < 2*interp_rate; i++) begin
+ blk_ctrl.recv_adv(port, recv_payload, data_bytes, metadata, pkt_info);
+
+ // Check the packet size
+ $sformat(s, "incorrect (drop) packet size! expected: %0d, actual: %0d", PKT_SIZE_BYTES/8, recv_payload.size());
+ `ASSERT_ERROR(recv_payload.size() == PKT_SIZE_BYTES/8, s);
+
+ // Check the timestamp
+ if (has_time) begin
+ bit [63:0] expected_time;
+ // Calculate what the timestamp should be
+ expected_time = start_time + i * SPP;
+ $sformat(s, "Incorrect timestamp: has_time = %0d, timestamp = 0x%0X, expected 0x%0X",
+ pkt_info.has_time, pkt_info.timestamp, expected_time);
+ `ASSERT_ERROR(pkt_info.has_time == 1 && pkt_info.timestamp == expected_time, s);
+ end else begin
+ `ASSERT_ERROR(pkt_info.has_time == 0, "Packet has timestamp when it shouldn't");
+ end
+
+ // Check EOB
+ if (i == 2*interp_rate-1) begin
+ `ASSERT_ERROR(pkt_info.eob == 1, "EOB not set on last packet");
+ end else begin
+ `ASSERT_ERROR(pkt_info.eob == 0,
+ $sformatf("EOB unexpectedly set on packet %0d", i));
+ end
+
+ // Check the sample values
+ samples = 64'd0;
+ for (int j = 0; j < PKT_SIZE_BYTES/8; j++) begin
+ samples = recv_payload[j];
+ $sformat(s, "Ramp word %0d invalid! Expected a real value, Received: %0d", 2*j, samples);
+ `ASSERT_ERROR(samples >= 0, s);
+ end
+ end
+ $display("Check complete");
+ end
+ join
+ end
+ endtask
+
+
+ //---------------------------------------------------------------------------
+ // Test Process
+ //---------------------------------------------------------------------------
+
+ initial begin : tb_main
+ const int port = 0;
+ test.start_tb("rfnoc_block_duc_tb");
+
+ // Start the BFMs running
+ blk_ctrl.run();
+
+
+ //-------------------------------------------------------------------------
+ // Reset
+ //-------------------------------------------------------------------------
+
+ test.start_test("Wait for Reset", 10us);
+ fork
+ blk_ctrl.reset_chdr();
+ blk_ctrl.reset_ctrl();
+ join;
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Check NoC ID and Block Info
+ //-------------------------------------------------------------------------
+
+ test.start_test("Verify Block Info", 2us);
+ `ASSERT_ERROR(blk_ctrl.get_noc_id() == rfnoc_block_duc_i.NOC_ID, "Incorrect NOC_ID value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect NUM_DATA_I value");
+ `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect NUM_DATA_O value");
+ `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU value");
+ test.end_test();
+
+
+ //-------------------------------------------------------------------------
+ // Test read-back regs
+ //-------------------------------------------------------------------------
+
+ begin
+ logic [63:0] val64;
+ test.start_test("Test registers", 10us);
+ read_user_reg(port, RB_NUM_HB, val64);
+ `ASSERT_ERROR(val64 == NUM_HB, "Register NUM_HB didn't read back expected value");
+ read_user_reg(port, RB_CIC_MAX_INTERP, val64);
+ `ASSERT_ERROR(val64 ==CIC_MAX_INTERP, "Register RB_CIC_MAX_INTERP didn't read back expected value");
+ test.end_test();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Test various interpolation rates (no timestamp)
+ //-------------------------------------------------------------------------
+
+ begin
+ test.start_test("Test interpolation rates (with timestamp)", 0.5ms);
+
+ $display("Note: This test will take a long time!");
+ send_ones(port, 1, 1); // HBs enabled: 0, CIC rate: 1
+ send_ones(port, 2, 1); // HBs enabled: 1, CIC rate: 1
+ send_ones(port, 3, 1); // HBs enabled: 0, CIC rate: 3
+ send_ones(port, 4, 1); // HBs enabled: 2, CIC rate: 1
+ send_ones(port, 6, 1); // HBs enabled: 1, CIC rate: 3
+ send_ones(port, 8, 1); // HBs enabled: 2, CIC rate: 2
+ send_ones(port, 12, 1); // HBs enabled: 2, CIC rate: 3
+ send_ones(port, 13, 1); // HBs enabled: 0, CIC rate: 13
+ send_ones(port, 16, 1); // HBs enabled: 2, CIC rate: 3
+ send_ones(port, 40, 1); // HBs enabled: 2, CIC rate: 20
+
+ test.end_test();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Test various interpolation rates (without timestamp)
+ //-------------------------------------------------------------------------
+
+ begin
+ test.start_test("Test interpolation rates (no timestamp)", 0.5ms);
+
+ send_ones(port, 1, 0); // HBs enabled: 0, CIC rate: 1
+ send_ones(port, 3, 0); // HBs enabled: 0, CIC rate: 3
+
+ test.end_test();
+ end
+
+
+ //-------------------------------------------------------------------------
+ // Test timed tune
+ //-------------------------------------------------------------------------
+
+ // This test has not been implemented because the RFNoC FFT has not been
+ // ported yet.
+
+
+ //-------------------------------------------------------------------------
+ // Finish
+ //-------------------------------------------------------------------------
+
+ // End the TB, but don't $finish, since we don't want to kill other
+ // instances of this testbench that may be running.
+ test.end_tb(0);
+
+ // Kill the clocks to end this instance of the testbench
+ rfnoc_chdr_clk_gen.kill();
+ rfnoc_ctrl_clk_gen.kill();
+ duc_clk_gen.kill();
+ end
+endmodule