Main Content

Introduction to Custom OFDM on NI USRP Radio

Since R2025a

This example shows how to deploy a custom orthogonal frequency division multiplexing (OFDM) transceiver on the FPGA of an NI™ USRP™ radio.

Introduction

In this example, you start with a Simulink® model of a custom OFDM transceiver. Follow the step-by-step guide to generate a bitstream from the model in Simulink and deploy it on an NI USRP radio using a generated MATLAB® host interface script. The example enables you to view the total bits received, number of error bits, and the constellation diagram of the decoded data.

The diagram shows the workflow.

Block diagram overview of the Target NI USRP radios workflow

For more information about how to prototype and deploy software-defined radio (SDR) algorithms on the FPGA of an NI USRP radio, see Target NI USRP Radios Workflow.

Supported Hardware

For details about the USRP radio hardware you can use with this example, see Supported Radio Devices.

When you use this example with a USRP E320 radio, some verification modes are not supported due to hardware limitations.

Design Overview

The example implements a transmitter and receiver for a custom OFDM communication system. It uses the algorithm from the Introduction to Custom OFDM (Wireless HDL Toolbox) example and provides an additional wrapper that enables you to generate a bitstream suitable for deployment on the FPGA of an NI USRP radio.

The algorithm sends an OFDM signal to the radio for transmission. It then receives the transmitted signal and decodes the data, which it sends to the host as a constellation through the onboard PL DDR buffer.

On the host, you visualize the constellation and calculate the error vector magnitude (EVM) to verify the accuracy of the demodulated OFDM data.

Block diagram overview of Introduction to Custom OFDM on NI USRP example

Open Simulink Model

The Simulink model implements the custom OFDM transceiver using a hardware modeling style and uses blocks that support HDL code generation. It uses fixed-point arithmetic and includes control signals to control the flow of data through the model.

Open the model from MATLAB.

open_system('wtCustomOFDMSL');

wtCustomOFDMSL model in Simulink

The model generates input data for and reads and plots output data from the CustomOFDMBlock subsystem.

Open the CustomOFDMBlock subsystem. This subsystem is the DUT that you generate HDL code for in this example. The CustomOFDMBlock subsystem contains three subsystems: OFDM Transceiver, Radio and Utilities, and Self-Check.

CustomOFDMBlock subsystem in Simulink

The OFDM Transceiver subsystem implements the algorithm from the Introduction to Custom OFDM (Wireless HDL Toolbox) example, which includes and an OFDM transmitter and receiver.

The Radio and Utilities subsystem contains the following subsystems and blocks:

  • Radio Interface subsystem

  • Self-Check block

  • ConstellationStreamOut block

Open the Radio Interface subsystem.

Radio Interface subsystem in Simulink

This subsystem contains a transmit path and a receive path.

  • In the transmit path, the generated OFDM signal is sent to the RadioOutputController block. This block buffers the data and sends it to the radio transmitter. The block monitors the amount of data in the buffer and controls the speed of the transmitted data to avoid overflows or underflows at the radio transmitter.

  • In the receive path, you can select to either loop back data directly from the output of the transmitter, or to transmit and capture the data over the air. The Prebuffer block buffers and reshapes the input samples for processing in the DUT.

The Self-Check block checks if the OFDM data is decoded correctly. You can check how many bits are received from the bitsReceived register and number of error bits from the bitErrors register.

The ConstellationStreamOut block in the Radio and Utilities subsystem uses the Data Packager block to format the constellation data into packets that conform with the simplified AXI-stream protocol. For more information, see Simplified AXI-Stream Protocol. The packetized data is sent to the host.

ConstellationStreamOut block in the Radio and Utilities subsystem in Simulink

The Prebuffer and Data Packager blocks can be found in the targetingLib library.

which("targetingLib")

Set Up Environment and Radio

To target an NI USRP radio with Wireless Testbench™, you must first install and configure additional toolboxes, support packages, and third-party tools. For more information, see Installation for Targeting NI USRP Radios.

If you have not previously saved a radio setup configuration for your radio hardware, use the radioSetupWizard function to open the Radio Setup wizard and follow the steps. To see your previously saved radio setup configurations, use the radioConfigurations function.

