diff options
Diffstat (limited to 'host')
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") | 
