Contenido principal

Introduction to Streaming Interfaces on NI USRP Radio

Since R2024a

This example shows how to deploy a multi-port packet-based algorithm on the FPGA of an NI™ USRP™ radio.

Workflow

In this example, you follow a step-by-step guide to generate a custom FPGA image from a Simulink® model and deploy it on an NI USRP radio by using a generated MATLAB® host interface script.

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.

Design Overview

The example uses a multi-port packet based algorithm that transmits a tone, receives data from the radio, performs a fast Fourier transform (FFT), and sends the output data to the host through the onboard PL DDR buffer. On the host, you control the tone transmission using the design under test (DUT), read the FFT data from the DUT, and visualize the data.

Requirements

To target NI USRP radio devices with Wireless Testbench™, you must install and configure third-party tools and additional support packages.

For details about which NI USRP radios you can target, see Supported Radio Devices.

Note

Generating a bitstream with this workflow is supported only on a Linux® operating system (OS). For details about host system requirements, see System Requirements.

For details about how to install and configure additional support packages and third-party tools, see Installation for Targeting NI USRP Radios.

Note

You cannot use this example with a USRP X310 radio with TwinRX daughterboards. The design in this example requires a transmit path.

Set Up Environment and Radio

Set up a working directory for running the example by using the openExample function in MATLAB. This function downloads the example files into a subfolder of the Examples folder in the currently running release and then opens the example. If a copy of the example exists, openExample opens the existing version of the example.

openExample('wt/IntroductionToStreamingInterfacesOnNIUSRPRadioExample')

The working directory contains all the files you need to use this example, including helper functions and files. The files you interact with are:

  • wtMultiPortPacketBasedUSRPTargetingExampleSL.slx — The Simulink hardware generation model. This model includes the DUT subsystem, which implements a multi-port packet based algorithm, and additional subsystems than enable you to simulate the DUT behavior.

  • IntroductionToStreamingInterfacesOnNIUSRPRadioExample.m — The MATLAB script that you can use to simulate the behavior of the Simulink model before you generate HDL code.

  • VerifyMultiPortPacketBasedAlgorithmUsingMATLABExample.m — The live script that you use in MATLAB to verify the algorithm running on your radio.

To program the FPGA on your radio with the bitstream that you generate in this example, and to verify the algorithm running on your radio, use the Radio Setup wizard to connect and set up your radio.

Simulink Hardware Generation Model

The Simulink model in this example is a hardware generation model of an SDR algorithm. Using the HDL Code tab in the Simulink Toolstrip or the HDL Workflow Advisor, you can generate a custom HDL IP core from the model, then generate a bitstream and load it onto the FPGA on your radio. You can then generate a host interface script that provides the MATLAB code you need to interact with the hardware.

Open Model

Open the Simulink model.

modelname = 'wtMultiPortPacketBasedUSRPTargetingExampleSL';
open_system(modelname);

The top-level structure of the model includes multiple subsystems. The TxRx subsystem is the design under test (DUT) that is used for HDL code generation. The other subsystems enable you to simulate the behavior of the DUT when it is deployed on the radio device:

  • The Radio RF Source and Radio RF Sink subsystems simulate the behavior of the radio connections to the DUT.

  • The TxRx subsystem generates a complex sine wave for transmission and performs an FFT on the received samples. This subsystem is the DUT.

  • The PL DDR Buffer subsystem and pass through registers simulate the behavior of the PL DDR buffer connection on the radio, which receives output data from the DUT.

  • The Process Frame, BinNumber2Frequency, and Calculate NCO Increment subsystems simulate the generated MATLAB host interface script.

Simulate Model

To confirm the behavior of the model, simulate the model.