Configure Model for IP Core Generation

First, use the hdlsetuptoolpath (HDL Coder) function to set up the Xilinx® tool chain. Specify the path to your Vivado® bin directory. For more information, see Set Up Third-Party Tools.

>> hdlsetuptoolpath('ToolName','Xilinx Vivado','ToolPath','/opt/Xilinx/Vivado/2021.1/bin');

From the Apps tab in the Simulink Toolstrip, select HDLCoder. Open the HDL Code tab and follow these steps:

  1. Ensure the CustomOFDMBlock subsystem is pinned in the Code for option. To pin this selection, select the CustomOFDMBlock subsystem in the Simulink model and click the pin icon.

  2. Select IP Core as the Output > IP Core option.

HDL Code tab in Simulink toolstrip

Configure HDL Code Generation Settings

Click Settings in the HDL Code tab to open the Configuration Parameters dialog box.

In the basic options of the HDL Code Generation panel, ensure that Language is set to Verilog. By default, HDL Coder generates the Verilog files in the hdlsrc folder. You can select an alternative location. If you make any changes, click Apply.

HDL code generation options in configuration parameters window

Configure HDL Code Generation Target

  1. In HDL Code Generation > Target > Workflow Settings, click Browse and select the project folder in which you want to save the generated project files.

  2. In HDL Code generation > Target > Tool and Device Settings, set Target Platform to USRP X410. If you are using a different USRP radio, select the corresponding target platform and adjust the reference design parameters accordingly.

  3. In HDL Code generation > Target > Reference Design Settings, set Reference Design to the FPGA image that corresponds to your radio setup.

  • External Memory – Set to PL DDR Buffer to stream samples through the memory buffer on the radio. This setting ensures contiguous samples between MATLAB and the radio.

  • Number of Input Streams – Set to 1 because the DUT is connected to one data input stream.

  • Number of Output Streams – Set to 2 because the DUT is connected to one data output stream and one transmit antenna.

  • Number of Antennas – Set to 1 because the DUT has one radio receive channel.

  • Sample Rate (S/s) – Set to 7.68e6, which is the bandwidth of custom OFDM signal. For a list of supported sample rates, see Baseband Sample Rate in NI USRP Radios.

  • BlockID – Set to any 32-bit hexadecimal number. The default is 12345678.

  • DUT Clock Source – Set to Custom so that you can select a custom target frequency of 61.44 MHz. For more information please refer to Generate HDL Code. If you are using a USRP E320, set to Radio, since the master clock rate is 61.44 MHz.

  • Stream Port FIFO Length (Samples) – Set to Auto.

  • Register Port FIFO Length (Samples) – Set to Auto.

4. In HDL Code generation > Target > Objective Settings, set Target Frequency (MHz) to 61.44.

Target settings in configuration parameters window

5. In HDL Code Generation > Optimization > Distributed Pipelining Settings, select Distributed pipelining.

6. Click Apply.

For more information, see Configure HDL Code Generation Settings.

Configure Target Interface

In the HDL Code tab, click Target Interface to open the IP Core editor. In the Interface Mapping tab, reload the port interface mapping options by clicking Reload IP core settings and interface mapping table from model icon.

  • Assign the input registers of the DUT as write registers.

  • Assign the output registers of the DUT as read registers.

  • Assign the data, valid, ready, last, and end of burst (EOB) signals.

Interface mapping table in IP Core editor

Each data input and output port has an options menu. For the rxDataIn options, set the source connection to Radio. The input samples to the DUT are received from the radio. Set the stream buffer size to 32768, which is the default setting. The buffer size must be a power of two to ensure optimal use of the FPGA RAM resources. The buffer size is specified in terms of the number of samples, with each sample having a size of 8 bytes.

Interface options for rxDataIn

For txDataOut options, select Radio as the sink connection. The generated data will be sent to the radio for transmission through this port.

Interface options for txDataOut

For the rxStreamTDataOut options, select the PL DDR buffer as the sink connection. The DUT streams samples first to the onboard radio memory buffer, then to MATLAB for post-processing.

