\page page_rfnoc_tools RFNoC Tools

\tableofcontents

# Basics

The RFNoC framework provides code generation tools to create blocks and to assemble an FPGA design using existing blocks. All tools have a command line interface (CLI) and graphical user interface (GUI). The block creation tool, called RFNoC ModTool, accepts \ref noc_shell_generator_options_anchor "basic parameters", \ref noc_shell_generation_options_control_anchor "control-plane parameters", \ref noc_shell_generation_options_data_anchor "data-plane parameters", and other user options to generate Verilog and C++ code templates for a new block, units tests and the supporting metadata files for design assembly and for use by UHD. After a basic template for a block has been created, users can iteratively develop the FPGA and software implementation for the block, and then move to the next step of design assembly. The design assembly tool, called RFNoC Image Builder, accepts parameters and performs the steps described in the section \ref design_assembly_toolflow_anchor "Design Assembly Toolflow" to build an FPGA image that instantiates blocks from the local block database, with connections specified statically at compile time. The generated image can then be deployed onto a USRP for UHD to automatically detect and target the blocks on the device.

User preferences can be communicated using files or GUI actions. The YAML format is used to describe all user-options and the XML format is used for generated files. In almost all cases, it should not be necessary to modify the XML files generated by the tools. The overview and interaction of the tools is shown in the figure below and is described in the following sections.

\image html rfnoc_toolflow_ov.png "RFNoC Tool flow Overview" width=800px


# RFNoC ModTool

## Overview

RFNoC ModTool should be used to generate a new RFNoC block which may have custom user-defined logic. The FPGA and software interfaces to the block are detailed in Section \ref noc_block_user_interface_anchor "NoC Block User Interface" and Section \ref block_controller_anchor "Block Controller" The inputs to RFNoC ModTool are described above, and using these *User Preferences*, the tool will generate the following files:

- C++ Source

  - *Block Controller Template*: The block controller template contains boilerplate UHD code to communicate with the block in the FPGA. It will contain a basic register interface, and placeholders to define and implement block arguments and block properties.

  - *Block Controller Build Script*: The build script is a cmake project that can be used to build a dynamic library that UHD can call into to instantiate the custom block.

  - *Unit Tests*: A template to implement basic unit tests to validate the block controller.

- HDL Source

  - *NoC Shell*: A fully functional Verilog NoC Shell that has all the interfaces requested by the user.

  - *NoC Block Template*: A Verilog template for the NoC Block, which includes an instantiation for the NoC shell and a placeholder for users to insert their custom logic.

  - *Block Testbench Template*: A testbench template to allow users to write HDL unit tests for their block.

- GNU Radio Bindings (if GNU Radio is installed)

  - A GRC XML file to include the block into a GNU Radio flow graph

  - Any additional shim code to enable the block to function in GNU Radio

- Metadata

  - *Block Definition File*: This is an XML file that defines the interfaces and behavior of the block. The block definition file is used by UHD to discover capabilities of the block and to load the appropriate block controller class. It is also used by the RFNoC Image Builder (see below) to assemble an FPGA design using the block.

The user can interact with RFNoC ModTool using a GUI or using the CLI and specifying a YAML file with following format.

## Input Format

The following is a description (and example) of the input YAML format.

