aboutsummaryrefslogtreecommitdiffstats
path: root/host/examples/rfnoc-example/fpga
diff options
context:
space:
mode:
Diffstat (limited to 'host/examples/rfnoc-example/fpga')
-rw-r--r--host/examples/rfnoc-example/fpga/CMakeLists.txt22
-rw-r--r--host/examples/rfnoc-example/fpga/Makefile.srcs21
-rw-r--r--host/examples/rfnoc-example/fpga/rfnoc_block_gain/CMakeLists.txt16
-rw-r--r--host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile47
-rw-r--r--host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile.srcs22
-rw-r--r--host/examples/rfnoc-example/fpga/rfnoc_block_gain/noc_shell_gain.v285
-rw-r--r--host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain.v315
-rw-r--r--host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain_tb.sv290
8 files changed, 1018 insertions, 0 deletions
diff --git a/host/examples/rfnoc-example/fpga/CMakeLists.txt b/host/examples/rfnoc-example/fpga/CMakeLists.txt
new file mode 100644
index 000000000..41d5a1a9a
--- /dev/null
+++ b/host/examples/rfnoc-example/fpga/CMakeLists.txt
@@ -0,0 +1,22 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+# List Makefile.srcs here (which needs to point to the individual blocks!) as
+# well as any non-block specific HDL files that should get installed alongside
+# the rest of the FPGA/Verilog/VHDL/HDL files. Only list files that are required
+# for synthesis, testbench-specific files do not get installed and thus do not
+# have to be listed (it won't hurt, it will just clutter your share/ directory).
+# Don't list the files in the block subdirectories, though, they will get added
+# below.
+install(FILES
+ Makefile.srcs
+ DESTINATION ${PROJECT_DATA_DIR}/fpga
+ COMPONENT fpga
+)
+
+# Now call add_subdirectory() for every block subdir
+add_subdirectory(rfnoc_block_gain)
+
diff --git a/host/examples/rfnoc-example/fpga/Makefile.srcs b/host/examples/rfnoc-example/fpga/Makefile.srcs
new file mode 100644
index 000000000..f95dab6ea
--- /dev/null
+++ b/host/examples/rfnoc-example/fpga/Makefile.srcs
@@ -0,0 +1,21 @@
+#
+# Copyright 2019 Ettus Research, A National Instruments Brand
+#
+# SPDX-License-Identifier: LGPL-3.0-or-later
+#
+
+# We first need to figure out our own path, in case this file is being included
+# from somewhere else (e.g., from a fpgadev/top/$device directory)
+RFNOC_EXAMPLE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+
+# One include statement for every RFNoC block with its own subdirectory, which
+# itself will contain a Makefile.srcs
+include $(RFNOC_EXAMPLE_DIR)/rfnoc_block_gain/Makefile.srcs
+
+# If there are additional modules or IP (other than what is in the RFNoC block
+# subdirectories) that needs to get installed in order to synthesize blocks from
+# this module, list them here:
+#RFNOC_OOT_SRCS += $(abspath $(addprefix ${RFNOC_EXAMPLE_DIR},
+#my_other_module.v \
+#ip/my_ip_core/my_ip_core.xci \
+#))
diff --git a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/CMakeLists.txt b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/CMakeLists.txt
new file mode 100644
index 000000000..7a497b837
--- /dev/null
+++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/CMakeLists.txt
@@ -0,0 +1,16 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+
+# This macro will tell CMake that this directory contains an RFNoC block. It
+# will parse Makefile.srcs to see which files need to be installed, and it will
+# register a testbench target for this directory.
+RFNOC_REGISTER_BLOCK_DIR()
+
+# This will do the same, but it will skip the testbench target.
+#RFNOC_REGISTER_BLOCK_DIR(NOTESTBENCH)
+
+
diff --git a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile
new file mode 100644
index 000000000..1ff3046ee
--- /dev/null
+++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile
@@ -0,0 +1,47 @@
+#
+# Copyright 2019 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.
+ifndef UHD_FPGA_DIR
+$(error "UHD_FPGA_DIR is not set! Must point to UHD FPGA repository!")
+endif
+BASE_DIR = $(UHD_FPGA_DIR)/usrp3/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 Makefile.srcs
+
+DESIGN_SRCS += $(abspath \
+$(RFNOC_CORE_SRCS) \
+$(RFNOC_UTIL_SRCS) \
+$(RFNOC_OOT_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+SIM_TOP = rfnoc_block_gain_tb
+SIM_SRCS = \
+$(abspath rfnoc_block_gain_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/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile.srcs b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile.srcs
new file mode 100644
index 000000000..8b551658b
--- /dev/null
+++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/Makefile.srcs
@@ -0,0 +1,22 @@
+#
+# Copyright 2019 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_gain.v \
+noc_shell_gain.v \
+)
diff --git a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/noc_shell_gain.v b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/noc_shell_gain.v
new file mode 100644
index 000000000..043ab5b97
--- /dev/null
+++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/noc_shell_gain.v
@@ -0,0 +1,285 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: noc_shell_gain
+//
+// Description:
+//
+// This is a tool-generated NoC-shell for the gain 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_gain #(
+ parameter [9:0] THIS_PORTID = 10'd0,
+ parameter CHDR_W = 64,
+ parameter [5:0] MTU = 10
+) (
+ //---------------------
+ // Framework Interface
+ //---------------------
+
+ // RFNoC Framework Clocks
+ input wire rfnoc_chdr_clk,
+ input wire rfnoc_ctrl_clk,
+
+ // NoC Shell Generated Resets
+ output wire rfnoc_chdr_rst,
+ output wire rfnoc_ctrl_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 [(1)*CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire [(1)-1:0] s_rfnoc_chdr_tlast,
+ input wire [(1)-1:0] s_rfnoc_chdr_tvalid,
+ output wire [(1)-1:0] s_rfnoc_chdr_tready,
+ // AXIS-CHDR Output Ports (to framework)
+ output wire [(1)*CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire [(1)-1:0] m_rfnoc_chdr_tlast,
+ output wire [(1)-1:0] m_rfnoc_chdr_tvalid,
+ input wire [(1)-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
+ //---------------------
+
+ // CtrlPort Clock and Reset
+ output wire ctrlport_clk,
+ output wire ctrlport_rst,
+ // CtrlPort Master
+ output wire m_ctrlport_req_wr,
+ output wire m_ctrlport_req_rd,
+ output wire [19:0] m_ctrlport_req_addr,
+ output wire [31:0] m_ctrlport_req_data,
+ input wire m_ctrlport_resp_ack,
+ input wire [31:0] m_ctrlport_resp_data,
+
+ // AXI-Stream Payload Context Clock and Reset
+ output wire axis_data_clk,
+ output wire axis_data_rst,
+ // Payload Stream to User Logic: in
+ output wire [32*1-1:0] m_in_payload_tdata,
+ output wire [1-1:0] m_in_payload_tkeep,
+ output wire m_in_payload_tlast,
+ output wire m_in_payload_tvalid,
+ input wire m_in_payload_tready,
+ // Context Stream to User Logic: in
+ output wire [CHDR_W-1:0] m_in_context_tdata,
+ output wire [3:0] m_in_context_tuser,
+ output wire m_in_context_tlast,
+ output wire m_in_context_tvalid,
+ input wire m_in_context_tready,
+ // Payload Stream from User Logic: out
+ input wire [32*1-1:0] s_out_payload_tdata,
+ input wire [0:0] s_out_payload_tkeep,
+ input wire s_out_payload_tlast,
+ input wire s_out_payload_tvalid,
+ output wire s_out_payload_tready,
+ // Context Stream from User Logic: out
+ input wire [CHDR_W-1:0] s_out_context_tdata,
+ input wire [3:0] s_out_context_tuser,
+ input wire s_out_context_tlast,
+ input wire s_out_context_tvalid,
+ output wire s_out_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'h00000B16),
+ .NUM_DATA_I (1),
+ .NUM_DATA_O (1),
+ .CTRL_FIFOSIZE ($clog2(32)),
+ .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)
+ );
+
+ //---------------------------------------------------------------------------
+ // Control Path
+ //---------------------------------------------------------------------------
+
+ assign ctrlport_clk = rfnoc_chdr_clk;
+ assign ctrlport_rst = rfnoc_chdr_rst;
+
+ ctrlport_endpoint #(
+ .THIS_PORTID (THIS_PORTID),
+ .SYNC_CLKS (0),
+ .AXIS_CTRL_MST_EN (0),
+ .AXIS_CTRL_SLV_EN (1),
+ .SLAVE_FIFO_SIZE ($clog2(32))
+ ) ctrlport_endpoint_i (
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ .rfnoc_ctrl_rst (rfnoc_ctrl_rst),
+ .ctrlport_clk (ctrlport_clk),
+ .ctrlport_rst (ctrlport_rst),
+ .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata),
+ .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast),
+ .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid),
+ .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready),
+ .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata),
+ .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast),
+ .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid),
+ .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready),
+ .m_ctrlport_req_wr (m_ctrlport_req_wr),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr),
+ .m_ctrlport_req_data (m_ctrlport_req_data),
+ .m_ctrlport_req_byte_en (),
+ .m_ctrlport_req_has_time (),
+ .m_ctrlport_req_time (),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack),
+ .m_ctrlport_resp_status (2'b0),
+ .m_ctrlport_resp_data (m_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'hF),
+ .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 ()
+ );
+
+ //---------------------------------------------------------------------------
+ // Data Path
+ //---------------------------------------------------------------------------
+
+ genvar i;
+
+ assign axis_data_clk = rfnoc_chdr_clk;
+ assign axis_data_rst = rfnoc_chdr_rst;
+
+ //---------------------
+ // Input Data Paths
+ //---------------------
+
+ chdr_to_axis_pyld_ctxt #(
+ .CHDR_W (CHDR_W),
+ .ITEM_W (32),
+ .NIPC (1),
+ .SYNC_CLKS (1),
+ .CONTEXT_FIFO_SIZE ($clog2(2)),
+ .PAYLOAD_FIFO_SIZE ($clog2(2)),
+ .CONTEXT_PREFETCH_EN (1)
+ ) chdr_to_axis_pyld_ctxt_in_in (
+ .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_payload_tdata),
+ .m_axis_payload_tkeep (m_in_payload_tkeep),
+ .m_axis_payload_tlast (m_in_payload_tlast),
+ .m_axis_payload_tvalid (m_in_payload_tvalid),
+ .m_axis_payload_tready (m_in_payload_tready),
+ .m_axis_context_tdata (m_in_context_tdata),
+ .m_axis_context_tuser (m_in_context_tuser),
+ .m_axis_context_tlast (m_in_context_tlast),
+ .m_axis_context_tvalid (m_in_context_tvalid),
+ .m_axis_context_tready (m_in_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])
+ );
+
+ //---------------------
+ // Output Data Paths
+ //---------------------
+
+ axis_pyld_ctxt_to_chdr #(
+ .CHDR_W (CHDR_W),
+ .ITEM_W (32),
+ .NIPC (1),
+ .SYNC_CLKS (1),
+ .CONTEXT_FIFO_SIZE ($clog2(2)),
+ .PAYLOAD_FIFO_SIZE ($clog2(2)),
+ .MTU (MTU),
+ .CONTEXT_PREFETCH_EN (1)
+ ) axis_pyld_ctxt_to_chdr_out_out (
+ .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_out_payload_tdata),
+ .s_axis_payload_tkeep (s_out_payload_tkeep),
+ .s_axis_payload_tlast (s_out_payload_tlast),
+ .s_axis_payload_tvalid (s_out_payload_tvalid),
+ .s_axis_payload_tready (s_out_payload_tready),
+ .s_axis_context_tdata (s_out_context_tdata),
+ .s_axis_context_tuser (s_out_context_tuser),
+ .s_axis_context_tlast (s_out_context_tlast),
+ .s_axis_context_tvalid (s_out_context_tvalid),
+ .s_axis_context_tready (s_out_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])
+ );
+
+endmodule // noc_shell_gain
+
+
+`default_nettype wire
diff --git a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain.v b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain.v
new file mode 100644
index 000000000..929439a52
--- /dev/null
+++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain.v
@@ -0,0 +1,315 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_gain
+//
+// Description:
+//
+// This is an example RFNoC block. It applies a numeric gain to incoming
+// samples then outputs the result. A single register is used to control the
+// gain setting.
+//
+// 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).
+//
+
+`default_nettype none
+
+
+module rfnoc_block_gain #(
+ parameter [9:0] THIS_PORTID = 10'd0,
+ parameter CHDR_W = 64,
+ parameter [5:0] MTU = 10
+)(
+ // RFNoC Framework Clocks and Resets
+ input wire rfnoc_chdr_clk,
+ input wire rfnoc_ctrl_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 [(1)*CHDR_W-1:0] s_rfnoc_chdr_tdata,
+ input wire [(1)-1:0] s_rfnoc_chdr_tlast,
+ input wire [(1)-1:0] s_rfnoc_chdr_tvalid,
+ output wire [(1)-1:0] s_rfnoc_chdr_tready,
+ // AXIS-CHDR Output Ports (to framework)
+ output wire [(1)*CHDR_W-1:0] m_rfnoc_chdr_tdata,
+ output wire [(1)-1:0] m_rfnoc_chdr_tlast,
+ output wire [(1)-1:0] m_rfnoc_chdr_tvalid,
+ input wire [(1)-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
+);
+
+ //---------------------------------------------------------------------------
+ // Signal Declarations
+ //---------------------------------------------------------------------------
+
+ // Clocks and Resets
+ wire ctrlport_clk;
+ wire ctrlport_rst;
+ wire axis_data_clk;
+ wire axis_data_rst;
+ // CtrlPort Master
+ wire m_ctrlport_req_wr;
+ wire m_ctrlport_req_rd;
+ wire [19:0] m_ctrlport_req_addr;
+ wire [31:0] m_ctrlport_req_data;
+ reg m_ctrlport_resp_ack;
+ reg [31:0] m_ctrlport_resp_data;
+ // Payload Stream to User Logic: in
+ wire [32*1-1:0] m_in_payload_tdata;
+ wire [1-1:0] m_in_payload_tkeep;
+ wire m_in_payload_tlast;
+ wire m_in_payload_tvalid;
+ wire m_in_payload_tready;
+ // Context Stream to User Logic: in
+ wire [CHDR_W-1:0] m_in_context_tdata;
+ wire [3:0] m_in_context_tuser;
+ wire m_in_context_tlast;
+ wire m_in_context_tvalid;
+ wire m_in_context_tready;
+ // Payload Stream from User Logic: out
+ wire [32*1-1:0] s_out_payload_tdata;
+ wire [0:0] s_out_payload_tkeep;
+ wire s_out_payload_tlast;
+ wire s_out_payload_tvalid;
+ wire s_out_payload_tready;
+ // Context Stream from User Logic: out
+ wire [CHDR_W-1:0] s_out_context_tdata;
+ wire [3:0] s_out_context_tuser;
+ wire s_out_context_tlast;
+ wire s_out_context_tvalid;
+ wire s_out_context_tready;
+
+ //---------------------------------------------------------------------------
+ // NoC Shell
+ //---------------------------------------------------------------------------
+
+ noc_shell_gain #(
+ .CHDR_W (CHDR_W),
+ .THIS_PORTID (THIS_PORTID),
+ .MTU (MTU)
+ ) noc_shell_gain_i (
+ //---------------------
+ // Framework Interface
+ //---------------------
+
+ // Clock Inputs
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_clk),
+ // Reset Outputs
+ .rfnoc_chdr_rst (),
+ .rfnoc_ctrl_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
+ //---------------------
+
+ // CtrlPort Clock and Reset
+ .ctrlport_clk (ctrlport_clk),
+ .ctrlport_rst (ctrlport_rst),
+ // CtrlPort Master
+ .m_ctrlport_req_wr (m_ctrlport_req_wr),
+ .m_ctrlport_req_rd (m_ctrlport_req_rd),
+ .m_ctrlport_req_addr (m_ctrlport_req_addr),
+ .m_ctrlport_req_data (m_ctrlport_req_data),
+ .m_ctrlport_resp_ack (m_ctrlport_resp_ack),
+ .m_ctrlport_resp_data (m_ctrlport_resp_data),
+
+ // 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
+ .m_in_payload_tdata (m_in_payload_tdata),
+ .m_in_payload_tkeep (m_in_payload_tkeep),
+ .m_in_payload_tlast (m_in_payload_tlast),
+ .m_in_payload_tvalid (m_in_payload_tvalid),
+ .m_in_payload_tready (m_in_payload_tready),
+ // Context Stream to User Logic: in
+ .m_in_context_tdata (m_in_context_tdata),
+ .m_in_context_tuser (m_in_context_tuser),
+ .m_in_context_tlast (m_in_context_tlast),
+ .m_in_context_tvalid (m_in_context_tvalid),
+ .m_in_context_tready (m_in_context_tready),
+ // Payload Stream from User Logic: out
+ .s_out_payload_tdata (s_out_payload_tdata),
+ .s_out_payload_tkeep (s_out_payload_tkeep),
+ .s_out_payload_tlast (s_out_payload_tlast),
+ .s_out_payload_tvalid (s_out_payload_tvalid),
+ .s_out_payload_tready (s_out_payload_tready),
+ // Context Stream from User Logic: out
+ .s_out_context_tdata (s_out_context_tdata),
+ .s_out_context_tuser (s_out_context_tuser),
+ .s_out_context_tlast (s_out_context_tlast),
+ .s_out_context_tvalid (s_out_context_tvalid),
+ .s_out_context_tready (s_out_context_tready)
+ );
+
+ //---------------------------------------------------------------------------
+ // User Logic
+ //---------------------------------------------------------------------------
+ //
+ // The code above this point is essentially unmodified from what was
+ // generated by the tool. The code below implements the gain example.
+ //
+ // All registers are in the ctrlport_clk domain and the signal processing is
+ // in the axis_data_clk domain. However, we specified in the block YAML
+ // configuration file that we want both the control and data interfaces on
+ // the rfnoc_chdr clock. So we don't need to worry about crossing the
+ // register data from ctrlport_clk and axis_data_clk.
+ //
+ //---------------------------------------------------------------------------
+
+ //---------------------------------------------------------------------------
+ // Registers
+ //---------------------------------------------------------------------------
+ //
+ // There's only one register now, but we'll structure the register code to
+ // make it easier to add more registers later.
+ //
+ //---------------------------------------------------------------------------
+
+ localparam REG_GAIN_ADDR = 0; // Address for gain value
+ localparam REG_GAIN_DEFAULT = 1; // Default gain value
+
+ reg [15:0] reg_gain = REG_GAIN_DEFAULT;
+
+ always @(posedge ctrlport_clk) begin
+ if (ctrlport_rst) begin
+ reg_gain = REG_GAIN_DEFAULT;
+ end else begin
+ // Default assignment
+ m_ctrlport_resp_ack <= 0;
+
+ // Handle read requests
+ if (m_ctrlport_req_rd) begin
+ case (m_ctrlport_req_addr)
+ REG_GAIN_ADDR: begin
+ m_ctrlport_resp_ack <= 1;
+ m_ctrlport_resp_data <= { 16'b0, reg_gain };
+ end
+ endcase
+ end
+
+ // Handle write requests
+ if (m_ctrlport_req_wr) begin
+ case (m_ctrlport_req_addr)
+ REG_GAIN_ADDR: begin
+ m_ctrlport_resp_ack <= 1;
+ reg_gain <= m_ctrlport_req_data[15:0];
+ end
+ endcase
+ end
+ end
+ end
+
+ //---------------------------------------------------------------------------
+ // Signal Processing
+ //---------------------------------------------------------------------------
+
+ wire [63:0] mult_tdata; // Multiply results in 32-bit I and 32-bit Q (sc32)
+ wire mult_tlast;
+ wire mult_tvalid;
+ wire mult_tready;
+
+ // Multiply complex sample by a real-valued gain. Only input the gain
+ // (real_tvalid) when we have payload data to go in (cplx_tdata). That way
+ // the current gain value always applies to the current sample. This assumes
+ // that real_tready and cplx_tready have identical behavior.
+ //
+ // Note that we receive the data with I on bits [31:16] and Q on bits [15:0],
+ // but this does not matter to our multiplier.
+ //
+ mult_rc #(
+ .WIDTH_REAL (16),
+ .WIDTH_CPLX (16),
+ .WIDTH_P (32),
+ .DROP_TOP_P (5), // Must be 5 for a normal multiply in DSP48E1
+ .LATENCY (4) // Turn on all pipeline registers in the DSP48E1
+ ) mult_rc_i (
+ .clk (axis_data_clk),
+ .reset (axis_data_rst),
+ .real_tdata (reg_gain),
+ .real_tlast (m_in_payload_tlast),
+ .real_tvalid (m_in_payload_tvalid),
+ .real_tready (),
+ .cplx_tdata (m_in_payload_tdata),
+ .cplx_tlast (m_in_payload_tlast),
+ .cplx_tvalid (m_in_payload_tvalid),
+ .cplx_tready (m_in_payload_tready),
+ .p_tdata (mult_tdata),
+ .p_tlast (mult_tlast),
+ .p_tvalid (mult_tvalid),
+ .p_tready (mult_tready)
+ );
+
+ // Clip the results
+ axi_clip_complex #(
+ .WIDTH_IN (32),
+ .WIDTH_OUT (16)
+ ) axi_clip_complex_i (
+ .clk (axis_data_clk),
+ .reset (axis_data_rst),
+ .i_tdata (mult_tdata),
+ .i_tlast (mult_tlast),
+ .i_tvalid (mult_tvalid),
+ .i_tready (mult_tready),
+ .o_tdata (s_out_payload_tdata),
+ .o_tlast (s_out_payload_tlast),
+ .o_tvalid (s_out_payload_tvalid),
+ .o_tready (s_out_payload_tready)
+ );
+
+ // Only 1-sample per clock, so tkeep should always be asserted
+ assign s_out_payload_tkeep = 1'b1;
+
+ // We're not doing anything fancy with the context (the CHDR header info) so
+ // we can simply pass the input context through unchanged.
+ assign s_out_context_tdata = m_in_context_tdata;
+ assign s_out_context_tuser = m_in_context_tuser;
+ assign s_out_context_tlast = m_in_context_tlast;
+ assign s_out_context_tvalid = m_in_context_tvalid;
+ assign m_in_context_tready = s_out_context_tready;
+
+endmodule // rfnoc_block_gain
+
+
+`default_nettype wire
diff --git a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain_tb.sv b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain_tb.sv
new file mode 100644
index 000000000..1f76563a8
--- /dev/null
+++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain_tb.sv
@@ -0,0 +1,290 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: rfnoc_block_gain_tb
+//
+// Description: Testbench for the gain RFNoC block.
+//
+
+`default_nettype none
+
+
+module rfnoc_block_gain_tb;
+
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgRfnocBlockCtrlBfm::*;
+ import PkgRfnocItemUtils::*;
+
+ //---------------------------------------------------------------------------
+ // Testbench Configuration
+ //---------------------------------------------------------------------------
+
+ localparam [ 9:0] THIS_PORTID = 10'h123;
+ localparam [31:0] NOC_ID = 32'h00000B16;
+ localparam int CHDR_W = 64;
+ localparam int NUM_PORTS_I = 1;
+ localparam int NUM_PORTS_O = 1;
+ localparam int MTU = 13;
+ localparam int SPP = 64;
+ localparam int PKT_SIZE_BYTES = SPP * 4; // Assumes 4 bytes per sample
+ localparam int STALL_PROB = 25; // Default BFM stall probability
+ localparam real CHDR_CLK_PER = 5.0; // 200 MHz
+ localparam real CTRL_CLK_PER = 25.0; // 40 MHz
+
+ //---------------------------------------------------------------------------
+ // Clocks and Resets
+ //---------------------------------------------------------------------------
+
+ 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 #(CTRL_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_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(CHDR_W)) blk_ctrl = new(backend, m_ctrl, s_ctrl);
+
+ // 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_gain #(
+ .THIS_PORTID (THIS_PORTID),
+ .CHDR_W (CHDR_W),
+ .MTU (MTU)
+ ) dut (
+ .rfnoc_chdr_clk (rfnoc_chdr_clk),
+ .rfnoc_ctrl_clk (rfnoc_ctrl_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)
+ );
+
+ //---------------------------------------------------------------------------
+ // Main Test Process
+ //---------------------------------------------------------------------------
+
+ // Multiply two signed 16-bit numbers and clip the result
+ function shortint mult_and_clip(shortint a, shortint b);
+ int product;
+ shortint result;
+ product = int'(a) * int'(b);
+ result = product[15:0];
+ if (product > 16'sh7FFF) result = 16'sh7FFF;
+ if (product < 16'sh8000) result = 16'sh8000;
+ return result;
+ endfunction : mult_and_clip
+
+ // Generate a random signed 16-bit integer in the range [a, b]
+ function shortint rand_shortint(int a, int b);
+ return signed'($urandom_range(b - a)) + a;
+ endfunction : rand_shortint
+
+ localparam int REG_GAIN_ADDR = dut.REG_GAIN_ADDR;
+
+ initial begin : tb_main
+
+ // Initialize the test exec object for this testbench
+ test.start_tb("rfnoc_block_gain_tb");
+
+ // 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
+ // Read and write the gain register to make sure it updates correctly.
+ logic [31:0] val32;
+ test.start_test("Verify gain register", 5us);
+
+ blk_ctrl.reg_read(REG_GAIN_ADDR, val32);
+ `ASSERT_ERROR(
+ val32 == 1, "Initial value for REG_GAIN_ADDR is not 1");
+
+ // Write a value wider than the register to verify the width
+ blk_ctrl.reg_write(REG_GAIN_ADDR, 32'h12348765);
+ blk_ctrl.reg_read(REG_GAIN_ADDR, val32);
+ `ASSERT_ERROR(
+ val32 == 32'h8765, "Initial value for REG_GAIN_ADDR is not correct");
+
+ test.end_test();
+ end
+
+ begin
+ // Iterate through a series of gain values to test.
+ localparam shortint MAX_TEST_VAL = 255;
+ localparam shortint MIN_TEST_VAL = -255;
+ static logic [15:0] test_gains[$] = {
+ 1, // Make sure unity gain leaves data unmodified
+ -1, // Make sure -1 negates data
+ 0, // Make sure 0 gain results in 0
+ 37, // Make sure a normal gain computes correctly
+ -22, // Make sure a normal gain computes correctly
+ 256 // Make sure a large gain causes clipping
+ };
+
+ foreach (test_gains[gain_index]) begin
+ shortint gain;
+ int num_bytes;
+ chdr_word_t send_payload[$]; // CHDR payload words (64-bit)
+ chdr_word_t recv_payload[$]; // CHDR payload words (64-bit)
+
+ gain = test_gains[gain_index];
+
+ test.start_test($sformatf("Test gain of %0d", int'(gain)), 10us);
+
+ blk_ctrl.reg_write(REG_GAIN_ADDR, gain);
+
+ // Generate a payload of random samples in the range [-255, 255], two
+ // samples per CHDR word.
+ send_payload = {};
+ for (int i = 0; i < SPP/2; i++) begin
+ send_payload.push_back({
+ rand_shortint(MIN_TEST_VAL, MAX_TEST_VAL), // 2nd sample I
+ rand_shortint(MIN_TEST_VAL, MAX_TEST_VAL), // 2nd sample Q
+ rand_shortint(MIN_TEST_VAL, MAX_TEST_VAL), // 1st sample I
+ rand_shortint(MIN_TEST_VAL, MAX_TEST_VAL) // 1st sample Q
+ });
+ end
+
+ // Queue a packet for transfer
+ blk_ctrl.send(0, send_payload);
+
+ // Receive the output packet
+ blk_ctrl.recv(0, recv_payload, num_bytes);
+
+ // Check the resulting payload size
+ `ASSERT_ERROR(num_bytes == SPP*4,
+ "Received payload didn't match size of payload sent");
+
+ // Check the resulting payload data
+ for (int i = 0; i < SPP/2; i++) begin
+ chdr_word_t expected;
+ chdr_word_t received;
+
+ expected[63:48] = mult_and_clip(gain, send_payload[i][63:48]);
+ expected[47:32] = mult_and_clip(gain, send_payload[i][47:32]);
+ expected[31:16] = mult_and_clip(gain, send_payload[i][31:16]);
+ expected[15: 0] = mult_and_clip(gain, send_payload[i][15: 0]);
+ received = recv_payload[i];
+
+ `ASSERT_ERROR(
+ expected == received,
+ $sformatf("For word %0d, gain %0d, input 0x%X, received 0x%X, expected 0x%X",
+ i, gain, send_payload[i], recv_payload[i], expected));
+ end
+
+ test.end_test();
+ end
+ end
+
+ //--------------------------------
+ // Finish Up
+ //--------------------------------
+
+ // Display final statistics and results
+ test.end_tb();
+ end : tb_main
+
+endmodule : rfnoc_block_gain_tb
+
+
+`default_nettype wire