Interface options for rxStreamDataOut

Click Validate IP core settings and interface mapping icon to validate the interface mapping.

For more information, see Map Target Interfaces.

Generate and Load Bitstream

To generate a bitstream from the IP core, first open the deployment settings from the Build Bitstream dropdown and ensure that the Run build process externally option is selected. This setting is the default and it ensures that the bitstream build executes in an external shell, which allows you to continue using MATLAB while building the FPGA image.

Build bitstream dropdown in HDL code tab of Simulink toolstrip

Build bitstream options in deployment settings

Click Build Bitstream to create a Vivado IP core project and build the bitstream. After the basic project checks complete, the Diagnostic Viewer displays a Build Bitstream Successful message along with warning messages. However, you must wait until the external shell displays a successful build message before you move to the next step. Closing the external shell before this terminates the build. The bitstream for this project generates with the name x4xx.bit and is located in the build_X410_HG/build_X410_HG folder of the working directory after a successful bitstream build.

From Build Bitstream, open the deployment settings. In the Program Target Device settings, set the IP address. The default is 192.168.10.2. If you changed the IP address from the default when you set up your hardware using the Radio Setup wizard, set the IP address accordingly. To load the bitstream onto the device, click Program Target Device.

Program target device options in deployment settings

Alternatively, if you want to load the bitstream outside of this workflow, use the programFPGA function in the generated host interface script.

For more information, see Generate Bitstream and Program FPGA.

Generate and Modify Host Interface Setup Script

In the HDL Code tab, select Host Interface Script to generate MATLAB scripts that enable you to connect to and run your deployed design on your radio. These scripts are specific to the target interface mapping of your IP core. For more information, see Run and Verify Hardware Implementation.

The gs_wtCustomOFDMscriptSL_setup.m file configures the fpga object with the hardware interfaces and ports from your DUT algorithm. It also contains DUT port objects that have the port name, direction, data type, and interface mapping information. It maps these DUT ports to the corresponding interfaces. Before you run the host interface script, you must first modify the configuration of the DataOut port.

Increase the value of RX_STREAM0_FrameSize to 3e5.

%% Add RFNoC Stream Interface
RX_STREAM0_FrameSize = 3e5;

Verify Design

After you have generated the bitstream, the following live script enables you to connect to, configure, and control your radio device with the custom OFDM block deployed on the FPGA.

Set Up Radio

Call the radioConfigurations function. The function returns all available radio setup configurations that you saved using the Radio Setup wizard.

savedRadioConfigurations = radioConfigurations;

To update the dropdown menu with your saved radio setup configuration names, click Update. Then select the radio to use with this example.

savedRadioConfigurationNames = [string({savedRadioConfigurations.Name})];
radio = savedRadioConfigurationNames(1); 

Create a usrp System object™. This System object enables you to configure the radio front end properties, stream samples from the radio front end to your DUT, and transmit and receive IQ data to and from the host. Create a structure, deviceConfig, to store your radio configuration name, usrp object, and fpga object. You pass this structure into a helper function to verify the design.

deviceConfig.hDevice = usrp(radio);

If you have not yet programmed your device with the bitstream, select the program bitstream option. Update the code with your bitstream and hand-off information files. You can find the file names in the host interface script, gs_wtCustomOFDMSL_interface. If your radio device is a USRP X310, you require only a bitstream file to program the FPGA.

programBitstream = false;
if(programBitstream)
     programFPGA(deviceConfig.hDevice,...
        "x410_hg.bit",...  % replace with your .bit file
        "x410_hg.dts");  % replace with your .dts file
end

Use the describeFPGA function to configure the DUT interfaces according to the hand-off information file.

describeFPGA(deviceConfig.hDevice,"wtCustomOFDMSL_wthandoffinfo.mat");

Set up the antennas, transmit and receive gain, center frequency, and sample rate. The design requires an input sample rate of 7.68 MS/s.

deviceConfig.hDevice.SampleRate = 7.68e6;
deviceConfig.hDevice.ReceiveCenterFrequency = 2.45e9;
deviceConfig.hDevice.TransmitRadioGain = 25;
deviceConfig.hDevice.ReceiveRadioGain = 25;

