aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSugandha Gupta <sugandha.gupta@ettus.com>2019-10-15 11:52:46 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 12:21:32 -0800
commita801d6b046743140e9a50c7788dd17dd71f5540a (patch)
tree58d164e1b4cb2a8d871ca532287699f3912ae3d8
parent2a7e69d862f661075b98bab19e58d958c28a9af8 (diff)
downloaduhd-a801d6b046743140e9a50c7788dd17dd71f5540a.tar.gz
uhd-a801d6b046743140e9a50c7788dd17dd71f5540a.tar.bz2
uhd-a801d6b046743140e9a50c7788dd17dd71f5540a.zip
examples: Add example out-of-tree module for RFNoC modules
This subdirectory is its own, self-contained project. It is supposed to work against the UHD version it is shipped with. Co-Authored-By: Martin Braun <martin.braun@ettus.com> Co-Authored-By: Wade Fife <wade.fife@ni.com>
-rw-r--r--host/examples/rfnoc-example/.gitignore4
-rw-r--r--host/examples/rfnoc-example/CMakeLists.txt181
-rw-r--r--host/examples/rfnoc-example/README.md28
-rw-r--r--host/examples/rfnoc-example/apps/CMakeLists.txt40
-rw-r--r--host/examples/rfnoc-example/apps/init_gain_block.cpp77
-rw-r--r--host/examples/rfnoc-example/blocks/CMakeLists.txt17
-rw-r--r--host/examples/rfnoc-example/blocks/gain.yml51
-rwxr-xr-xhost/examples/rfnoc-example/cmake/Modules/run_testbench.sh.in69
-rw-r--r--host/examples/rfnoc-example/cmake/cmake_uninstall.cmake.in32
-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
-rw-r--r--host/examples/rfnoc-example/icores/.gitignore2
-rw-r--r--host/examples/rfnoc-example/icores/CMakeLists.txt7
-rw-r--r--host/examples/rfnoc-example/icores/x310_rfnoc_image_core.yml109
-rw-r--r--host/examples/rfnoc-example/include/rfnoc/example/CMakeLists.txt13
-rw-r--r--host/examples/rfnoc-example/include/rfnoc/example/gain_block_control.hpp39
-rw-r--r--host/examples/rfnoc-example/lib/CMakeLists.txt83
-rw-r--r--host/examples/rfnoc-example/lib/gain_block_control.cpp41
24 files changed, 1811 insertions, 0 deletions
diff --git a/host/examples/rfnoc-example/.gitignore b/host/examples/rfnoc-example/.gitignore
new file mode 100644
index 000000000..0b19f9025
--- /dev/null
+++ b/host/examples/rfnoc-example/.gitignore
@@ -0,0 +1,4 @@
+build/
+xsim*log
+xsim_proj
+.Xil/
diff --git a/host/examples/rfnoc-example/CMakeLists.txt b/host/examples/rfnoc-example/CMakeLists.txt
new file mode 100644
index 000000000..27f96bfb2
--- /dev/null
+++ b/host/examples/rfnoc-example/CMakeLists.txt
@@ -0,0 +1,181 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+cmake_minimum_required(VERSION 3.8)
+project(rfnoc-example CXX C)
+
+#make sure our local CMake Modules path comes first
+#list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules)
+
+#install to PyBOMBS target prefix if defined
+#if(DEFINED ENV{PYBOMBS_PREFIX})
+# set(CMAKE_INSTALL_PREFIX $ENV{PYBOMBS_PREFIX})
+# message(STATUS "PyBOMBS installed GNU Radio. Setting CMAKE_INSTALL_PREFIX to $ENV{PYBOMBS_PREFIX}")
+#endif()
+
+########################################################################
+# Setup install directories
+########################################################################
+set(RFNOC_DATA_DIR share CACHE PATH "Base location for data")
+set(RFNOC_PKG_DATA_DIR ${RFNOC_DATA_DIR}/rfnoc/ CACHE PATH "Path to install RFNoC package data")
+set(PROJECT_DATA_DIR ${RFNOC_PKG_DATA_DIR}/example/ CACHE PATH "Path for this project's package data")
+
+if(NOT DEFINED LIB_SUFFIX AND REDHAT AND CMAKE_SYSTEM_PROCESSOR MATCHES "64$")
+ set(LIB_SUFFIX 64)
+endif()
+if(CMAKE_INSTALL_LIBDIR MATCHES lib64)
+ set(LIB_SUFFIX 64)
+endif()
+
+########################################################################
+# Find bash (for executing make and sourcing the Vivado env)
+########################################################################
+find_package(UnixCommands)
+if(BASH)
+ message(STATUS "Found bash interpreter: ${BASH}")
+ configure_file(
+ ${CMAKE_SOURCE_DIR}/cmake/Modules/run_testbench.sh.in
+ ${CMAKE_BINARY_DIR}/cmake/Modules/run_testbench.sh
+ @ONLY
+ )
+else()
+ message(WARNING
+ "Bash interpreter not found: Cannot generate FPGA targets.")
+endif()
+
+###########################################################################
+# Find UHD
+###########################################################################
+find_package(UHD)
+if(UHD_FOUND)
+ message(STATUS "Found UHD:")
+ include_directories(${UHD_INCLUDE_DIRS})
+ message(STATUS " * INCLUDES = ${UHD_INCLUDE_DIRS}")
+ link_directories(${UHD_LIBRARIES})
+ message(STATUS " * LIBS = ${UHD_LIBRARIES}")
+ find_program(_rfnoc_image_builder_exe
+ "rfnoc_image_builder"
+ )
+ if (_rfnoc_image_builder_exe)
+ message(STATUS
+ " * rfnoc_image_builder = ${_rfnoc_image_builder_exe}")
+ endif()
+else()
+ message(WARNING "UHD not found. Cannot build block controllers.")
+endif()
+
+###########################################################################
+# Find FPGA
+###########################################################################
+set(UHD_FPGA_DIR "" CACHE PATH "Path to FPGA source directory")
+message(STATUS "Checking FPGA source directory...")
+if(NOT UHD_FPGA_DIR)
+ message(WARNING
+ "Could not find FPGA directory. Skipping all FPGA targets."
+ "Please provide it using -DUHD_FPGA_DIR!")
+endif(NOT UHD_FPGA_DIR)
+if(UHD_FPGA_DIR AND NOT EXISTS ${UHD_FPGA_DIR}/usrp3/top/Makefile.common)
+ message(
+ FATAL_ERROR
+ "Invalid FPGA source directory: ${UHD_FPGA_DIR}. "
+ "Please provide it using -DUHD_FPGA_DIR!")
+endif()
+message(STATUS "Using FPGA source directory: ${UHD_FPGA_DIR}")
+
+set(UHD_FPGA_DEFAULT_DEVICE "x310"
+ CACHE STRING "Default device for testbench execution")
+
+########################################################################
+# Testbench targets and FPGA helpers
+########################################################################
+add_custom_target(testbenches)
+macro(RFNOC_ADD_TB_DIR)
+ if(BASH AND UHD_FPGA_DIR)
+ get_filename_component(_tb_dir "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
+ set(_target_name "${_tb_dir}_tb")
+ message(STATUS "Adding testbench target: ${_target_name}")
+ add_custom_target(${_target_name}
+ COMMAND ${CMAKE_BINARY_DIR}/cmake/Modules/run_testbench.sh ${UHD_FPGA_DIR} ${UHD_FPGA_DEFAULT_DEVICE} ${CMAKE_CURRENT_SOURCE_DIR} xsim
+ )
+ add_dependencies(testbenches ${_target_name})
+ endif()
+endmacro()
+
+# Helper macro to register an RFNoC block directory.
+# Such a directory must always have a Makefiles.srcs containing all the
+# required HDL files for synthesis, and optionally a Makefile file for running
+# the testbench.
+# The NOTESTBENCH argument can be used to skip the testbench target generation.
+macro(RFNOC_REGISTER_BLOCK_DIR)
+ cmake_parse_arguments(_rfnoc_block "NOTESTBENCH" "" "" ${ARGN})
+ get_filename_component(_blk_name "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
+ message(STATUS "Registering RFNoC block: ${_blk_name}")
+ file(READ ${CMAKE_CURRENT_SOURCE_DIR}/Makefile.srcs _makefile_srcs)
+ list(APPEND _block_src_files "Makefile.srcs")
+ string(REGEX MATCHALL "[a-z_]+\\.v" _src_files ${_makefile_srcs})
+ foreach(_src_file ${_src_files})
+ string(STRIP "${_src_file}" _src_file})
+ list(APPEND _block_src_files "${_src_file}")
+ endforeach()
+ install(FILES ${_block_src_files}
+ DESTINATION ${PROJECT_DATA_DIR}/fpga/${_blk_name}
+ COMPONENT fpga)
+ if(NOT ${_rfnoc_block_NOTESTBENCH})
+ RFNOC_ADD_TB_DIR()
+ endif()
+endmacro()
+
+macro(RFNOC_REGISTER_IMAGE_CORE)
+ cmake_parse_arguments(_rfnoc_image_core "" "SRC" "" ${ARGN})
+ get_filename_component(_target_name ${_rfnoc_image_core_SRC} NAME_WE)
+ if(NOT _target_name MATCHES "image_core")
+ message(FATAL_ERROR
+ "Invalid image core source file name: ${_rfnoc_image_core_SRC} (must end in `image_core`)")
+ endif()
+ if (_rfnoc_image_builder_exe)
+ message(STATUS "Adding image core target: ${_target_name}")
+ add_custom_target(${_target_name}
+ COMMAND ${_rfnoc_image_builder_exe} -F ${UHD_FPGA_DIR} -y ${CMAKE_CURRENT_SOURCE_DIR}/${_rfnoc_image_core_SRC} -I ${CMAKE_SOURCE_DIR}
+ )
+ endif()
+endmacro()
+
+########################################################################
+# Create uninstall target
+########################################################################
+configure_file(
+ ${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake
+@ONLY)
+add_custom_target(uninstall
+ ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake
+)
+
+########################################################################
+# Install cmake search helper for this library
+########################################################################
+if(NOT CMAKE_MODULES_DIR)
+ set(CMAKE_MODULES_DIR lib${LIB_SUFFIX}/cmake)
+endif(NOT CMAKE_MODULES_DIR)
+
+#TODO
+#install(FILES cmake/Modules/RfnocExampleConfig.cmake
+# DESTINATION ${CMAKE_MODULES_DIR}/rfnoc
+#)
+
+########################################################################
+# Subdirectories
+########################################################################
+if(UHD_FPGA_DIR)
+ add_subdirectory(blocks)
+ add_subdirectory(fpga)
+ add_subdirectory(icores)
+endif()
+if(UHD_FOUND)
+ add_subdirectory(include/rfnoc/example)
+ add_subdirectory(lib)
+ add_subdirectory(apps)
+endif()
diff --git a/host/examples/rfnoc-example/README.md b/host/examples/rfnoc-example/README.md
new file mode 100644
index 000000000..608c54745
--- /dev/null
+++ b/host/examples/rfnoc-example/README.md
@@ -0,0 +1,28 @@
+# RFNoC: An example out-of-tree module
+
+This directory contains a fully functional out-of-tree module with a gain block.
+It serves as an example for OOT modules with UHD 4.0 and above.
+
+## Directory Structure
+
+* `blocks`: This directory contains all the block definitions. These block
+ definitions can be read by the RFNoC tools, and will get installed into the
+ system for use by other out-of-tree modules.
+
+* `cmake`: This directory only needs to be modified if this OOT module will
+ come with its own custom CMake modules.
+
+* `fpga`: This directory contains the source code for the HDL modules of the
+ individual RFNoC blocks, along with their testbenches, and additional modules
+ required to build the blocks. There is one subdirectory for every block.
+
+* `include/rfnoc/example`: Here, all the header files for the block controllers
+ are stored, along with any other include files that should be installed when
+ installing this OOT module.
+
+* `lib`: Here, all the non-header source files for the block controllers are stored,
+ along with any other include file that should be installed when installing
+ this OOT module. This includes the block controller cpp files.
+
+* `apps`: This contains an example application that links against UHD and this
+ OOT module. The app does not get installed, it resides in the build directory.
diff --git a/host/examples/rfnoc-example/apps/CMakeLists.txt b/host/examples/rfnoc-example/apps/CMakeLists.txt
new file mode 100644
index 000000000..db704b720
--- /dev/null
+++ b/host/examples/rfnoc-example/apps/CMakeLists.txt
@@ -0,0 +1,40 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+# This app needs Boost
+set(BOOST_REQUIRED_COMPONENTS
+ program_options
+ system
+)
+if(MSVC)
+ set(BOOST_ALL_DYN_LINK "${BOOST_ALL_DYN_LINK}" CACHE BOOL "boost enable dynamic linking")
+ if(BOOST_ALL_DYN_LINK)
+ add_definitions(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc
+ else(BOOST_ALL_DYN_LINK)
+ set(BOOST_REQUIRED_COMPONENTS) #empty components list for static link
+ endif(BOOST_ALL_DYN_LINK)
+endif(MSVC)
+find_package(Boost 1.58 REQUIRED ${BOOST_REQUIRED_COMPONENTS})
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/lib
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/lib
+ ${CMAKE_BINARY_DIR}/include
+ ${UHD_INCLUDE_DIRS}
+ ${Boost_INCLUDE_DIR}
+)
+link_directories(
+ ${Boost_LIBRARY_DIRS}
+)
+
+add_executable(init_gain_block
+ init_gain_block.cpp
+)
+target_link_libraries(init_gain_block
+ ${UHD_LIBRARIES}
+ ${Boost_LIBRARIES}
+)
diff --git a/host/examples/rfnoc-example/apps/init_gain_block.cpp b/host/examples/rfnoc-example/apps/init_gain_block.cpp
new file mode 100644
index 000000000..f9de5f92c
--- /dev/null
+++ b/host/examples/rfnoc-example/apps/init_gain_block.cpp
@@ -0,0 +1,77 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+// Example application to show how to write applications that depend on both UHD
+// and out-of-tree RFNoC modules.
+//
+// It will see if a USRP is runnging the gain block, if so, it will test to see
+// if it can change the gain.
+
+#include <uhd/exception.hpp>
+#include <uhd/rfnoc_graph.hpp>
+#include <uhd/utils/safe_main.hpp>
+#include <rfnoc/example/gain_block_control.hpp>
+#include <boost/program_options.hpp>
+
+namespace po = boost::program_options;
+
+int UHD_SAFE_MAIN(int argc, char* argv[])
+{
+ std::string args;
+
+ // setup the program options
+ po::options_description desc("Allowed options");
+ // clang-format off
+ desc.add_options()
+ ("help", "help message")
+ ("args", po::value<std::string>(&args)->default_value(""), "USRP device address args")
+ ;
+ // clang-format on
+ po::variables_map vm;
+ po::store(po::parse_command_line(argc, argv, desc), vm);
+ po::notify(vm);
+
+ // print the help message
+ if (vm.count("help")) {
+ std::cout << "Init RFNoC gain block " << desc << std::endl;
+ std::cout << std::endl
+ << "This application attempts to find a gain block in a USRP "
+ "and tries to peek/poke registers..\n"
+ << std::endl;
+ return EXIT_SUCCESS;
+ }
+
+ // Create RFNoC graph object:
+ auto graph = uhd::rfnoc::rfnoc_graph::make(args);
+
+ // Verify we have a gain block:
+ auto gain_blocks = graph->find_blocks<rfnoc::example::gain_block_control>("");
+ if (gain_blocks.empty()) {
+ std::cout << "No gain block found." << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ auto gain_block =
+ graph->get_block<rfnoc::example::gain_block_control>(gain_blocks.front());
+ if (!gain_block) {
+ std::cout << "ERROR: Failed to extract block controller!" << std::endl;
+ return EXIT_FAILURE;
+ }
+ constexpr uint32_t new_gain_value = 42;
+ gain_block->set_gain_value(new_gain_value);
+ const uint32_t gain_value_read = gain_block->get_gain_value();
+
+ if (gain_value_read != new_gain_value) {
+ std::cout << "ERROR: Readback of gain value not working! "
+ << "Expected: " << new_gain_value << " Read: " << gain_value_read
+ << std::endl;
+ return EXIT_FAILURE;
+ } else {
+ std::cout << "Gain value read/write loopback successful!" << std::endl;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/host/examples/rfnoc-example/blocks/CMakeLists.txt b/host/examples/rfnoc-example/blocks/CMakeLists.txt
new file mode 100644
index 000000000..046af2265
--- /dev/null
+++ b/host/examples/rfnoc-example/blocks/CMakeLists.txt
@@ -0,0 +1,17 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+# Reminder: This won't auto-update when you add a file, you need to re-run CMake
+# to re-generate the glob. Or, you add the files directly into the install()
+# statement below.
+file(GLOB yml_files "*.yml")
+# List all header files here (UHD and GNU Radio)
+install(
+ FILES
+ ${yml_files}
+ DESTINATION ${PROJECT_DATA_DIR}/blocks
+ COMPONENT blocks
+)
diff --git a/host/examples/rfnoc-example/blocks/gain.yml b/host/examples/rfnoc-example/blocks/gain.yml
new file mode 100644
index 000000000..58172ae36
--- /dev/null
+++ b/host/examples/rfnoc-example/blocks/gain.yml
@@ -0,0 +1,51 @@
+schema: rfnoc_modtool_args
+module_name: gain
+version: 1.0
+rfnoc_version: 1.0
+chdr_width: 64
+noc_id: 0xB16
+
+clocks:
+ - name: rfnoc_chdr
+ freq: "[]"
+ - name: rfnoc_ctrl
+ freq: "[]"
+
+control:
+ sw_iface: nocscript
+ fpga_iface: ctrlport
+ interface_direction: slave
+ fifo_depth: 32
+ clk_domain: rfnoc_chdr
+ ctrlport:
+ byte_mode: False
+ timed: False
+ has_status: False
+
+data:
+ fpga_iface: axis_pyld_ctxt
+ clk_domain: rfnoc_chdr
+ inputs:
+ in:
+ index: 0
+ item_width: 32
+ nipc: 1
+ context_fifo_depth: 2
+ payload_fifo_depth: 2
+ format: int32
+ mdata_sig: ~
+ outputs:
+ out:
+ index: 0
+ item_width: 32
+ nipc: 1
+ context_fifo_depth: 2
+ payload_fifo_depth: 2
+ format: int32
+ mdata_sig: ~
+
+io_port:
+
+registers:
+
+properties:
diff --git a/host/examples/rfnoc-example/cmake/Modules/run_testbench.sh.in b/host/examples/rfnoc-example/cmake/Modules/run_testbench.sh.in
new file mode 100755
index 000000000..d1256cf97
--- /dev/null
+++ b/host/examples/rfnoc-example/cmake/Modules/run_testbench.sh.in
@@ -0,0 +1,69 @@
+#!@BASH@
+
+if [[ ! $# -eq 4 ]]; then
+ echo "Usage: $0 uhd_fpga_dir test_device makefile_dir test_target"
+ echo ""
+ echo "Arguments:"
+ echo "- uhd_fpga_dir: Path to fpga repository (without usrp3/top)"
+ echo "- test_device: Device used for testing (e.g. x300, n3xx, e320)"
+ echo "- makefile_dir: Path to the directory with the testbench Makefile"
+ echo "- test_target: Test target (xsim, vsim)"
+ exit 0
+fi
+
+uhd_fpga_dir=$1
+uhd_fpga_dir_top=$uhd_fpga_dir/usrp3/top
+test_device=$2
+test_target=$4
+makefile_dir=$3
+# Clear $# and pos args so setupenv.sh doesn't freak out
+shift
+shift
+shift
+shift
+
+# Need to convert device types to directory names
+device_dir=$test_device
+if [ $device_dir == "x310" ]; then
+ device_dir=x300
+fi
+if [ $device_dir == "n300" ]; then
+ device_dir=n3xx
+fi
+if [ $device_dir == "n310" ]; then
+ device_dir=n3xx
+fi
+if [ $device_dir == "n320" ]; then
+ device_dir=n3xx
+fi
+if [ $device_dir == "e310" ]; then
+ device_dir=e31x
+fi
+
+# Now check the paths are valid
+if [ ! -r $uhd_fpga_dir_top/Makefile.common ]; then
+ echo "ERROR! '${uhd_fpga_dir_top}' is not a valid top-level FPGA directory."
+ exit 1
+fi
+device_dir=$uhd_fpga_dir_top/$device_dir
+if [ ! -r $device_dir/setupenv.sh ]; then
+ echo "ERROR! '${test_device}' is not a valid USRP device."
+ exit 1
+fi
+echo "Using device directory: $device_dir"
+if [ ! -r $makefile_dir/Makefile ]; then
+ echo "ERROR! '${makefile_dir}' does not point to an RFNoC block."
+ exit 1
+fi
+# Check the Makefile actually contains a testbench target
+if ! grep -q viv_simulator.mak $makefile_dir/Makefile; then
+ echo "ERROR! '${makefile_dir}/Makefile' does not contain a test target!."
+ exit 1
+fi
+
+# Load environment
+echo "Loading environment from: '$device_dir/setupenv.sh'"
+source $device_dir/setupenv.sh
+
+# And, go
+make -C $makefile_dir $test_target UHD_FPGA_DIR=$uhd_fpga_dir || exit 1
diff --git a/host/examples/rfnoc-example/cmake/cmake_uninstall.cmake.in b/host/examples/rfnoc-example/cmake/cmake_uninstall.cmake.in
new file mode 100644
index 000000000..9ae1ae4bd
--- /dev/null
+++ b/host/examples/rfnoc-example/cmake/cmake_uninstall.cmake.in
@@ -0,0 +1,32 @@
+# http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F
+
+IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+ MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
+ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+
+FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
+STRING(REGEX REPLACE "\n" ";" files "${files}")
+FOREACH(file ${files})
+ MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
+ IF(EXISTS "$ENV{DESTDIR}${file}")
+ EXEC_PROGRAM(
+ "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+ OUTPUT_VARIABLE rm_out
+ RETURN_VALUE rm_retval
+ )
+ IF(NOT "${rm_retval}" STREQUAL 0)
+ MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
+ ENDIF(NOT "${rm_retval}" STREQUAL 0)
+ ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}")
+ EXEC_PROGRAM(
+ "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+ OUTPUT_VARIABLE rm_out
+ RETURN_VALUE rm_retval
+ )
+ IF(NOT "${rm_retval}" STREQUAL 0)
+ MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
+ ENDIF(NOT "${rm_retval}" STREQUAL 0)
+ ELSE(EXISTS "$ENV{DESTDIR}${file}")
+ MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
+ ENDIF(EXISTS "$ENV{DESTDIR}${file}")
+ENDFOREACH(file)
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
diff --git a/host/examples/rfnoc-example/icores/.gitignore b/host/examples/rfnoc-example/icores/.gitignore
new file mode 100644
index 000000000..363fc924d
--- /dev/null
+++ b/host/examples/rfnoc-example/icores/.gitignore
@@ -0,0 +1,2 @@
+*.v
+*.hex
diff --git a/host/examples/rfnoc-example/icores/CMakeLists.txt b/host/examples/rfnoc-example/icores/CMakeLists.txt
new file mode 100644
index 000000000..71bb8caa3
--- /dev/null
+++ b/host/examples/rfnoc-example/icores/CMakeLists.txt
@@ -0,0 +1,7 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+RFNOC_REGISTER_IMAGE_CORE(SRC x310_rfnoc_image_core.yml)
diff --git a/host/examples/rfnoc-example/icores/x310_rfnoc_image_core.yml b/host/examples/rfnoc-example/icores/x310_rfnoc_image_core.yml
new file mode 100644
index 000000000..2fa512990
--- /dev/null
+++ b/host/examples/rfnoc-example/icores/x310_rfnoc_image_core.yml
@@ -0,0 +1,109 @@
+# General parameters
+# -----------------------------------------
+schema: rfnoc_imagebuilder_args # Identifier for the schema used to validate this file
+copyright: 'Ettus Research, A National Instruments Brand' # Copyright information used in file headers
+license: 'SPDX-License-Identifier: LGPL-3.0-or-later' # License information used in file headers
+version: 1.0 # File version
+rfnoc_version: 1.0 # RFNoC protocol version
+chdr_width: 64 # Bitwidth of the CHDR bus for this block
+device: 'x310' # USRP device descriptor file
+default_target: 'X310_HG'
+
+# A list of all stream endpoints in design
+# ----------------------------------------
+stream_endpoints:
+ ep0: # Stream endpoint name
+ ctrl: True # Endpoint passes control traffic
+ data: True # Endpoint passes data traffic
+ buff_size: 32768 # Ingress buffer size for data
+ ep1: # Stream endpoint name
+ ctrl: False # Endpoint passes control traffic
+ data: True # Endpoint passes data traffic
+ buff_size: 0 # Ingress buffer size for data
+ ep2: # Stream endpoint name
+ ctrl: False # Endpoint passes control traffic
+ data: True # Endpoint passes data traffic
+ buff_size: 32768 # Ingress buffer size for data
+ ep3: # Stream endpoint name
+ ctrl: False # Endpoint passes control traffic
+ data: True # Endpoint passes data traffic
+ buff_size: 0 # Ingress buffer size for data
+ ep4: # Stream endpoint name
+ ctrl: False # Endpoint passes control traffic
+ data: True # Endpoint passes data traffic
+ buff_size: 32768 # Ingress buffer size for data
+
+# A list of all NoC blocks in design
+# ----------------------------------
+noc_blocks:
+ duc0: # NoC block name
+ block_desc: 'duc_1x64.yml' # Block device descriptor file
+ ddc0:
+ block_desc: 'ddc_2x64.yml'
+ radio0:
+ block_desc: 'radio_2x64.yml'
+ duc1:
+ block_desc: 'duc_1x64.yml'
+ ddc1:
+ block_desc: 'ddc_2x64.yml'
+ radio1:
+ block_desc: 'radio_2x64.yml'
+ fifo0:
+ block_desc: 'axi_ram_fifo_2x64.yml'
+ parameters:
+ # These parameters match the interface on the x300/X310
+ MEM_DATA_W: 64
+ MEM_ADDR_W: 30
+ FIFO_ADDR_BASE: "{30'h02000000, 30'h00000000}"
+ FIFO_ADDR_MASK: "{30'h01FFFFFF, 30'h01FFFFFF}"
+ MEM_CLK_RATE: "300e6"
+ gain0:
+ block_desc: 'gain.yml'
+
+# A list of all static connections in design
+# ------------------------------------------
+# Format: A list of connection maps (list of key-value pairs) with the following keys
+# - srcblk = Source block to connect
+# - srcport = Port on the source block to connect
+# - dstblk = Destination block to connect
+# - dstport = Port on the destination block to connect
+connections:
+ - { srcblk: ep0, srcport: out0, dstblk: fifo0, dstport: port0 }
+ - { srcblk: fifo0, srcport: port0, dstblk: duc0, dstport: port0 }
+ - { srcblk: duc0, srcport: port0, dstblk: radio0, dstport: port0 }
+ - { srcblk: radio0, srcport: port0, dstblk: ddc0, dstport: port0 }
+ - { srcblk: radio0, srcport: port1, dstblk: ddc0, dstport: port1 }
+ - { srcblk: ddc0, srcport: port0, dstblk: ep0, dstport: in0 }
+ - { srcblk: ddc0, srcport: port1, dstblk: ep1, dstport: in0 }
+ - { srcblk: ep2, srcport: out0, dstblk: fifo0, dstport: port1 }
+ - { srcblk: fifo0, srcport: port1, dstblk: duc1, dstport: port0 }
+ - { srcblk: duc1, srcport: port0, dstblk: radio1, dstport: port0 }
+ - { srcblk: radio1, srcport: port0, dstblk: ddc1, dstport: port0 }
+ - { srcblk: radio1, srcport: port1, dstblk: ddc1, dstport: port1 }
+ - { srcblk: ddc1, srcport: port0, dstblk: ep2, dstport: in0 }
+ - { srcblk: ddc1, srcport: port1, dstblk: ep3, dstport: in0 }
+ - { srcblk: ep4, srcport: out0, dstblk: gain0, dstport: in }
+ - { srcblk: gain0, srcport: out, dstblk: ep4, dstport: in0 }
+ - { srcblk: radio0, srcport: ctrl_port, dstblk: _device_, dstport: ctrlport_radio0 }
+ - { srcblk: radio1, srcport: ctrl_port, dstblk: _device_, dstport: ctrlport_radio1 }
+ - { srcblk: _device_, srcport: x300_radio0, dstblk: radio0, dstport: x300_radio }
+ - { srcblk: _device_, srcport: x300_radio1, dstblk: radio1, dstport: x300_radio }
+ - { srcblk: _device_, srcport: time_keeper, dstblk: radio0, dstport: time_keeper }
+ - { srcblk: _device_, srcport: time_keeper, dstblk: radio1, dstport: time_keeper }
+ - { srcblk: _device_, srcport: dram, dstblk: fifo0, dstport: axi_ram }
+
+# A list of all clock domain connections in design
+# # ------------------------------------------
+# # Format: A list of connection maps (list of key-value pairs) with the following keys
+# # - srcblk = Source block to connect (Always "_device"_)
+# # - srcport = Clock domain on the source block to connect
+# # - dstblk = Destination block to connect
+# # - dstport = Clock domain on the destination block to connect
+clk_domains:
+ - { srcblk: _device_, srcport: radio, dstblk: radio0, dstport: radio }
+ - { srcblk: _device_, srcport: ce, dstblk: ddc0, dstport: ce }
+ - { srcblk: _device_, srcport: ce, dstblk: duc0, dstport: ce }
+ - { srcblk: _device_, srcport: radio, dstblk: radio1, dstport: radio }
+ - { srcblk: _device_, srcport: ce, dstblk: ddc1, dstport: ce }
+ - { srcblk: _device_, srcport: ce, dstblk: duc1, dstport: ce }
+
diff --git a/host/examples/rfnoc-example/include/rfnoc/example/CMakeLists.txt b/host/examples/rfnoc-example/include/rfnoc/example/CMakeLists.txt
new file mode 100644
index 000000000..04ee6810e
--- /dev/null
+++ b/host/examples/rfnoc-example/include/rfnoc/example/CMakeLists.txt
@@ -0,0 +1,13 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+# List all header files here (UHD and GNU Radio)
+install(
+ FILES
+ gain_block_control.hpp
+ DESTINATION include/rfnoc/gain
+ COMPONENT headers
+)
diff --git a/host/examples/rfnoc-example/include/rfnoc/example/gain_block_control.hpp b/host/examples/rfnoc-example/include/rfnoc/example/gain_block_control.hpp
new file mode 100644
index 000000000..5943454b8
--- /dev/null
+++ b/host/examples/rfnoc-example/include/rfnoc/example/gain_block_control.hpp
@@ -0,0 +1,39 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_RFNOC_EXAMPLE_GAIN_BLOCK_CONTROL_HPP
+#define INCLUDED_RFNOC_EXAMPLE_GAIN_BLOCK_CONTROL_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/rfnoc/noc_block_base.hpp>
+#include <uhd/types/stream_cmd.hpp>
+
+namespace rfnoc { namespace example {
+
+/*! Block controller for the gain block: Multiply amplitude of signal
+ *
+ * This block multiplies the signal input with a fixed gain value.
+ */
+class UHD_API gain_block_control : public uhd::rfnoc::noc_block_base
+{
+public:
+ RFNOC_DECLARE_BLOCK(gain_block_control)
+
+ //! The register address of the gain value
+ static const uint32_t REG_GAIN_VALUE;
+
+ /*! Set the gain value
+ */
+ virtual void set_gain_value(const uint32_t gain) = 0;
+
+ /*! Get the current gain value (read it from the device)
+ */
+ virtual uint32_t get_gain_value() = 0;
+};
+
+}} // namespace rfnoc::example
+
+#endif /* INCLUDED_RFNOC_EXAMPLE_GAIN_BLOCK_CONTROL_HPP */
diff --git a/host/examples/rfnoc-example/lib/CMakeLists.txt b/host/examples/rfnoc-example/lib/CMakeLists.txt
new file mode 100644
index 000000000..45b5c9b03
--- /dev/null
+++ b/host/examples/rfnoc-example/lib/CMakeLists.txt
@@ -0,0 +1,83 @@
+#
+# Copyright 2019 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+########################################################################
+# Setup library
+########################################################################
+#include(GrPlatform) #define LIB_SUFFIX
+
+# List any C++ sources here. If there are no sources (e.g., because there
+# is no block controller), then this directory will be skipped.
+list(APPEND rfnoc_example_sources
+ gain_block_control.cpp
+)
+if(NOT rfnoc_example_sources)
+ MESSAGE(STATUS "No C++ sources... skipping lib/")
+ return()
+endif()
+
+########################################################################
+# Setup the include and linker paths
+########################################################################
+include_directories(
+ ${CMAKE_SOURCE_DIR}/lib
+ ${CMAKE_SOURCE_DIR}/include
+ ${CMAKE_BINARY_DIR}/lib
+ ${CMAKE_BINARY_DIR}/include
+ ${UHD_INCLUDE_DIRS}
+ ${Boost_INCLUDE_DIR}
+)
+
+link_directories(
+ ${Boost_LIBRARY_DIRS}
+)
+
+add_library(rfnoc-example SHARED
+ ${rfnoc_example_sources}
+)
+target_link_libraries(rfnoc-example
+ ${UHD_LIBRARIES}
+ ${Boost_LIBRARIES}
+ ${GNURADIO_ALL_LIBRARIES}
+ ${ETTUS_LIBRARIES}
+)
+set_target_properties(rfnoc-example
+ PROPERTIES DEFINE_SYMBOL "rfnoc_example_EXPORTS")
+
+########################################################################
+# Install built library files
+########################################################################
+install(TARGETS rfnoc-example
+ LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file
+ ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file
+ RUNTIME DESTINATION bin # .dll file
+)
+
+########################################################################
+# Build and register unit test
+########################################################################
+#include(GrTest)
+#
+#include_directories(${CPPUNIT_INCLUDE_DIRS})
+#
+#list(APPEND test_gain_sources
+# ${CMAKE_CURRENT_SOURCE_DIR}/test_gain.cc
+# ${CMAKE_CURRENT_SOURCE_DIR}/qa_gain.cc
+#)
+#
+#add_executable(test-gain ${test_gain_sources})
+#
+#target_link_libraries(
+# test-gain
+# ${GNURADIO_RUNTIME_LIBRARIES}
+# ${Boost_LIBRARIES}
+# ${CPPUNIT_LIBRARIES}
+# ${ETTUS_LIBRARIES}
+# ${PC_ETTUS_LIBDIR}
+# gnuradio-gain
+#)
+#
+#GR_ADD_TEST(test_gain test-gain)
diff --git a/host/examples/rfnoc-example/lib/gain_block_control.cpp b/host/examples/rfnoc-example/lib/gain_block_control.cpp
new file mode 100644
index 000000000..d53bafc8b
--- /dev/null
+++ b/host/examples/rfnoc-example/lib/gain_block_control.cpp
@@ -0,0 +1,41 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+// Include our own header:
+#include <rfnoc/example/gain_block_control.hpp>
+
+// These two includes are the minimum required to implement a block:
+#include <uhd/rfnoc/defaults.hpp>
+#include <uhd/rfnoc/registry.hpp>
+
+using namespace rfnoc::example;
+using namespace uhd::rfnoc;
+
+const uint32_t gain_block_control::REG_GAIN_VALUE = 0x00;
+
+class gain_block_control_impl : public gain_block_control
+{
+public:
+ RFNOC_BLOCK_CONSTRUCTOR(gain_block_control)
+ {
+ }
+
+ void set_gain_value(const uint32_t gain)
+ {
+ regs().poke32(REG_GAIN_VALUE, gain);
+ }
+
+ uint32_t get_gain_value()
+ {
+ return regs().peek32(REG_GAIN_VALUE);
+ }
+
+private:
+
+};
+
+UHD_RFNOC_BLOCK_REGISTER_DIRECT(
+ gain_block_control, 0xb16, "Gain", CLOCK_KEY_GRAPH, "bus_clk")