By default, the model simulates with a sample rate of 61.44 MS/s and a master clock rate (MCR) of 245.76 MHz, which is a valid MCR for a USRP N320 radio. If you have a different radio, update the sample rate and MCR with valid values for your radio. To determine supported values, see Determine Radio Device Capabilities.

  1. From the Modelling tab in the Simulink toolstrip, select Model Explorer. Then, open Model Workspace under the model hierarchy.

  2. Set the MCR to a supported MCR for your radio.

  3. Set the sampleRate to a supported resampled rate of the MCR value.

  4. Set the resamplingFactor to MCR divided by sampleRate.

Set the toneFreq to an arbitrary complex tone frequency to transmit from the DUT for the simulation. The default value is -7.68 MHz, which is an exact division of sampleRate and in quadrature phase. You can also change the simulated PL DDR buffer allocation by setting the PLDDRBufferAllocation variable and the simulated burst length by setting the BurstLength variable.

Run the model. The output of the simulation is a plot of the bin magnitudes from the FFT algorithm which is run on the data samples transmitted from the DUT.

sim(modelname);

Investigate Model Behavior

To review the behavior of the DUT streaming ports, open the Logic Analyzer. From the Apps tab in the Simulink Toolstrip, select Logic Analyzer.

The transitions across the full simulation time are shown in the diagram.

  • When the radio requests samples by setting Tx_Ready_In high, the DUT provides data for transmission on Tx_Data_Out and sets Tx_Valid_Out high.

  • When there is space in the PL DDR buffer, DUT_Ready_In is high.

  • When the internal FFT block is ready to receive data from the radio, the DUT sets RF_Ready_Out.

  • After some time, the radio sends samples to the DUT on RF_Data_In.

  • When the DUT receives a full frame of samples, it sets RF_Ready_Out low.

  • The frame is processed by the DUT and then output on DUT_Data_Out.

  • When the DUT is ready for new samples, it sets RF_Ready_Out high.

  • This sequence is repeated until the PL DDR buffer is full.

Open the TxRx subsystem.

open_system([modelname '/TxRx']);

This contains the Generate TX Tone and Compute RX Spectrum Magnitudes subsystems.

Open the Generate TX Tone subsystem.

open_system([modelname '/TxRx/Generate TX Tone'])

The Generate TX Tone subsystem generates a complex sine wave using the NCO block. The subsystem is controlled with these signals.

  • The TX_Increment_In port is a write register that controls the frequency of the generated sine wave.

  • The Tx_Ready_In port controls the rate of the valid input of the NCO. The radio uses the ready signal to request data samples from the DUT at the specified sample rate.

  • The TransmitTone port is a write register that enables the transmission.

  • The Tx_Data_Out port sends data to the radio for transmission. Each sample is a complex 16-bit integer from the NCO.

  • The Tx_Valid_Out port controls the data rate of the tx data. The data rate must match data rate of the Tx_Ready_In port.

  • The Tx_last_Out port indicates the end of a streaming packet. It is asserted every 256 valid samples.

  • The TX_EOB_Out port indicates the end of a contiguous burst of samples. It is asserted when the TransmitTone signal is false.

Use the logic analyzer to examine the signals.

The timing diagram demonstrates that the Generate TX Tone subsystem responds to the ready signal from the radio. The radio requests samples in packets of 256 samples. The sample rate used in the diagram is 61.44 Ms/s and the MCR, which is the rate at which the DUT is clocked, is 245.76 MHz. The sample rate is one quarter of the MCR, so the ready signal is high for one sample in every four.

The timing diagram also demonstrates how the Generate TX Tone subsystem asserts the last output at the end of each packet of 256 samples. Failing to assert the last output signal will delay the data packet. This can lead to an overflows in the internal buffers.

Open the Compute RX Spectrum Magnitudes subsystem.

open_system([modelname '/TxRx/Compute RX Spectrum Magnitudes'])

