diff options
author | Sugandha Gupta <sugandha.gupta@ettus.com> | 2019-10-15 11:52:46 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 12:21:32 -0800 |
commit | a801d6b046743140e9a50c7788dd17dd71f5540a (patch) | |
tree | 58d164e1b4cb2a8d871ca532287699f3912ae3d8 | |
parent | 2a7e69d862f661075b98bab19e58d958c28a9af8 (diff) | |
download | uhd-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>
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") |