```yaml
# General parameters
# -----------------------------------------
schema: rfnoc_modtool_args      # Name of the schema used to validate this file
module_name: my_block           # Name of the RFNoC block
version: "1.0"                  # Format version of this file
rfnoc_version: "1.0"            # Version of RFNoC
chdr_width: 256                 # Bit width of the CHDR bus
noc_id: 0xDEADBEEF              # NoC ID for this block


# Block-specific parameters
# -------------------------
# Optional section, use this if the block is parametrizable.
# Specify these as key/value pairs. Mako expressions are allowed.
parameters:
  keyname: "${ 1 + 1 }"

# HDL Parameters (Generics)
# -------------------------
# Optional section. If this section is not given, the previous section
# (parameters) is made available as HDL parameters. Mako expressions are
# allowed, and may reference parameters. When the HDL code for the RFNoC
# block is generated, these are used to generate module parameters for
# the block.
hdl_parameters:
  MY_GENERIC: "${ parameters['keyname'] }"

# A list of all clocks needed by this block
# -----------------------------------------
# - rfnoc_chdr_clk and rfnoc_ctrl_clk are required clocks
# - All other clocks will be considered as user-defined clocks
clocks:
  - rfnoc_chdr:                     # Clock name prefix
      freq: 'range(100e6, 300e6)'   # Acceptable frequency range of this clock
  - rfnoc_ctrl:
      freq: 'range(10e6, 100e6)'
  - user0:
      freq: 'range(0, 1e9)'

# Options for the control interface
# ---------------------------------
control:
  sw_iface: nocscript           # Software controller implementation: {nocscript, c++}
  fpga_iface: axis_ctrl         # Type of FPGA interface: {axis_ctrl, ctrlport}
  interface_direction: slave    # Direction of control endpoint:
                                #   {slave, master_slave, remote_master_slave}
  fifo_depth: 32                # Number of 32-bit words in input buffer: [32, 4096]
  clk_domain: rfnoc_ctrl        # Clock domain for ctrl interface: {<Choose from "clocks">}
  ctrlport:                     # ctrlport specific options
    byte_mode: True             # Instantiate a byte enable: {True, False}
    timed: False                # Allow timed commands: {True, False}
    has_status: False           # Instantiate a status bus: {True, False}
  axis_ctrl:                    # axis_ctrl specific
    64_bit: False               # Instantiate a 64-bit bus instead of 32: {True, False}

# Options for the data interface
# ------------------------------
data:
  fpga_iface: axis_pyld_ctxt    # Type of FPGA interface:
                                #   {axis_chdr, axis_pyld_ctxt, axis_data}
  clk_domain: user0             # Clock domain for data interface: {<Choose from "clocks">}
  # A list of all input ports for this block:
  inputs:
    in0:                        # Port name
      context: True             # Is context port instantiated?: {True, False}
      num_ports: 2              # Optional number of ports (if not 1): [1, 64]
      item_width: 32            # Bit width of a sample
      nipc: 2                   # Number of samples per cycle (items per cycle)
      format: sc16              # Sample data format: {int16, sc8, sc16, ...}
      mdata_sig: ~              # Hash of the metadata signature: {~, MD5 sum}
      context_fifo_depth: 32    # Depth of context FIFO: Powers of 2 in [1, ∞)
      payload_fifo_depth: 32    # Depth of payload FIFO: Powers of 2 in [1, ∞)
    in1:
      context: True
      item_width: 16
      nipc: 4
      format: int16
      mdata_sig: 0412ffc5e7e1a19d8d23b4e288b3ced2
      context_fifo_depth: 32
      payload_fifo_depth: 32
  # A list of all output ports for this block:
  outputs:
    out0:
      context: True
      item_width: 32
      nipc: 2
      format: sc16
      mdata_sig: 0412ffc5e7e1a19d8d23b4e288b3ced4
      context_fifo_depth: 32
      payload_fifo_depth: 32
    out_1:
      context: True
      item_width: 16
      nipc: 4
      format: int16
      mdata_sig: ~
      context_fifo_depth: 32
      payload_fifo_depth: 32

# A list of all IO ports for this block
# -------------------------------------
io_ports:
  time:                       # Name of IO port
    type: timekeeper          # Descriptor for the IO signature of this port
    drive: listener           # Drive mode for port: {master, slave, listener, broadcaster}
  custom_xy:
    type: my_iface_sic
    drive: slave

# A list of files that need to be included for the build process
# --------------------------------------------------------------
fpga_includes:
  # $(LIB_DIR) points to the main RFNoC library, where all the core files
  # are stored. This is generally not used for out-of-tree blocks, unless
  # they need to include modules from the main RFNoC source tree. This line
  # will include the Makefile.srcs file into the build process, which may
  # provide additional variables or file paths.
  - include: "$(LIB_DIR)/rfnoc/blocks/rfnoc_block_my_block/Makefile.srcs"
    # This variable is defined in the Makefile.srcs file referenced above.
    make_var: "$(RFNOC_BLOCK_MY_BLOCK_SRCS)"
  # If the block is defined in an out-of-tree module, relative filenames
  # are preferred. The build process will then search for the file.
  - include: fpga/my_module/rfnoc_block_my_mblock/Makefile.srcs
    make_var: "$(RFNOC_BLOCK_MY_BLOCK_SRCS)"
  # Mako expressions are allowed:
    # Assumption is that 'my_block_mako_expression' evaluates to a path
  - include: "${ my_block_mako_expression }"
    make_var: "$(RFNOC_BLOCK_MY_BLOCK_SRCS)"

# A list of constraint files that need to be included for the build process
# -------------------------------------------------------------------------
# Optional. Use this if the Vivado build requires special constraint files.
constraints:
  # List one file per list entry. Mako expressions are allowed.
  - constraints/timing/my_block_timing.xdc

# A list of DTS files that become part of the device tree overlay
# ---------------------------------------------------------------
# Optional. Only for embedded devices (e.g., X4xx, N3xx, E3xx).
# List one file per list entry. Mako expressions are allowed.
dts_includes:
  - path/to/device_tree_info.dts

# Custom io_signatures only relevant for this block
# -------------------------------------------------
# Optional. Use if you have io_ports with a custom signature.
io_signatures:
  my_iface_sic:
    type: master-slave
    ports: [] # List ports here, with name/type/width
```