The Compute RX Spectrum Magnitudes subsystem performs an FFT on the samples received from the radio, and then plots the output in MATLAB. The subsystem is controlled with these signals.

  • The Data_In port receives samples from the radio as complex 16-bit integers.

  • The Valid_In port signals the rate of the input from the radio, which is determined by the sample rate.

  • The EOB_In port indicates the final packet in a stream of contiguous samples. It is asserted for the duration of the final packet.

  • The Last_In port is true for the last sample of each packet.

  • The EOB_In and Last_in ports are input to an AND gate. The output of this is true on the last sample of the last packet, indicating the end of the stream of contiguous samples. If the FFT is still ready for new samples after this goes high, this indicates an incomplete frame, which would result in a discontiguity. Therefore, the output signal from the AND gate is used to reset the Simulink block which erases the incomplete frame.

  • The ProcessFrames port is a write register used to enable or disable the DUT. When de-asserted, it resets the FFT block and asserts the EOB_Out port to prevent stale samples from being received when the system is re-enabled.

  • The Ready_Out port when false exerts back pressure on the streaming connection with the radio. If samples are received from the radio when Ready_out is low they are buffered in the endpoint buffer. If the buffer is full there is an overflow and the radio stops streaming new samples. the port is asserted true when the ProcessFrames register is true, the upstream PL DDR buffer is ready as indicated by the Ready_In port, and the FFT block is ready for new samples.

  • The MaxBin port is a read register that gives the index of the largest magnitude bin in the most recent FFT packet, which is calculated in the Hold Largest Magnitude Bin Index subsystem.

  • The Data_Out port outputs data from the DUT as unsigned 32-bit integers. Each sample contains a signed 17 bit integer bin magnitude computed by the Complex to Magnitude-Angle Simulink block and uses the highest bit to signal the start of each FFT packet.

  • The Valid_Out port indicates the rate of the output data and is high when the output of the Complex to Magnitude-Angle Simulink block is valid and the ProcessFrames input port is asserted.

  • The EOB_Out port indicates the end of a packet from the FFT and is asserted by the FFT block end signal or when ProcessFrames input port is de-asserted.

  • The Last_Out port indicates the end of streaming packet. It is high for one sample every 256 valid samples and at the end of the FFT packet.

Use the logic analyzer to examine a full burst of data from the radio.

The timing diagram shows a full burst of samples. Each packet has 256 samples which. Since the number of samples in the burst is the same as the FFT length, the RF_Ready_Out port is deasserted after the final sample of the final packet while the frame of samples is processed.

Zoom in to see the final packet in the burst. The end of burst signal on the RF_EOB_In port is high for the duration of the last packet.

Finally, use the logic analyzer to examine the signals that make up the DUT streaming interface to the host through the PL DDR buffer. The timing diagram shows a packet of output data from the DUT. The data is buffered in the PL DDR buffer where it can be read from the host interface. The length of the packet corresponds to the FFT length. To ensure the data from the DUT is sent into the PL DDR buffer, the DUT_Last_Out signal is asserted every 256 samples and the end of burst is asserted at the end of the DUT data packet.

Configure IP Core

When you are satisfied with the simulated behavior of the model, you can proceed to integrate your design into a custom IP core by generating HDL code and mapping the model inputs and outputs to the hardware interfaces.

First, use the hdlsetuptoolpath (HDL Coder) function to set up the 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 HDL Coder. Then open the HDL Code tab.

Configure Output Options

In the HDL Code tab, configure the output options:

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

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

HDL Code tab in the Simulink Toolstrip

Configure HDL Code Generation Settings

Open the Configuration Parameters dialog box by clicking Settings in the HDL Code tab.

In the HDL Code Generation pane, 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 panel in the Configuration Parameters window

