diff options
author | Wade Fife <wade.fife@ettus.com> | 2020-05-14 09:07:09 -0500 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2020-05-28 15:04:05 -0500 |
commit | 5134b6caea58da825c4da1888a4d26888acc126a (patch) | |
tree | fb576fa8af08a11325fce7eec1094eae6d0fa6d4 /fpga | |
parent | 902c6f4488d5a75cf7a83cc1dc998d42703b0929 (diff) | |
download | uhd-5134b6caea58da825c4da1888a4d26888acc126a.tar.gz uhd-5134b6caea58da825c4da1888a4d26888acc126a.tar.bz2 uhd-5134b6caea58da825c4da1888a4d26888acc126a.zip |
fpga: rfnoc: Add RFNoC Add/Sub block
Diffstat (limited to 'fpga')
7 files changed, 1190 insertions, 10 deletions
diff --git a/fpga/usrp3/lib/hls/addsub_hls/Makefile.inc b/fpga/usrp3/lib/hls/addsub_hls/Makefile.inc index 0e2f0737a..03976bce8 100644 --- a/fpga/usrp3/lib/hls/addsub_hls/Makefile.inc +++ b/fpga/usrp3/lib/hls/addsub_hls/Makefile.inc @@ -1,22 +1,19 @@ # -# Copyright 2015-2017 Ettus Research -# Copyright 2016 Ettus Research, a National Instruments Company +# Copyright 2020 Ettus Research, a National Instruments Brand # # SPDX-License-Identifier: LGPL-3.0-or-later # # Add C/C++/tcl files relative to usrp3/lib/hls/<ip> directory -HLS_IP_ADDSUB_HLS_SRCS = \ +HLS_IP_ADDSUB_HLS_LIB_SRCS = $(addprefix $(HLS_IP_DIR)/addsub_hls/, \ addsub_hls.cpp \ -addsub_hls.tcl - -HLS_IP_ADDSUB_HLS_OUTS = $(addprefix $(IP_BUILD_DIR)/addsub_hls/, \ -solution/impl/verilog/addsub_hls.v \ +addsub_hls.tcl \ ) -# Sources in lib directory -HLS_IP_ADDSUB_HLS_LIB_SRCS = $(addprefix $(HLS_IP_DIR)/addsub_hls/, $(HLS_IP_ADDSUB_HLS_SRCS)) +# HLS output artifact points to the ip/hdl/verilog folder. The build process +# will glob all the files in this directory, including *.dat files. +HLS_IP_ADDSUB_HLS_OUTS = $(IP_BUILD_DIR)/addsub_hls/solution/impl/ip/hdl/verilog # Build with HLS $(HLS_IP_ADDSUB_HLS_OUTS) : $(HLS_IP_ADDSUB_HLS_LIB_SRCS) - $(call BUILD_VIVADO_HLS_IP,addsub_hls,$(PART_ID),$(HLS_IP_ADDSUB_HLS_LIB_SRCS),$(HLS_IP_DIR),$(IP_BUILD_DIR),) + $(call BUILD_VIVADO_HLS_IP,addsub_hls,$(PART_ID),$(HLS_IP_ADDSUB_HLS_LIB_SRCS),$(HLS_IP_DIR),$(IP_BUILD_DIR),)
\ No newline at end of file diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/Makefile b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/Makefile new file mode 100644 index 000000000..0c825f41d --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/Makefile @@ -0,0 +1,47 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +# Define BASE_DIR to point to the "top" dir. Note: +# UHD_FPGA_DIR must be passed into this Makefile. +BASE_DIR = ../../../../top +# Include viv_sim_preample after defining BASE_DIR +include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak + +#------------------------------------------------- +# 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 $(BASE_DIR)/../lib/hls/Makefile.inc +include Makefile.srcs + +DESIGN_SRCS += $(abspath \ +$(RFNOC_CORE_SRCS) \ +$(RFNOC_UTIL_SRCS) \ +$(RFNOC_OOT_SRCS) \ +$(HLS_IP_ADDSUB_HLS_OUTS) \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +SIM_TOP = rfnoc_block_addsub_all_tb +SIM_SRCS = \ +$(abspath rfnoc_block_addsub_tb.sv) \ +$(abspath rfnoc_block_addsub_all_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_addsub/Makefile.srcs b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/Makefile.srcs new file mode 100644 index 000000000..2fb71b41f --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/Makefile.srcs @@ -0,0 +1,22 @@ +# +# Copyright 2020 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +################################################## +# RFNoC Block Sources +################################################## +# Here, list all the files that are necessary to synthesize this block. Don't +# include testbenches! +# Make sure that the source files are nicely detectable by a regex. Best to put +# one on each line. +# The first argument to addprefix is the current path to this Makefile, so the +# path list is always absolute, regardless of from where we're including or +# calling this file. RFNOC_OOT_SRCS needs to be a simply expanded variable +# (not a recursively expanded variable), and we take care of that in the build +# infrastructure. +RFNOC_OOT_SRCS += $(addprefix $(dir $(abspath $(lastword $(MAKEFILE_LIST)))), \ +rfnoc_block_addsub.v \ +noc_shell_addsub.v \ +) diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/noc_shell_addsub.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/noc_shell_addsub.v new file mode 100644 index 000000000..333f6cbfb --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/noc_shell_addsub.v @@ -0,0 +1,345 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: noc_shell_addsub +// +// Description: +// +// This is a tool-generated NoC-shell for the addsub block. +// See the RFNoC specification for more information about NoC shells. +// +// Parameters: +// +// THIS_PORTID : Control crossbar port to which this block is connected +// CHDR_W : AXIS-CHDR data bus width +// MTU : Maximum transmission unit (i.e., maximum packet size in +// + +`default_nettype none + + +module noc_shell_addsub #( + parameter [9:0] THIS_PORTID = 10'd0, + parameter CHDR_W = 64, + parameter [5:0] MTU = 10, + parameter USE_IMPL = "Verilog" + +) ( + //--------------------- + // Framework Interface + //--------------------- + + // RFNoC Framework Clocks + input wire rfnoc_chdr_clk, + input wire rfnoc_ctrl_clk, + input wire ce_clk, + + // NoC Shell Generated Resets + output wire rfnoc_chdr_rst, + output wire rfnoc_ctrl_rst, + output wire ce_rst, + + // RFNoC Backend Interface + input wire [511:0] rfnoc_core_config, + output wire [511:0] rfnoc_core_status, + + // AXIS-CHDR Input Ports (from framework) + input wire [(2)*CHDR_W-1:0] s_rfnoc_chdr_tdata, + input wire [(2)-1:0] s_rfnoc_chdr_tlast, + input wire [(2)-1:0] s_rfnoc_chdr_tvalid, + output wire [(2)-1:0] s_rfnoc_chdr_tready, + // AXIS-CHDR Output Ports (to framework) + output wire [(2)*CHDR_W-1:0] m_rfnoc_chdr_tdata, + output wire [(2)-1:0] m_rfnoc_chdr_tlast, + output wire [(2)-1:0] m_rfnoc_chdr_tvalid, + input wire [(2)-1:0] m_rfnoc_chdr_tready, + + // AXIS-Ctrl Control 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 Control 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 Interface + //--------------------- + + // AXI-Stream Payload Context Clock and Reset + output wire axis_data_clk, + output wire axis_data_rst, + // Payload Stream to User Logic: in_a + output wire [32*1-1:0] m_in_a_payload_tdata, + output wire [1-1:0] m_in_a_payload_tkeep, + output wire m_in_a_payload_tlast, + output wire m_in_a_payload_tvalid, + input wire m_in_a_payload_tready, + // Context Stream to User Logic: in_a + output wire [CHDR_W-1:0] m_in_a_context_tdata, + output wire [3:0] m_in_a_context_tuser, + output wire m_in_a_context_tlast, + output wire m_in_a_context_tvalid, + input wire m_in_a_context_tready, + // Payload Stream to User Logic: in_b + output wire [32*1-1:0] m_in_b_payload_tdata, + output wire [1-1:0] m_in_b_payload_tkeep, + output wire m_in_b_payload_tlast, + output wire m_in_b_payload_tvalid, + input wire m_in_b_payload_tready, + // Context Stream to User Logic: in_b + output wire [CHDR_W-1:0] m_in_b_context_tdata, + output wire [3:0] m_in_b_context_tuser, + output wire m_in_b_context_tlast, + output wire m_in_b_context_tvalid, + input wire m_in_b_context_tready, + // Payload Stream from User Logic: add + input wire [32*1-1:0] s_add_payload_tdata, + input wire [0:0] s_add_payload_tkeep, + input wire s_add_payload_tlast, + input wire s_add_payload_tvalid, + output wire s_add_payload_tready, + // Context Stream from User Logic: add + input wire [CHDR_W-1:0] s_add_context_tdata, + input wire [3:0] s_add_context_tuser, + input wire s_add_context_tlast, + input wire s_add_context_tvalid, + output wire s_add_context_tready, + // Payload Stream from User Logic: sub + input wire [32*1-1:0] s_sub_payload_tdata, + input wire [0:0] s_sub_payload_tkeep, + input wire s_sub_payload_tlast, + input wire s_sub_payload_tvalid, + output wire s_sub_payload_tready, + // Context Stream from User Logic: sub + input wire [CHDR_W-1:0] s_sub_context_tdata, + input wire [3:0] s_sub_context_tuser, + input wire s_sub_context_tlast, + input wire s_sub_context_tvalid, + output wire s_sub_context_tready +); + + //--------------------------------------------------------------------------- + // 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 (32'hADD00000), + .NUM_DATA_I (2), + .NUM_DATA_O (2), + .CTRL_FIFOSIZE ($clog2(2)), + .MTU (MTU) + ) backend_iface_i ( + .rfnoc_chdr_clk (rfnoc_chdr_clk), + .rfnoc_chdr_rst (rfnoc_chdr_rst), + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .rfnoc_ctrl_rst (rfnoc_ctrl_rst), + .rfnoc_core_config (rfnoc_core_config), + .rfnoc_core_status (rfnoc_core_status), + .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) + ); + + //--------------------------------------------------------------------------- + // Reset Generation + //--------------------------------------------------------------------------- + + wire ce_rst_pulse; + + pulse_synchronizer #(.MODE ("POSEDGE")) pulse_synchronizer_ce_clk ( + .clk_a(rfnoc_chdr_clk), .rst_a(1'b0), .pulse_a (rfnoc_chdr_rst), .busy_a (), + .clk_b(ce_clk), .pulse_b (ce_rst_pulse) + ); + + pulse_stretch_min #(.LENGTH(32)) pulse_stretch_min_ce_clk ( + .clk(ce_clk), .rst(1'b0), + .pulse_in(ce_rst_pulse), .pulse_out(ce_rst) + ); + + //--------------------------------------------------------------------------- + // Control Path + //--------------------------------------------------------------------------- + + // No control path for this block + assign s_rfnoc_ctrl_tready = 1'b1; + assign m_rfnoc_ctrl_tdata = 32'b0; + assign m_rfnoc_ctrl_tlast = 1'b0; + assign m_rfnoc_ctrl_tvalid = 1'b0; + + //--------------------------------------------------------------------------- + // Data Path + //--------------------------------------------------------------------------- + + genvar i; + + assign axis_data_clk = ce_clk; + assign axis_data_rst = ce_rst; + + //--------------------- + // Input Data Paths + //--------------------- + + chdr_to_axis_pyld_ctxt #( + .CHDR_W (CHDR_W), + .ITEM_W (32), + .NIPC (1), + .SYNC_CLKS (0), + .CONTEXT_FIFO_SIZE ($clog2(2)), + .PAYLOAD_FIFO_SIZE ($clog2(2)), + .CONTEXT_PREFETCH_EN (1) + ) chdr_to_axis_pyld_ctxt_in_in_a ( + .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[(0)*CHDR_W+:CHDR_W]), + .s_axis_chdr_tlast (s_rfnoc_chdr_tlast[0]), + .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid[0]), + .s_axis_chdr_tready (s_rfnoc_chdr_tready[0]), + .m_axis_payload_tdata (m_in_a_payload_tdata), + .m_axis_payload_tkeep (m_in_a_payload_tkeep), + .m_axis_payload_tlast (m_in_a_payload_tlast), + .m_axis_payload_tvalid (m_in_a_payload_tvalid), + .m_axis_payload_tready (m_in_a_payload_tready), + .m_axis_context_tdata (m_in_a_context_tdata), + .m_axis_context_tuser (m_in_a_context_tuser), + .m_axis_context_tlast (m_in_a_context_tlast), + .m_axis_context_tvalid (m_in_a_context_tvalid), + .m_axis_context_tready (m_in_a_context_tready), + .flush_en (data_i_flush_en), + .flush_timeout (data_i_flush_timeout), + .flush_active (data_i_flush_active[0]), + .flush_done (data_i_flush_done[0]) + ); + + chdr_to_axis_pyld_ctxt #( + .CHDR_W (CHDR_W), + .ITEM_W (32), + .NIPC (1), + .SYNC_CLKS (0), + .CONTEXT_FIFO_SIZE ($clog2(2)), + .PAYLOAD_FIFO_SIZE ($clog2(2)), + .CONTEXT_PREFETCH_EN (1) + ) chdr_to_axis_pyld_ctxt_in_in_b ( + .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[(1)*CHDR_W+:CHDR_W]), + .s_axis_chdr_tlast (s_rfnoc_chdr_tlast[1]), + .s_axis_chdr_tvalid (s_rfnoc_chdr_tvalid[1]), + .s_axis_chdr_tready (s_rfnoc_chdr_tready[1]), + .m_axis_payload_tdata (m_in_b_payload_tdata), + .m_axis_payload_tkeep (m_in_b_payload_tkeep), + .m_axis_payload_tlast (m_in_b_payload_tlast), + .m_axis_payload_tvalid (m_in_b_payload_tvalid), + .m_axis_payload_tready (m_in_b_payload_tready), + .m_axis_context_tdata (m_in_b_context_tdata), + .m_axis_context_tuser (m_in_b_context_tuser), + .m_axis_context_tlast (m_in_b_context_tlast), + .m_axis_context_tvalid (m_in_b_context_tvalid), + .m_axis_context_tready (m_in_b_context_tready), + .flush_en (data_i_flush_en), + .flush_timeout (data_i_flush_timeout), + .flush_active (data_i_flush_active[1]), + .flush_done (data_i_flush_done[1]) + ); + + //--------------------- + // Output Data Paths + //--------------------- + + axis_pyld_ctxt_to_chdr #( + .CHDR_W (CHDR_W), + .ITEM_W (32), + .NIPC (1), + .SYNC_CLKS (0), + .CONTEXT_FIFO_SIZE ($clog2(2)), + .PAYLOAD_FIFO_SIZE ($clog2(2)), + .MTU (MTU), + .CONTEXT_PREFETCH_EN (1) + ) axis_pyld_ctxt_to_chdr_out_add ( + .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[(0)*CHDR_W+:CHDR_W]), + .m_axis_chdr_tlast (m_rfnoc_chdr_tlast[0]), + .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid[0]), + .m_axis_chdr_tready (m_rfnoc_chdr_tready[0]), + .s_axis_payload_tdata (s_add_payload_tdata), + .s_axis_payload_tkeep (s_add_payload_tkeep), + .s_axis_payload_tlast (s_add_payload_tlast), + .s_axis_payload_tvalid (s_add_payload_tvalid), + .s_axis_payload_tready (s_add_payload_tready), + .s_axis_context_tdata (s_add_context_tdata), + .s_axis_context_tuser (s_add_context_tuser), + .s_axis_context_tlast (s_add_context_tlast), + .s_axis_context_tvalid (s_add_context_tvalid), + .s_axis_context_tready (s_add_context_tready), + .framer_errors (), + .flush_en (data_o_flush_en), + .flush_timeout (data_o_flush_timeout), + .flush_active (data_o_flush_active[0]), + .flush_done (data_o_flush_done[0]) + ); + + axis_pyld_ctxt_to_chdr #( + .CHDR_W (CHDR_W), + .ITEM_W (32), + .NIPC (1), + .SYNC_CLKS (0), + .CONTEXT_FIFO_SIZE ($clog2(2)), + .PAYLOAD_FIFO_SIZE ($clog2(2)), + .MTU (MTU), + .CONTEXT_PREFETCH_EN (1) + ) axis_pyld_ctxt_to_chdr_out_sub ( + .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[(1)*CHDR_W+:CHDR_W]), + .m_axis_chdr_tlast (m_rfnoc_chdr_tlast[1]), + .m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid[1]), + .m_axis_chdr_tready (m_rfnoc_chdr_tready[1]), + .s_axis_payload_tdata (s_sub_payload_tdata), + .s_axis_payload_tkeep (s_sub_payload_tkeep), + .s_axis_payload_tlast (s_sub_payload_tlast), + .s_axis_payload_tvalid (s_sub_payload_tvalid), + .s_axis_payload_tready (s_sub_payload_tready), + .s_axis_context_tdata (s_sub_context_tdata), + .s_axis_context_tuser (s_sub_context_tuser), + .s_axis_context_tlast (s_sub_context_tlast), + .s_axis_context_tvalid (s_sub_context_tvalid), + .s_axis_context_tready (s_sub_context_tready), + .framer_errors (), + .flush_en (data_o_flush_en), + .flush_timeout (data_o_flush_timeout), + .flush_active (data_o_flush_active[1]), + .flush_done (data_o_flush_done[1]) + ); + +endmodule // noc_shell_addsub + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub.v b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub.v new file mode 100644 index 000000000..083bc35b3 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub.v @@ -0,0 +1,336 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_addsub +// +// Description: +// +// This block takes in two streams and adds and subtracts them, creating two +// output streams with the sum and difference of the input streams. It +// assumes the input and output packets are all the same length and use sc16 +// samples. +// +// This block also demonstrates how to use Verilog, VHDL and/or +// high-level-synthesis (HLS) in a design. You can set the USE_IMPL parameter +// to control which implementation is used. +// +// Parameters: +// +// THIS_PORTID : Control crossbar port to which this block is connected +// CHDR_W : AXIS-CHDR data bus width +// MTU : Maximum transmission unit (i.e., maximum packet size in CHDR +// words is 2**MTU). +// USE_IMPL : Indicates which implementation to use. This is a string that +// can be set to "Verilog", "VHDL", or "HLS". +// +`default_nettype none + + +module rfnoc_block_addsub #( + parameter [9:0] THIS_PORTID = 10'd0, + parameter CHDR_W = 64, + parameter [5:0] MTU = 10, + parameter USE_IMPL = "Verilog" +) ( + // RFNoC Framework Clocks and Resets + input wire rfnoc_chdr_clk, + input wire rfnoc_ctrl_clk, + input wire ce_clk, + // RFNoC Backend Interface + input wire [ 511:0] rfnoc_core_config, + output wire [ 511:0] rfnoc_core_status, + // AXIS-CHDR Input Ports (from framework) + input wire [2*CHDR_W-1:0] s_rfnoc_chdr_tdata, + input wire [ 2-1:0] s_rfnoc_chdr_tlast, + input wire [ 2-1:0] s_rfnoc_chdr_tvalid, + output wire [ 2-1:0] s_rfnoc_chdr_tready, + // AXIS-CHDR Output Ports (to framework) + output wire [2*CHDR_W-1:0] m_rfnoc_chdr_tdata, + output wire [ 2-1:0] m_rfnoc_chdr_tlast, + output wire [ 2-1:0] m_rfnoc_chdr_tvalid, + input wire [ 2-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 +); + + // This block currently only supports 64-bit CHDR + if (CHDR_W != 64) begin + CHDR_W_must_be_64_for_the_addsub_block(); + end + + + //--------------------------------------------------------------------------- + // Signal Declarations + //--------------------------------------------------------------------------- + + // Clocks and Resets + wire axis_data_clk; + wire axis_data_rst; + // Payload Stream to User Logic: in_a + wire [32*1-1:0] m_in_a_payload_tdata; + wire m_in_a_payload_tlast; + wire m_in_a_payload_tvalid; + wire m_in_a_payload_tready; + // Context Stream to User Logic: in_a + wire [CHDR_W-1:0] m_in_a_context_tdata; + wire [3:0] m_in_a_context_tuser; + wire m_in_a_context_tlast; + wire m_in_a_context_tvalid; + wire m_in_a_context_tready; + // Payload Stream to User Logic: in_b + wire [32*1-1:0] m_in_b_payload_tdata; + wire m_in_b_payload_tlast; + wire m_in_b_payload_tvalid; + wire m_in_b_payload_tready; + // Context Stream to User Logic: in_b + wire m_in_b_context_tready; + // Payload Stream from User Logic: add + wire [32*1-1:0] s_add_payload_tdata; + wire s_add_payload_tlast; + wire s_add_payload_tvalid; + wire s_add_payload_tready; + // Context Stream from User Logic: add + wire [CHDR_W-1:0] s_add_context_tdata; + wire [3:0] s_add_context_tuser; + wire s_add_context_tlast; + wire s_add_context_tvalid; + wire s_add_context_tready; + // Payload Stream from User Logic: sub + wire [32*1-1:0] s_sub_payload_tdata; + wire s_sub_payload_tlast; + wire s_sub_payload_tvalid; + wire s_sub_payload_tready; + // Context Stream from User Logic: sub + wire [CHDR_W-1:0] s_sub_context_tdata; + wire [3:0] s_sub_context_tuser; + wire s_sub_context_tlast; + wire s_sub_context_tvalid; + wire s_sub_context_tready; + + + //--------------------------------------------------------------------------- + // NoC Shell + //--------------------------------------------------------------------------- + + noc_shell_addsub #( + .CHDR_W (CHDR_W), + .THIS_PORTID (THIS_PORTID), + .MTU (MTU), + .USE_IMPL (USE_IMPL) + ) noc_shell_addsub_i ( + //--------------------- + // Framework Interface + //--------------------- + + // Clock Inputs + .rfnoc_chdr_clk (rfnoc_chdr_clk), + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .ce_clk (ce_clk), + // Reset Outputs + .rfnoc_chdr_rst (), + .rfnoc_ctrl_rst (), + .ce_rst (), + // RFNoC Backend Interface + .rfnoc_core_config (rfnoc_core_config), + .rfnoc_core_status (rfnoc_core_status), + // CHDR Input Ports (from framework) + .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), + // CHDR Output Ports (to framework) + .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), + // AXIS-Ctrl Input Port (from framework) + .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), + // AXIS-Ctrl Output Port (to framework) + .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), + + //--------------------- + // Client Interface + //--------------------- + + // AXI-Stream Payload Context Clock and Reset + .axis_data_clk (axis_data_clk), + .axis_data_rst (axis_data_rst), + // Payload Stream to User Logic: in_a + .m_in_a_payload_tdata (m_in_a_payload_tdata), + .m_in_a_payload_tkeep (), + .m_in_a_payload_tlast (m_in_a_payload_tlast), + .m_in_a_payload_tvalid (m_in_a_payload_tvalid), + .m_in_a_payload_tready (m_in_a_payload_tready), + // Context Stream to User Logic: in_a + .m_in_a_context_tdata (m_in_a_context_tdata), + .m_in_a_context_tuser (m_in_a_context_tuser), + .m_in_a_context_tlast (m_in_a_context_tlast), + .m_in_a_context_tvalid (m_in_a_context_tvalid), + .m_in_a_context_tready (m_in_a_context_tready), + // Payload Stream to User Logic: in_b + .m_in_b_payload_tdata (m_in_b_payload_tdata), + .m_in_b_payload_tkeep (), + .m_in_b_payload_tlast (m_in_b_payload_tlast), + .m_in_b_payload_tvalid (m_in_b_payload_tvalid), + .m_in_b_payload_tready (m_in_b_payload_tready), + // Context Stream to User Logic: in_b + .m_in_b_context_tdata (), + .m_in_b_context_tuser (), + .m_in_b_context_tlast (), + .m_in_b_context_tvalid (), + .m_in_b_context_tready (m_in_b_context_tready), + // Payload Stream from User Logic: add + .s_add_payload_tdata (s_add_payload_tdata), + .s_add_payload_tkeep (1'b1), + .s_add_payload_tlast (s_add_payload_tlast), + .s_add_payload_tvalid (s_add_payload_tvalid), + .s_add_payload_tready (s_add_payload_tready), + // Context Stream from User Logic: add + .s_add_context_tdata (s_add_context_tdata), + .s_add_context_tuser (s_add_context_tuser), + .s_add_context_tlast (s_add_context_tlast), + .s_add_context_tvalid (s_add_context_tvalid), + .s_add_context_tready (s_add_context_tready), + // Payload Stream from User Logic: diff + .s_sub_payload_tdata (s_sub_payload_tdata), + .s_sub_payload_tkeep (1'b1), + .s_sub_payload_tlast (s_sub_payload_tlast), + .s_sub_payload_tvalid (s_sub_payload_tvalid), + .s_sub_payload_tready (s_sub_payload_tready), + // Context Stream from User Logic: diff + .s_sub_context_tdata (s_sub_context_tdata), + .s_sub_context_tuser (s_sub_context_tuser), + .s_sub_context_tlast (s_sub_context_tlast), + .s_sub_context_tvalid (s_sub_context_tvalid), + .s_sub_context_tready (s_sub_context_tready) + ); + + + //--------------------------------------------------------------------------- + // Context Handling + //--------------------------------------------------------------------------- + + // We use the A input to control the packet size and other attributes of the + // output packets. So we duplicate the A context and discard the B context. + assign m_in_b_context_tready = 1; + + axis_split #( + .DATA_W (1 + 4 + CHDR_W), // TLAST + TUSER + TDATA + .NUM_PORTS (2) + ) axis_split_i ( + .clk (axis_data_clk), + .rst (axis_data_rst), + .s_axis_tdata ({m_in_a_context_tlast, + m_in_a_context_tuser, + m_in_a_context_tdata}), + .s_axis_tvalid (m_in_a_context_tvalid), + .s_axis_tready (m_in_a_context_tready), + .m_axis_tdata ({s_sub_context_tlast, + s_sub_context_tuser, + s_sub_context_tdata, + s_add_context_tlast, + s_add_context_tuser, + s_add_context_tdata}), + .m_axis_tvalid ({s_sub_context_tvalid, s_add_context_tvalid}), + .m_axis_tready ({s_sub_context_tready, s_add_context_tready}) + ); + + + //--------------------------------------------------------------------------- + // Add/Subtract logic + //--------------------------------------------------------------------------- + + generate + if (USE_IMPL == "HLS") begin : gen_hls + // Use the module generated through Vivado High-Level Synthesis (see + // addsub_hls.cpp). + addsub_hls addsub_hls_i ( + .ap_clk (axis_data_clk), + .ap_rst_n (~axis_data_rst), + .a_TDATA (m_in_a_payload_tdata), + .a_TVALID (m_in_a_payload_tvalid), + .a_TREADY (m_in_a_payload_tready), + .a_TLAST (m_in_a_payload_tlast), + .b_TDATA (m_in_b_payload_tdata), + .b_TVALID (m_in_b_payload_tvalid), + .b_TREADY (m_in_b_payload_tready), + .b_TLAST (m_in_b_payload_tlast), + .add_TDATA (s_add_payload_tdata), + .add_TVALID (s_add_payload_tvalid), + .add_TREADY (s_add_payload_tready), + .add_TLAST (s_add_payload_tlast), + .sub_TDATA (s_sub_payload_tdata), + .sub_TVALID (s_sub_payload_tvalid), + .sub_TREADY (s_sub_payload_tready), + .sub_TLAST (s_sub_payload_tlast) + ); + end else if (USE_IMPL == "VHDL") begin : gen_vhdl + // Use the VHDL implementation + addsub_vhdl #( + .width_g (16) + ) addsub_vhdl_i ( + .clk_i (axis_data_clk), + .rst_i (axis_data_rst), + .i0_tdata (m_in_a_payload_tdata), + .i0_tlast (m_in_a_payload_tlast), + .i0_tvalid (m_in_a_payload_tvalid), + .i0_tready (m_in_a_payload_tready), + .i1_tdata (m_in_b_payload_tdata), + .i1_tlast (m_in_b_payload_tlast), + .i1_tvalid (m_in_b_payload_tvalid), + .i1_tready (m_in_b_payload_tready), + .sum_tdata (s_add_payload_tdata), + .sum_tlast (s_add_payload_tlast), + .sum_tvalid (s_add_payload_tvalid), + .sum_tready (s_add_payload_tready), + .diff_tdata (s_sub_payload_tdata), + .diff_tlast (s_sub_payload_tlast), + .diff_tvalid (s_sub_payload_tvalid), + .diff_tready (s_sub_payload_tready) + ); + end else begin : gen_verilog + // Use Verilog implementation + addsub #( + .WIDTH (16) + ) inst_addsub ( + .clk (axis_data_clk), + .reset (axis_data_rst), + .i0_tdata (m_in_a_payload_tdata), + .i0_tlast (m_in_a_payload_tlast), + .i0_tvalid (m_in_a_payload_tvalid), + .i0_tready (m_in_a_payload_tready), + .i1_tdata (m_in_b_payload_tdata), + .i1_tlast (m_in_b_payload_tlast), + .i1_tvalid (m_in_b_payload_tvalid), + .i1_tready (m_in_b_payload_tready), + .sum_tdata (s_add_payload_tdata), + .sum_tlast (s_add_payload_tlast), + .sum_tvalid (s_add_payload_tvalid), + .sum_tready (s_add_payload_tready), + .diff_tdata (s_sub_payload_tdata), + .diff_tlast (s_sub_payload_tlast), + .diff_tvalid (s_sub_payload_tvalid), + .diff_tready (s_sub_payload_tready) + ); + end + endgenerate + +endmodule // rfnoc_block_addsub + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub_all_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub_all_tb.sv new file mode 100644 index 000000000..faf871606 --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub_all_tb.sv @@ -0,0 +1,28 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_addsub_all_tb +// +// Description: +// +// Top-level testbench for the Add/Sub RFNoC block. This instantiates +// rfnoc_block_addsub_tb with different parameters to test multiple +// configurations. +// + +`default_nettype none + + +module rfnoc_block_addsub_all_tb; + + // Test all three implementations + rfnoc_block_addsub_tb #(.USE_IMPL("Verilog")) test_verilog (); + rfnoc_block_addsub_tb #(.USE_IMPL("VHDL")) test_vhdl (); + rfnoc_block_addsub_tb #(.USE_IMPL("HLS")) test_hls (); + +endmodule : rfnoc_block_addsub_all_tb + + +`default_nettype wire diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub_tb.sv new file mode 100644 index 000000000..10e9f968f --- /dev/null +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_addsub/rfnoc_block_addsub_tb.sv @@ -0,0 +1,405 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_addsub_tb +// +// Description: Testbench for the addsub RFNoC block. +// +// Parameters: +// +// USE_IMPL : Specifies the implementation string to pass to +// rfnoc_block_addsub. +// + +`default_nettype none + + +module rfnoc_block_addsub_tb #( + parameter string USE_IMPL = "Verilog" +); + + `include "test_exec.svh" + + import PkgTestExec::*; + import PkgChdrUtils::*; + import PkgRfnocBlockCtrlBfm::*; + import PkgRfnocItemUtils::*; + + + //--------------------------------------------------------------------------- + // Testbench Configuration + //--------------------------------------------------------------------------- + + localparam [31:0] NOC_ID = 32'hADD00000; + localparam [ 9:0] THIS_PORTID = 10'h123; + localparam int CHDR_W = 64; // CHDR size in bits + localparam int MTU = 10; // Log2 of max transmission unit in CHDR words + localparam int NUM_PORTS_I = 2; + localparam int NUM_PORTS_O = 2; + localparam int ITEM_W = 32; // Sample size in bits + localparam int SPP = 64; // Samples per packet + localparam int PKT_SIZE_BYTES = SPP * (ITEM_W/8); + localparam int STALL_PROB = 25; // Default BFM stall probability + localparam real CHDR_CLK_PER = 5.0; // 200 MHz + localparam real CTRL_CLK_PER = 8.0; // 125 MHz + localparam real CE_CLK_PER = 4.0; // 250 MHz + + // Port numbers for A, B, SUM and DIFF + localparam IN_PORT_A = 0; + localparam IN_PORT_B = 1; + localparam OUT_PORT_SUM = 0; + localparam OUT_PORT_DIFF = 1; + + + //--------------------------------------------------------------------------- + // Clocks and Resets + //--------------------------------------------------------------------------- + + bit rfnoc_chdr_clk; + bit rfnoc_ctrl_clk; + bit ce_clk; + + sim_clock_gen #(.PERIOD(CHDR_CLK_PER), .AUTOSTART(0)) + rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst()); + sim_clock_gen #(.PERIOD(CTRL_CLK_PER), .AUTOSTART(0)) + rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst()); + sim_clock_gen #(.PERIOD(CE_CLK_PER), .AUTOSTART(0)) + ce_clk_gen (.clk(ce_clk), .rst()); + + + //--------------------------------------------------------------------------- + // Bus Functional Models + //--------------------------------------------------------------------------- + + // Backend Interface + RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); + + // AXIS-Ctrl Interface + AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); + AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); + + // AXIS-CHDR Interfaces + AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS_I] (rfnoc_chdr_clk, 1'b0); + AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS_O] (rfnoc_chdr_clk, 1'b0); + + // Block Controller BFM + RfnocBlockCtrlBfm #(CHDR_W, ITEM_W) blk_ctrl = new(backend, m_ctrl, s_ctrl); + + // CHDR word and item/sample data types + typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; + typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t; + + // Connect block controller to BFMs + for (genvar i = 0; i < NUM_PORTS_I; i++) begin : gen_bfm_input_connections + initial begin + blk_ctrl.connect_master_data_port(i, m_chdr[i], PKT_SIZE_BYTES); + blk_ctrl.set_master_stall_prob(i, STALL_PROB); + end + end + for (genvar i = 0; i < NUM_PORTS_O; i++) begin : gen_bfm_output_connections + initial begin + blk_ctrl.connect_slave_data_port(i, s_chdr[i]); + blk_ctrl.set_slave_stall_prob(i, STALL_PROB); + end + end + + + //--------------------------------------------------------------------------- + // Device Under Test (DUT) + //--------------------------------------------------------------------------- + + // DUT Slave (Input) Port Signals + logic [CHDR_W*NUM_PORTS_I-1:0] s_rfnoc_chdr_tdata; + logic [ NUM_PORTS_I-1:0] s_rfnoc_chdr_tlast; + logic [ NUM_PORTS_I-1:0] s_rfnoc_chdr_tvalid; + logic [ NUM_PORTS_I-1:0] s_rfnoc_chdr_tready; + + // DUT Master (Output) Port Signals + logic [CHDR_W*NUM_PORTS_O-1:0] m_rfnoc_chdr_tdata; + logic [ NUM_PORTS_O-1:0] m_rfnoc_chdr_tlast; + logic [ NUM_PORTS_O-1:0] m_rfnoc_chdr_tvalid; + logic [ NUM_PORTS_O-1:0] m_rfnoc_chdr_tready; + + // Map the array of BFMs to a flat vector for the DUT connections + for (genvar i = 0; i < NUM_PORTS_I; i++) begin : gen_dut_input_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]; + end + for (genvar i = 0; i < NUM_PORTS_O; i++) begin : gen_dut_output_connections + // 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_addsub #( + .THIS_PORTID (THIS_PORTID), + .CHDR_W (CHDR_W), + .MTU (MTU), + .USE_IMPL (USE_IMPL) + ) dut ( + .rfnoc_chdr_clk (rfnoc_chdr_clk), + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .ce_clk (ce_clk), + .rfnoc_core_config (backend.cfg), + .rfnoc_core_status (backend.sts), + .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 (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 Logic + //--------------------------------------------------------------------------- + + // Rand#(WIDTH)::rand_logic() returns a WIDTH-bit random number. We avoid + // std::randomize() due to license requirements and limited tool support. + class Rand #(WIDTH = 32); + static function logic [WIDTH-1:0] rand_logic(); + logic [WIDTH-1:0] result; + int num_rand32 = (WIDTH + 31) / 32; + for (int i = 0; i < num_rand32; i++) begin + result = {result, $urandom()}; + end + return result; + endfunction : rand_logic + endclass : Rand + + typedef struct { + item_t samples[$]; + chdr_word_t mdata[$]; + packet_info_t pkt_info; + } test_packet_t; + + typedef struct packed { + bit [15:0] i; + bit [15:0] q; + } sc16_t; + + + //--------------------------------------------------------------------------- + // Test Tasks + //--------------------------------------------------------------------------- + + task automatic test_rand( + int num_packets, + int max_spp = SPP, + int prob_a = STALL_PROB, + int prob_b = STALL_PROB, + int prob_sum = STALL_PROB, + int prob_diff = STALL_PROB + ); + mailbox #(test_packet_t) packets_mb_a = new(); + mailbox #(test_packet_t) packets_mb_b = new(); + + // Set the BFM TREADY behavior + blk_ctrl.set_master_stall_prob(IN_PORT_A, prob_a); + blk_ctrl.set_master_stall_prob(IN_PORT_B, prob_b); + blk_ctrl.set_slave_stall_prob(OUT_PORT_SUM, prob_sum); + blk_ctrl.set_slave_stall_prob(OUT_PORT_DIFF, prob_diff); + + fork + repeat (num_packets) begin : send_process + test_packet_t packet_a, packet_b; + int packet_length; + + packet_length = $urandom_range(1, max_spp); + + // Generate random data and header + packet_a.samples = {}; + packet_b.samples = {}; + for (int i = 0; i < packet_length; i++) begin + packet_a.samples.push_back($urandom()); + packet_b.samples.push_back($urandom()); + end + + // Generate random metadata + packet_a.mdata = {}; + packet_b.mdata = {}; + for (int i = 0; i < $urandom_range(0,31); i++) + packet_a.mdata.push_back(Rand #(CHDR_W)::rand_logic()); + for (int i = 0; i < $urandom_range(0,31); i++) + packet_b.mdata.push_back(Rand #(CHDR_W)::rand_logic()); + + // Generate random header info + packet_a.pkt_info = Rand #($bits(packet_a.pkt_info))::rand_logic(); + packet_b.pkt_info = Rand #($bits(packet_b.pkt_info))::rand_logic(); + + // Enqueue the packets for each port + blk_ctrl.send_items(IN_PORT_A, packet_a.samples, packet_a.mdata, packet_a.pkt_info); + blk_ctrl.send_items(IN_PORT_B, packet_b.samples, packet_b.mdata, packet_b.pkt_info); + + // Enqueue what we sent for the receiver to check the output + packets_mb_a.put(packet_a); + packets_mb_b.put(packet_b); + end + repeat (num_packets) begin : recv_process + test_packet_t packet_a, packet_b, packet_add, packet_sub; + string str; + + // Grab the next pair of packets that was input + packets_mb_a.get(packet_a); + packets_mb_b.get(packet_b); + + // Receive a packet from each port + blk_ctrl.recv_items_adv(OUT_PORT_SUM, packet_add.samples, + packet_add.mdata, packet_add.pkt_info); + blk_ctrl.recv_items_adv(OUT_PORT_DIFF, packet_sub.samples, + packet_sub.mdata, packet_sub.pkt_info); + + // Make sure both output packets have the same length + `ASSERT_ERROR(packet_add.samples.size() == packet_sub.samples.size(), + "ADD and SUB packets were not the same length"); + + // Make sure the output packet length matches the A input + `ASSERT_ERROR(packet_a.samples.size() == packet_add.samples.size(), + "Output packet length didn't match A input"); + + // Check that the output packet header info matches the A input + `ASSERT_ERROR(packet_info_equal(packet_a.pkt_info, packet_add.pkt_info), + "ADD output header info didn't match A input"); + `ASSERT_ERROR(packet_info_equal(packet_a.pkt_info, packet_sub.pkt_info), + "SUB output header info didn't match A input"); + + // Check the metdata + `ASSERT_ERROR(ChdrData #(CHDR_W)::chdr_equal(packet_a.mdata, packet_add.mdata), + "ADD metadata info didn't match A input"); + `ASSERT_ERROR(ChdrData #(CHDR_W)::chdr_equal(packet_a.mdata, packet_sub.mdata), + "SUB metadata info didn't match A input"); + + // Verify that the data has the expected values + for (int i = 0; i < packet_add.samples.size(); i++) begin + sc16_t a, b, a_plus_b, a_min_b, add, sub; + + // Grab the input and output samples + a = packet_a.samples[i]; + b = packet_b.samples[i]; + add = packet_add.samples[i]; + sub = packet_sub.samples[i]; + + // Compute expected sum and difference + a_plus_b.i = a.i + b.i; + a_plus_b.q = a.q + b.q; + a_min_b.i = a.i - b.i; + a_min_b.q = a.q - b.q; + + // Check that the results match + $sformat(str, + "Incorrect value received on ADD output! Expected: 0x%X, Received: 0x%X", + a_plus_b, add); + `ASSERT_ERROR(add == a_plus_b, str); + $sformat(str, + "Incorrect value received on SUB output! Expected: 0x%X, Received: 0x%X", + a_min_b, sub); + `ASSERT_ERROR(sub == a_min_b, str); + end + end + join + endtask : test_rand + + + //--------------------------------------------------------------------------- + // Main Test Process + //--------------------------------------------------------------------------- + + initial begin : tb_main + + // Initialize the test exec object for this testbench + test.start_tb("rfnoc_block_addsub_tb"); + + // Don't start the clocks until after start_tb() returns. This ensures that + // the clocks aren't toggling while other instances of this testbench are + // running, which speeds up simulation time. + rfnoc_chdr_clk_gen.start(); + rfnoc_ctrl_clk_gen.start(); + ce_clk_gen.start(); + + // Start the BFMs running + blk_ctrl.run(); + + //-------------------------------- + // Reset + //-------------------------------- + + test.start_test("Flush block then reset it", 10us); + blk_ctrl.flush_and_reset(); + test.end_test(); + + //-------------------------------- + // Verify Block Info + //-------------------------------- + + test.start_test("Verify Block Info", 2us); + `ASSERT_ERROR(blk_ctrl.get_noc_id() == NOC_ID, "Incorrect NOC_ID Value"); + `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS_I, "Incorrect NUM_DATA_I Value"); + `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS_O, "Incorrect NUM_DATA_O Value"); + `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value"); + test.end_test(); + + //-------------------------------- + // Test Sequences + //-------------------------------- + + begin + const int NUM_PACKETS = 100; + + test.start_test("Test random packets", 1ms); + test_rand(NUM_PACKETS); + test.end_test(); + + test.start_test("Test without back pressure", 1ms); + test_rand(NUM_PACKETS, SPP, 0, 0, 0, 0); + test.end_test(); + + test.start_test("Test back pressure", 1ms); + test_rand(NUM_PACKETS, SPP, 25, 25, 50, 50); + test.end_test(); + + test.start_test("Test underflow", 1ms); + test_rand(NUM_PACKETS, SPP, 50, 50, 25, 25); + test.end_test(); + + test.start_test("Test min packet size", 1ms); + test_rand(10, 1); + test.end_test(); + end + + //-------------------------------- + // Finish Up + //-------------------------------- + + // 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(); + ce_clk_gen.kill(); + + end : tb_main + +endmodule : rfnoc_block_addsub_tb + + +`default_nettype wire |