EttusResearch / uhd

The USRP™ Hardware Driver Repository
http://uhd.ettus.com
Other
976 stars 655 forks source link

Python and RFNOC: Atomic item size with FFT blocks #634

Open LorenzoMinutolo opened 2 years ago

LorenzoMinutolo commented 2 years ago

Issue Description

After adding the FFT block to a RFNOC design and successfully compiling the firmware, I cannot commit the graph due to an error generated on graph.commit() in my Python script. Specifically the error is: RuntimeError: ValueError: samples per package must not be smaller than atomic item size Which should stem from a mismatch in the samples per packet setup. I do take care of this aspect by setting the spp at the beginning and configuring each block with the same value.

Setup Details

[INFO] [UHD] linux; GNU C++ version 11.2.0; Boost_107400; UHD_4.2.0.1-0-g321295fb, Python 3.10 The USRP I am using is a x300 with a WBX daughterboard connected via a single 10Gbe. The NIC is an Intel X710.

Expected Behavior

The script should commit the graph without issues. Or at least tell me which block is causing the problem.

Actual Behaviour

RuntimeError: ValueError: samples per package must not be smaller than atomic item size

Steps to reproduce the problem

Compile a firmware with the FFT block. The usrp_probe returns:

|   |   * 0/SEP#6:0==>0/FFT#0:0
|   |   * 0/FFT#0:0==>0/SEP#6:0
|   |   * 0/SEP#0:0==>0/DUC#0:0
|   |   * 0/DUC#0:0==>0/Radio#0:0
|   |   * 0/Radio#0:0==>0/DDC#0:0
|   |   * 0/DDC#0:0==>0/SEP#0:0
|   |   * 0/Radio#0:1==>0/DDC#0:1
|   |   * 0/DDC#0:1==>0/SEP#1:0
|   |   * 0/SEP#2:0==>0/DUC#1:0
|   |   * 0/DUC#1:0==>0/Radio#1:0
|   |   * 0/Radio#1:0==>0/DDC#1:0
|   |   * 0/DDC#1:0==>0/SEP#2:0
|   |   * 0/Radio#1:1==>0/DDC#1:1
|   |   * 0/DDC#1:1==>0/SEP#3:0
|   |   * 0/SEP#4:0==>0/Replay#0:0
|   |   * 0/Replay#0:0==>0/SEP#4:0
|   |   * 0/SEP#5:0==>0/Replay#0:1
|   |   * 0/Replay#0:1==>0/SEP#5:0

Run the following code:

import numpy as np
import uhd
args = "addr=<address of the USRP>"
graph = uhd.rfnoc.RfnocGraph(args)

radio_ID_A = uhd.rfnoc.BlockID(0, "Radio", 0);
radio_block_A = graph.get_block(radio_ID_A);
radio_ctrl_A = uhd.rfnoc.RadioControl(radio_block_A)

set_freq = int(300e6)
spp = 512
radio_ctrl_A.set_tx_frequency(set_freq, 0)
tx_freq = radio_ctrl_A.get_tx_frequency(0)
radio_ctrl_A.set_rx_frequency(set_freq, 0)
rx_freq = radio_ctrl_A.get_rx_frequency(0)

radio_ctrl_A.set_properties(f'spp={spp}', 0)
radio_ctrl_A.set_rx_antenna('RX2',0)
radio_ctrl_A.set_rate(int(200e6))

DDC_ID = graph.find_blocks("DDC")[0]
DDC_block = graph.get_block(DDC_ID)
DDC_control = uhd.rfnoc.DdcBlockControl(DDC_block)
DDC_control.set_input_rate(int(200e6), 0)
DDC_control.set_output_rate(int(5e6), 0)