In the Target pane, configure these settings:

  • Under Workflow Settings, select the IP Core Generation workflow. To set Project Folder, click Browse and select a target location for saving the generated project files. If you do not specify a project folder, the software saves the generated project files in the working directory.

  • Under Tool and Device Settings, select your radio from the Target Platform list. This example uses a USRP N320 radio. If you are using a different radio, adjust the reference design parameters accordingly.

  • Under Reference Design Settings, set Reference Design to the desired reference FPGA image for your design, based on how you set up your hardware. For a USRP N320 radio, you have one option: HG: 1GigE, 10GigE.

    Set the reference design parameters to these values:

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

    • Number of Output Streams — Set to 2 because the DUT is connected to one data output stream to the radio and one data output stream to the host.

    • Sample Rate (S/s) — Set to any valid value for your radio, which is a supported MCR divided by a supported interpolation or decimation rate. This example uses a USRP N320 radio and a sample rate of 61.44 MHz, which corresponds to an MCR of 245.76 MHz and an interpolation or decimation factor of 4. For more information about supported values on your radio, see Determine Radio Device Capabilities.

    • Reference Design Optimization — Set to None. This example uses a simple design that does not require optimizations to meet timing constraints.

    • DUT Clock Source — Set to Radio. When you select this setting, the DUT is clocked at the MCR used by the radio to achieve the specified sample rate. Alternatively, you can set this setting to Custom to set the DUT clock frequency to a user-defined value in the Target Frequency parameter under Objectives Settings.

    • Stream Port FIFO Length (Samples) — Set to Auto. This setting automatically calculates the buffer length for each DUT input and output data streaming port.

    • Register Port FIFO Length (Samples) — Set to Auto. This setting automatically calculates the buffer length for each DUT register port.

    Under Objectives Settings, since the DUT Clock Source is set to Radio, the Target Frequency is fixed at the highest supported MCR of the radio.

HDL Code Generation > Target panel in the Configuration Parameters window

To apply these settings and close the Configuration Parameters window, click OK.

For more information, see Configure HDL Code Generation Settings.

Map Target Interfaces

In the HDL Code tab, click Target Interface to open the Interface Mapping table in the IP Core editor. To populate the table with your user logic, click the Reload IP core settings button: Reload IP core settings and interface mapping table from model icon.

IP Core Interface Mapping table

The Source, Port Type, and Data Type columns are populated based on the Simulink model. The Interface column automatically populates based on the port names in your model:

  • The input register ports, ProcessFrames, TransmitTone, and TX_Increment_In, map to Write Register interfaces.

  • The output register port MaxBin maps to a Read Register interface.

  • The RF_Data_In, RF_Valid_In, RF_Last_In, RF_EOB_In, and RF_Ready_Out ports map to a Simplified AXI4-stream Input0 interface.

  • The DUT_Data_Out, DUT_Valid_Out, DUT_Last_Out, DUT_EOB_Out, and DUT_Ready_In ports map to a Simplified AXI4-stream Output0 interface.

  • The TX_Data, TX_Valid, TX_Last, TX_EOB, and TX_Ready ports map to a Simplified AXI4-stream Output1 interface.

The Interface Mapping column is populated automatically based on the port names in the model.

To set the interface options for the streaming interfaces, open the Set Interface Options window by clicking Options in the far right of the table.

For the RF_Data_In port options, select a receive antenna as the source connection. The DUT receives input samples from this antenna on 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.

RF_Data_In interface options

For the DUT_Data_Out port 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.

DUT_Data_Out interface options

For the Tx_Data port options, select a transmit antenna as the sink connection.

Tx_Data interface options

When you have populated the table, validate the interface mapping by clicking the Validate IP core settings button: Validate IP core settings and interface mapping icon.

For more information, see Map Target Interfaces.

Generate and Load Bitstream

To generate a bitstream from the configured IP core, first open the deployment settings from the Build Bitstream button.

Deployment Settings button in Build Bitstream drop-down menu

  • 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.

  • In the Program Target Device settings, set the IP address. The default is 192.168.10.2. If your radio has a different IP address, update this field with the correct value.

Deployment Settings window

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 bitstream build before moving to the next step. Closing the external shell before this time terminates the build.

The bitstream for this project generates with the name n3xx.bit and is located in the build_N320_HG/build_N320_HG folder of the working directory after a successful bitstream build. If you are using a different radio, the name and location reflects your radio.