# RFNoC Image Builder

## Overview

RFNoC Image Builder should be used to generate an FPGA design and a bitstream using blocks provided by Ettus Research or created by the user. RFNoC Image Builder will generate Verilog to instantiate blocks requested by the user, connect them and integrate all components with the USRP board support package, to create a full design that can be synthesized and built into a bitstream. The code and the bitstream is the only output of this tool.

The user can interact with RFNoC Image Builder using a GUI or using the CLI and specifying a YAML file with following format.

## Input Format

The following is an example of the input YAML format.

```yaml
# General parameters
# -----------------------------------------
schema: rfnoc_imagebuilder_args         # Identifier for the schema used to validate this file
version: "1.0"                          # File version
rfnoc_version: "1.0"                    # RFNoC protocol version
chdr_width: 64                          # Bit width of the CHDR bus for this image
device: 'x310'                          # USRP device to build for
default_target: 'X310_HG'               # Default FPGA image type to build


# 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
    num_data_i: 1                       # Number of data input ports
    num_data_o: 2                       # Number of data output ports
    buff_size: 32768                    # Ingress buffer size for data
  ep1:
    ctrl: False
    data: True
    num_data_i: 1
    num_data_o: 1
    buff_size: 32768

# A list of all NoC blocks in design
# ----------------------------------
noc_blocks:
  blk0:                             # NoC block name
    block_desc: 'blk0_desc.yml'     # Block device descriptor file
    parameters:                     # Optional list of module parameters
      MEM_DEPTH: 64                 # Block-specific module parameters to use
      MASKS: '{8'hE0, 8'h1F}'
  blk1:
     block_desc: 'blk1_desc.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: blk0,     srcport: out_0,        dstblk: blk1, dstport: din          }
    - {srcblk: blk1,     srcport: dout,         dstblk: ep0,  dstport: in0          }
    - {srcblk: ep1,      srcport: out0,         dstblk: blk0, dstport: in_1         }
    - {srcblk: blk0,     srcport: user_iface_0, dstblk: blk1, dstport: user_iface_0 }
    - {srcblk: _device_, srcport: time,         dstblk: blk0, dstport: time         }

# 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: blk1, dstport: user0  }
...
```