FFT_ID = graph.find_blocks("FFT")[0]
FFT_block = graph.get_block(FFT_ID)
FFT_control = uhd.rfnoc.FftBlockControl(FFT_block)
FFT_control.set_magnitude(uhd.libpyuhd.rfnoc.fft_magnitude.COMPLEX)
FFT_control.set_direction(uhd.libpyuhd.rfnoc.fft_direction.FORWARD)
FFT_control.set_shift_config(uhd.libpyuhd.rfnoc.fft_shift.NORMAL)
FFT_control.set_length(int(spp))

stream_args = uhd.usrp.StreamArgs('fc32','sc16')
stream_args.args = uhd.types.DeviceAddr(f'spp={spp}')
rx_stream = graph.create_rx_streamer(1, stream_args)

graph.connect(
    radio_ID_A,0,
    DDC_ID,0,
    False
)
graph.connect(
    DDC_ID,0,
    FFT_ID,0,
    False
)
graph.connect(
    FFT_ID,0,
    rx_stream,0
)
graph.commit()
LorenzoMinutolo commented 2 years ago

Interestingly this version of the code commits the graph successfully.

args = "addr=<address of the USRP>"
spp = 128
graph = uhd.rfnoc.RfnocGraph(args)
radio_ID_A = uhd.rfnoc.BlockID(0, "Radio", 0)
FFT_ID = graph.find_blocks("FFT")[0]

DDC_ID = graph.find_blocks("DDC")[0]
DDC_block = graph.get_block(DDC_ID)
DDC_control = uhd.rfnoc.DdcBlockControl(DDC_block)
DDC_control.set_input_rate(int(200e6), 0)
DDC_control.set_output_rate(int(5e6), 0)

stream_args = uhd.usrp.StreamArgs('fc32','sc16')
rx_stream = graph.create_rx_streamer(1, stream_args)

uhd.rfnoc.connect_through_blocks(graph,radio_ID_A,0,DDC_ID,0)

FFT_block = graph.get_block(FFT_ID)
FFT_control = uhd.rfnoc.FftBlockControl(FFT_block)
FFT_control.set_magnitude(uhd.libpyuhd.rfnoc.fft_magnitude.COMPLEX)
FFT_control.set_direction(uhd.libpyuhd.rfnoc.fft_direction.FORWARD)
FFT_control.set_shift_config(uhd.libpyuhd.rfnoc.fft_shift.NORMAL)
FFT_control.set_length(int(spp))

radio_block_A = graph.get_block(radio_ID_A)
radio_ctrl_A = uhd.rfnoc.RadioControl(radio_block_A)
radio_ctrl_A.set_properties(f'spp={spp}', 0)
radio_ctrl_A.set_rx_antenna('RX2',0)

graph.connect(DDC_ID, 0, FFT_ID, 0,False)
graph.connect(FFT_ID, 0, rx_stream, 0,False)

graph.commit()

The main differences are: 1) the use of the function connect_through_blocks instead of just connect for connecting the radio block. What is the difference between these functions? 2) I am not setting the spp for the streamer. Whenever I do that, the original error is thrown.

Also: does the order in which I declare/connect blocks matter?

I just noticed that changing the spp above 256 makes the script throw the original error. Does this rings a bell to anyone?

Thanks, Lorenzo

mbr0wn commented 1 year ago

Hey @LorenzoMinutolo, we are aware of this issue. In fact, we had already fixed it in f163af41a47ab4c702ffbdb10352cf875d604d74, but we had to revert it in 036f7a32a67ffdc87484675da3cb7bc519f8f7c8 because it broke some streaming tests. It's on our backlog to put the fix in again.

cstandy commented 2 weeks ago

Hi @mbr0wn, we are facing the same issue with AIS and SPP. I am using the C++-based UHD RFNoC library on UHD 4.5. This issue forces my SPP to be 4x my FFT size since each sample is 4 bytes in sc16. I use USRP X310, MTU 9000, and a 10G link. The largest FFT size I can do is also 256, where the desired values are 1024/2048/4096. Do you have any updates on this issue? Thank you.