To load the bitstream onto the device now, click Program Target Device from the Build Bitstream button. Alternatively, you can load the bitstream later by using the programFPGA function in the generated host interface script.

For more information, see Generate Bitstream and Program FPGA.

Generate Host Interface Scripts

To generate MATLAB scripts that enable you to connect to and run your deployed design on your radio, in the HDL Code tab, click Host Interface Script. This step generates two scripts in your working directory based on the target interface mapping that you configured for your IP core.

  • gs_wtMultiPortPacketBasedUSRPTargetingExampleSL_interface.mHost interface script that creates an fpga object for interfacing with your DUT running on the FPGA from MATLAB. The script contains code that connects to your hardware and programs the FPGA and code samples to get you started with running the algorithm on your radio. For more information, see Interface Script File.

  • gs_wtMultiPortPacketBasedUSRPTargetingExampleSL_setup.mSetup function that configures the fpga object with the hardware interfaces and ports from your DUT algorithm. The function contains DUT port objects that have the port name, direction, data type, and interface mapping information, which it maps to the corresponding interfaces. For more information, see Setup Function File.

    Note

    The number of samples, specified in the FrameSize property, is set by default to 1e5. To change this value or the value of the Timeout property, manually edit the setup function.

Edit Setup Function File

In the generated setup function file, the streaming interfaces are configured with the default frame, which is 1e5 samples. Before you run the host interface script, manually update the frame size to 2048e3 by following these steps. This value corresponds to 1000 frames of data, since a frame of data from the FFT block is 2048 samples.

  1. Open the setup function file for editing.

    edit gs_wtMultiPortPacketBasedUSRPTargetingExampleSL_setup.m

  2. Identify the call to the addRFNoCStreamInterface function for the data output port.

  3. Update the FrameSize value to 2048e3.

    RX_STREAM0_FrameSize = 2048e3;

  4. Save the updated setup function file.

Verify Multi-Port Packet Based Algorithm Using MATLAB

To verify the algorithm running on your radio, use this modified version of the host interface script to connect to the DUT running on the radio, transmit and receive a tone, then plot the output using MATLAB.

Open Live Script

You can open this live script in MATLAB from the example working directory and use it interactively. In the Files panel, navigate to your example working directory and open VerifyMultiPortPacketBasedAlgorithmUsingMATLABExample.m.

Select 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 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) ;

Evaluate the transmit and capture antennas available on your radio device. You select DUT input and output antenna connections from the available options later in the script.

transmitAntennas = hTransmitAntennas(radio);
receiveAntennas = hCaptureAntennas(radio);

Create usrp System Object

Create a usrp System object™ with the specified radio. This System object controls the radio hardware.

device = usrp(radio);

Program FPGA

If you have not yet programmed your device with the bitstream, select the load bitstream option to use the programFPGA function. Update the code with your bitstream and device tree file. You can find these files in the programFPGA call in the generated host interface script, gs_wtMultiPortPacketBasedUSRPTargetingExampleSL_interface. If your radio is a USRP X310, you need only a bitstream file to program the FPGA.

loadBitstream = false;
if(loadBitstream)
     programFPGA(device, ...
        "build_N320_HG/build-N320_HG/n3xx.bit", ...     % replace with your .bit file
        "build_N320_HG/build/usrp_n320_fpga_HG.dts");   % replace with your .dts file
end

To configure the DUT interfaces according to the hand-off information file, use the describeFPGA function. Calling this function additionally sets the default values for the SampleRate, DUTInputAntennas, and DUTOutputAntennas properties based on the selections you made in Simulink.

% Replace with your hand-off information file
describeFPGA(device, ...
    "wtMultiPortPacketBasedUSRPTargetingExampleSL_wthandoffinfo.mat");

Configure Device

Configure the usrp System object to loop back a test waveform to the input of the DUT. You can loop back the waveform over the air or directly on the FPGA.

To specify a sample rate different from the value you selected in the Simulink IP core generation workflow, uncomment this line and specify a value for the SampleRate property.

