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