Select transmit and receive antennas from the dropdown menus.

captureAntennaSelection = hCaptureAntennas(radio);
deviceConfig.hDevice.DUTInputAntennas = captureAntennaSelection(2);
transmitAntennaSelection = hTransmitAntennas(radio);
deviceConfig.hDevice.DUTOutputAntennas = transmitAntennaSelection(2); 

Create an fpga object to access the DUT on the FPGA of your radio device. Use the setup function script gs_wtCustomOFDMSL_setup to configure the fpga object with the hardware interfaces and ports from your DUT algorithm.

deviceConfig.hFPGA = fpga(deviceConfig.hDevice);
gs_wtCustomOFDMSL_setup(deviceConfig.hFPGA);

Deploy Transmitter and Receiver

To verify both the transmitter and receiver running in real time on the FPGA, set the verification mode to Tx and Rx.

verificationMode = "Tx and Rx" ;

In this configuration, the DUT sends OFDM data to the radio for transmission, the radio receives the data and sends it back to the DUT, where the algorithm demodulates the data and sends it back to the host for post-processing.

Block diagram of Tx and Rx verification mode

Set the loopback mode to FPGA. This means that the data is not transmitted and receiver over the air from the radio antennas, avoiding noise from the analog to digital converter (ADC) and any other environmental noise.

deviceConfig.LoopbackMode = "FPGA";

Use the hApplyVerifyMode helper function to configure the control registers for this verification setup based on the verification mode you select in the verificationMode variable.

deviceConfig = hApplyVerifyMode(verificationMode,deviceConfig, radio);
 

Run Verification and Plot Constellation Diagram

Use the hVerifyCustomOFDM helper function to verify the results from hardware. This function does the following:

  1. Configures the transmitter and receiver connections based on the selected verification mode.

  2. Runs the algorithm on your radio.

  3. Sends and receives signals to the radio or algorithm, or both, depending on the verification mode.

  4. Displays the number of bits received and the number of error bits.

  5. Returns the constellation data to the workspace.

data = hVerifyCustomOFDM(verificationMode,deviceConfig);
Number of bits received: 8849870
Number of error bits: 0

Plot the constellation diagram.

constDiagram = comm.ConstellationDiagram(ShowReferenceConstellation=false,EnableMeasurements=true);
constDiagram(data);

Alternate Verification Modes

Four verification modes enable you to verify different configurations of the hardware implementation.

  • Deploy Transmitter and Post Process In MATLAB – Use this setup to verify the transmitter implementation.

  • Transmit Repeat and Deploy Receiver – Use this setup to verify the receiver implementation.

  • Deploy Transmitter and Receiver – Use this setup to verify the transmitter and receiver implementation in the presence of real-world impairments. This example uses this verification mode.

  • Deploy Transmitter and Receiver in Internal Loopback – Use this setup to verify the transmitter and receiver design only on hardware.

To run these verification, use the hVerifyCustomOFDM helper function with the verification mode as the first input argument.

Deploy Transmitter and Post-Process in MATLAB

To verify the transmitter alone running in real-time on the FPGA, set the verification mode to Tx Only. In this configuration, the transmitted data is read directly from the receiver to MATLAB for post-processing.

This verification mode is not supported on USRP E320 radios.

Block diagram of Tx only verification mode

Transmit Repeat and Deploy Receiver

To verify the receiver alone running in real-time on the FPGA, set the verification mode to Rx Only. In this configuration, the host transmits data continuously and the radio receives data to the DUT, where the algorithm demodulates the OFDM data and sends it to MATLAB for post-processing.

This verification mode is not supported on USRP E320 radios.

Block diagram of Rx only verification mode

Deploy Transmitter and Receiver with Internal Loopback

In this mode, set the verificationMode value to Internal Loopback. In this configuration, the DUT transmitter output is connected directly back to the receiver input, where the algorithm demodulates the OFDM data and sends it to MATLAB for post-processing. The radio is not used.

Block diagram of internal loopback verification mode

See Also

Topics