% device.SampleRate = 61.44e6;

Set the LoopbackMode property to Disabled to transmit and receive data over the air. Alternatively, you can select FPGA to loop back each transmit antenna to an associated receive antenna on the FPGA.

device.LoopbackMode = "Disabled";

Configure the transmit and receive antennas to connect to your DUT. If you are using FPGA loopback mode, the transmit antenna you choose must be an antenna pair with the DUT input antenna you have configured. For details, see the LoopbackMode property description.

device.DUTInputAntennas = receiveAntennas(2);
device.DUTOutputAntennas = transmitAntennas(1);

Specify the center frequency and gain for the selected transmit and receive antennas. To loop back over the air, ensure the transmit and receive center frequencies are equal. If FPGA loopback mode is selected, these properties have no effect.

device.ReceiveCenterFrequency = 2400000000;
device.TransmitCenterFrequency = 2400000000;
device.ReceiveRadioGain = 20;
device.TransmitRadioGain = 20;

Create and Set Up FPGA Object

To connect to the DUT on the FPGA of your radio, create an fpga object with the your usrp System object device.

dut = fpga(device);

Set up the fpga object using the generated setup function. If you have not updated this file to set the frame size to 2048e3, return to Edit Setup Function File.

gs_wtMultiPortPacketBasedUSRPTargetingExampleSL_setup(dut);

Set Up Device Object

To establish a connection with the radio hardware, call the setup function on the usrp System object.

setup(device);

Configure DUT

After connecting to the radio, configure the register ports on the DUT using the writePort and readPort functions.

First, set the ProcessFrames and TransmitTone register port to false to disable tone generation and FFT processing.

writePort(dut,"ProcessFrames",false);
writePort(dut,"TransmitTone",false);

Calculate the phase increment of the NCO that will produce the desired transmit tone frequency. The phase increment is calculated based on the NCO word length, which is 16, the sample rate, and the desired tone frequency. Then, write this value to the TX_Increment_In register port.

toneFrequency = device.SampleRate/4;
ncoWordLength = 16;
toneIncrement = floor(2^ncoWordLength*toneFrequency/device.SampleRate);
writePort(dut,"TX_Increment_In",toneIncrement);

Finally, start transmitting the tone from the DUT output antenna by setting the TransmitTone port to true.

writePort(dut,"TransmitTone",true);

Stream Received Data to DUT

To enable input of samples from the radio to the DUT, set the ProcessFrames register port to true. Asserting this port asserts the ready signal, indicating to the radio that the DUT is ready to receive samples.

writePort(dut,"ProcessFrames",true);

To start streaming RF data from the radio front end into the DUT, call the usrp System object device as a function. Request one frame of samples, which is 2048 samples.

fftLength = 2048;
device(fftLength);

Compare Tone Frequency with Computed Maximum Bin

Read the computed maximum bin value from the MaxBin register port by using the readPort function.

maxBin = readPort(dut,"MaxBin");

Convert the bin number to a frequency offset using the hbinTofreq helper function defined at the end of the example.

maxBinFreq = hBinToFreq(maxBin,device.SampleRate,fftLength);

Verify that the computed value is accurate to within the width of the bin.

if abs(toneFrequency-maxBinFreq) <= device.SampleRate/fftLength
    disp("Result within bin width."+newline+ ...
        "Transmitted tone (Hz): "+string(toneFrequency)+"."+newline+ ...
        "Maximum bin frequency read from DUT: "+string(maxBinFreq)+".")
else
    warning("Tone frequency not within one bin width of computed peak."+newline+ ...
        "Transmitted tone (Hz): "+string(toneFrequency)+"."+newline+ ...
        "Maximum bin frequency read from DUT: "+string(maxBinFreq)+".")
end
Result within bin width.
Transmitted tone (Hz): 15360000.
Maximum bin frequency read from DUT: 15345000.

Read Output Data from DUT

Read the buffered DUT output data from the DUT_Data_Out port by using the readPort function.

[data,numSamps,overflow] = readPort(dut,"DUT_Data_Out");

The length of the data returned is equal to the frame size specified in the setup function file, which you set to 1000 frames of FFT data. When you called the usrp System object, device, as a function, you requested one frame of FFT data, or 2048 samples. The returned data is padded with zeros up to the frame size in the setup function file. Remove the padded data.

data = data(1:numSamps); % Remove padded zeros

Verify DUT Output Data

Verify that no overflows are detected in the internal buffers. Since this example uses the PLL DDR buffer, you should not encounter overflows. If you opted to stream data directly to the host, overflows can occur.

if overflow
    error("Overflow detected in internal buffers.")
end

Verify that a full frame of samples is returned.

if numSamps ~= fftLength
    warning(string(numSamps)+ " samples returned from the buffer. "+ ...
    "Expected "+string(fftLength)+" samples.")
end

In the DUT model, the most significant bit in the uint32 output data is connected to the start output port of the FFT block. The first packet of data should be a 1, while the first packet of each subsequent packet should be 0. Verify that most significant bit of the first data sample is 1. If the value is 0, this indicates that the sample is not the start of the FFT packet.

fftStartBit = bitget(data(1),32);
if ~fftStartBit
    error("Most significant bit of the first sample of the FFT packet should be 1.")
end

Extract FFT Result from DUT Output Data

In the DUT model, the least significant 17 bits in the uint32 output data correspond to the signed fixed point FFT bin magnitudes. Use the bitsliceget function to extract the FFT bin magnitudes from the output data.

fftBinMagnitudes_fi = bitsliceget(fi(data,0,32,0),17,1);

Use the reinterpretcast function to convert the data to a signed 17 bit data type.

fftBinMagnitudes = reinterpretcast(fftBinMagnitudes_fi,numerictype(1,17,0));

Use the hBinToFreq helper function to get the frequency offset of each bin.

binFrequencies = hBinToFreq(1:fftLength,device.SampleRate,fftLength);

Plot Data

Use the stem function to plot the FFT bin magnitudes. Set hold on to retain the current plot when you plot additional data.

stem(binFrequencies/1e6,fftBinMagnitudes);
ylabel("Bin Magnitude")
xlabel("Baseband Frequency (MHz)")
hold on

Adjust Center Frequency and Repeat

Set the receive center frequency of the usrp System object device to a small offset from the transmit center frequency.

device.ReceiveCenterFrequency = device.TransmitCenterFrequency + toneFrequency/2;

Request an FFT frame of samples from the radio to the DUT.

device(fftLength);

Read the data from the DUT and discard padded samples.

[data,numSamps,overflow] = readPort(dut,"DUT_Data_Out");
data = data(1:numSamps);

Plot the FFT bin magnitudes.

fftBinMagnitudes = bitsliceget(fi(data,0,32,0),17,1);
fftBinMagnitudes = reinterpretcast(fftBinMagnitudes,numerictype(1,17,0));
stem(binFrequencies/1e6,fftBinMagnitudes);
hold off
legend("Offset: 0 MHz","Offset: "+num2str(toneFrequency/2e6)+" MHz")

Reset the receive center frequency equal to the transmit center frequency.

device.ReceiveCenterFrequency = device.TransmitCenterFrequency;

Read Burst of FFT Packets and Change Tone Frequency

Calculate DUT Continuous Throughput

The number of packets that can be streamed continuously depends on two factors:

  • The throughput of the FFT block.

  • The stream buffer size of the streaming interface.

The Burst Radix 2 architecture of the FFT block has a latency of 12485 clock cycles for a FFT Length of 2048.

fftLength = 2048;
fftLatency = 12485;

Assuming no overlap in the computation, a theoretical sustainable sample throughput can be calculated as clockRate*fftLength/fftLatency.

clockRate = device.DUTClockFrequency
clockRate = 
245760000
fftThroughput = clockRate*fftLength/fftLatency;
disp("Continuous throughput of the DUT is "+string(fftThroughput/1e6)+" MS/s.")
Continuous throughput of the DUT is 40.3137 MS/s.

To achieve greater throughput, return to the Configure HDL Code Generation Settings step and increase the DUT clock frequency by setting the DUT Clock Source parameter to Custom and specifying the Target Frequency (MHz) parameter to the desired clock rate.

Update Sample Rate

Stop the transmission from the DUT and release the usrp System object. Update the sample rate to a value less than the calculated throughput. For a USRP N320 radio running at an MCR of 245.76 MHz, the calculated throughput is approximately 40 MS/s, so a sample rate of 30.72 MHz is a suitable value.

writePort(dut,"TransmitTone",false);
release(device)
device.SampleRate = 30720000;
if device.SampleRate > fftThroughput
    error("Sample rate value must be less than the calculated throughput.")
end

Rerun the setup function to return the usrp System object to it's default state.

setup(device)

Change Tone Frequency and Stream Data from DUT

Set the TX_Increment_In register port according to the tone frequency and restart the transmission by asserting the TransmitTone register port. Then, assert the ProcessFrames register port to enable the FFT block.

writePort(dut,"TX_Increment_In",toneIncrement);
writePort(dut,"TransmitTone",true);
writePort(dut,"ProcessFrames",true);

Stream 1000 FFT frames of samples from the radio front end to the PL DDR Buffer. After requesting the data, update the "TX_Increment_In" register to flip the complexity of the complex generated tone. Retrieve the packets from the PL DDR Buffer and plot.

numPackets = 1000;
device(fftLength*numPackets);

Set the TX_Increment_In register port according to flip the complexity of the generated tone.

writePort(dut,"TX_Increment_In",-toneIncrement);

Retrieve the samples from the PL DDR buffer and remove any padded zeros from the data. Then, Arrange each FFT packet to create a matrix.

[data_burst,numSamps_burst] = readPort(dut, "DUT_Data_Out");
data_burst = data_burst(1:numSamps_burst);
data_burst = reshape(data_burst,fftLength,[]);

Verify that the first sample of each packet is the first FFT packet.

fftStartBits = bitget(data_burst(1,:),32);
if ~all(fftStartBits)
    error("Most significant bit of the first sample of each FFT packet should be 1.")
end

Plot Data

Plot the FFT bin magnitudes as a surface plot. The plot shows the first 209 FFT packets at a baseband frequency of 7.68 MHz and the remaining FFT packets at a baseband frequency of -7.68 MHz.

fftBinMagnitudes = bitsliceget(fi(data_burst,0,32,0),17,1);
fftBinMagnitudes = reinterpretcast(fftBinMagnitudes,numerictype(1,17,0));
figure
binFrequencies_burst = hBinToFreq(1:fftLength, device.SampleRate, fftLength);
surf(1:numPackets,binFrequencies_burst/1e6,fftBinMagnitudes)
shading(gca,'interp')
xlabel("DUT FFT Algorithm Packet")
ylabel("Baseband Frequency (MHz)")
zlabel("Bin Magnitude")

Release Hardware Resources

Stop the transmission from the DUT and release the device System object and the fpga object to disconnect from the hardware.

writePort(dut,"TransmitTone",false);
release(dut);
release(device);

Convert FFT Bin Number to Frequency

function freqs = hBinToFreq(binNums, sampleRate, fftLength)
% Returns the frequency in Hz corresponding to the middle of the specified bin(s)
binNums = double(binNums);
binWidth = sampleRate/fftLength;
freqs = zeros(1,length(binNums));
for i = 1:length(binNums)
    binNum = binNums(i);
    if binNum > 1024
        freqs(i) = (binNum-fftLength)*sampleRate/fftLength-(binWidth/2);
    else
        freqs(i) = binNum*sampleRate/fftLength-(binWidth/2);
    end
end
end

See Also

Topics