USRP Hardware Driver and Device Manual  Version: 4.11.0.0-0-g0d7ed3b1
UHD and USRP Manual
uhd::rfnoc::ddc_block_control Class Referenceabstract

#include <uhd/rfnoc/ddc_block_control.hpp>

Inheritance diagram for uhd::rfnoc::ddc_block_control:
uhd::rfnoc::noc_block_base uhd::rfnoc::node_t uhd::rfnoc::register_iface_holder

Detailed Description

DDC Block Control Class

Overview

The DDC is a multi-channel digital downconverter with a built-in DDS frequency shifter. It is commonly placed directly after a radio block to select a channel from a wider-band input and reduce the sampling rate before the data is sent to another RFNoC block or to the host.

The block processes signed complex 16-bit samples (sc16). Each channel has an independent frequency shift and decimation setting. The number of channels and the maximum supported decimation are configured in the FPGA at synthesis time. The block controller reads these maximum capabilities, stored in capability registers, during initialization.

Features

  • Per-channel frequency shifting with optional timed commands.
  • Integer decimation using a CIC filter followed by a configurable cascade of half-band filters.
  • IQ scaling to compensate for the gain of the decimation filters.
  • Independent rate and frequency configuration for every channel.

Theory of Operation

The total decimation is the product of the CIC decimation and the half-band decimation stages. With NUM_HB half-band stages and a CIC limit of CIC_MAX_DECIM, the maximum supported decimation is \(\mathrm{CIC\_MAX\_DECIM} \times 2^{\mathrm{NUM\_HB}}\).

FPGA Compile-Time Configuration

The DDC FPGA block is configured through the following parameters:

  • NUM_PORTS: Number of independent DDC channels.
  • NUM_HB: Number of half-band filter stages in each channel.
  • CIC_MAX_DECIM: Maximum decimation through CIC filter stage in each channel.
  • NIPC: Number of samples processed per clock cycle. NIPC == 1 selects the legacy single-sample implementation. Values greater than one select the multisample implementation, which processes samples in parallel for wideband images.

The in-tree DDC YAML descriptor derives NIPC from the configured RF bandwidth. Each parallel processing chain is sized for approximately 200 MHz of RF bandwidth, with the resulting value rounded up to a power of two. Customized FPGA images may choose a different value based on their clock rates and resource budget.

Runtime Configuration

This block exposes two user-configurable configuration parameters per channel:

  • freq: Frequency shift in Hz. The set_freq() convenience method should be preferred over setting the property directly because it also handles a command time.
  • decim: Integer decimation value.

The decimation value can be changed at runtime, but a change is applied only between bursts. Once a decimation value has been applied, it remains in effect for the entire burst.

The legacy single-sample DDC and the multisample (wideband) DDC react differently to timed and untimed frequency-shift commands:

  • Legacy single-sample implementation:
    • Untimed commands: Configure the frequency shift for the next IQ sample processed by the DDC and all subsequent samples until the next frequency change. If the DDC has not processed data since the command was issued, a subsequent untimed command overwrites the earlier one.
    • Timed commands: Follow the standard timed-command mechanism. The command is held until the IQ sample corresponding to its timestamp is processed, and the new frequency shift applies to that sample and all subsequent samples until the next frequency change.
  • Multisample (wideband) implementation: Timed and untimed commands are stored in the same 32-entry FPGA command queue. Commands are applied only when the DDC is actively processing data, and only the command at the front of the queue can be applied.
    • Untimed commands: An untimed command waits for all commands already ahead of it in the queue, including timed commands. It is then applied on the next data transfer. Consequently, successive untimed commands are not collapsed or skipped; each remains active for at least one data transfer containing NIPC IQ samples.
    • Timed commands: A timed command waits in the queue until it reaches the front and its requested timestamp has been reached. It is applied starting with the data transfer containing the IQ sample corresponding to that timestamp and remains active until the next frequency change.

In the multisample implementation, timed frequency shifts have word-level rather than sample-level granularity. One word is one data transfer containing NIPC samples (see NIPC). Therefore, if the requested timestamp falls within a word, the new frequency shift will be applied to samples earlier in that same word. For example, if the timestamp corresponds to the third sample in a word of eight samples, the new frequency shift will be applied to all eight samples in that word, including the first two samples preceding the requested timestamp, and to all subsequent samples until the next frequency change. The exact sample-level transition is not guaranteed.

Register Maps and Compatibility

The DDC has two register-map generations:

  • Compatibility major 0 is the legacy single-sample register map, used by implementations for bandwidths up to 200 MHz.
  • Compatibility major 1 is the multisample register map, used by the wideband implementation.

The block controller reads the FPGA compatibility number from RB_COMPAT_NUM and selects the corresponding register map. An unsupported major version is rejected. REG_ADDRS_V0 and REG_ADDRS_V1 contain the version-specific addresses used by the controller.

Classes

struct  reg_addrs_t
 Version-specific register map for the DDC block. More...
 

Public Member Functions

virtual double set_freq (const double freq, const size_t chan, const std::optional< uhd::time_spec_t > time={})=0
 
double set_freq (const double freq, const size_t chan, const uhd::time_spec_t time)
 
double set_freq (const double freq, const size_t chan, const boost::optional< uhd::time_spec_t > time)
 
virtual double get_freq (const size_t chan) const =0
 
virtual uhd::freq_range_t get_frequency_range (const size_t chan) const =0
 
virtual double get_input_rate (const size_t chan) const =0
 
virtual void set_input_rate (const double rate, const size_t chan)=0
 
virtual double get_output_rate (const size_t chan) const =0
 
virtual uhd::meta_range_t get_output_rates (const size_t chan) const =0
 
virtual double set_output_rate (const double rate, const size_t chan)=0
 
virtual void issue_stream_cmd (const uhd::stream_cmd_t &stream_cmd, const size_t port)=0
 
- Public Member Functions inherited from uhd::rfnoc::noc_block_base
 ~noc_block_base () override
 
std::string get_unique_id () const override
 Unique ID for an RFNoC block is its block ID. More...
 
size_t get_num_input_ports () const override
 
size_t get_num_output_ports () const override
 
noc_id_t get_noc_id () const
 
const block_id_t & get_block_id () const
 
double get_tick_rate () const
 
size_t get_mtu (const res_source_info &edge)
 
size_t get_chdr_hdr_len (const bool account_for_ts=true) const
 
size_t get_max_payload_size (const res_source_info &edge, const bool account_for_ts=true)
 
uhd::device_addr_t get_block_args () const
 
uhd::property_tree::sptr & get_tree () const
 Return a reference to this block's subtree. More...
 
uhd::property_tree::sptr & get_tree ()
 Return a reference to this block's subtree (non-const version) More...
 
std::shared_ptr< mb_controller > get_mb_controller ()
 
- Public Member Functions inherited from uhd::rfnoc::node_t
 node_t ()
 
virtual ~node_t ()
 
std::vector< std::string > get_property_ids () const
 
template<typename prop_data_t >
void set_property (const std::string &id, const prop_data_t &val, const size_t instance=0)
 
void set_properties (const uhd::device_addr_t &props, const size_t instance=0)
 
template<typename prop_data_t >
const prop_data_t & get_property (const std::string &id, const size_t instance=0)
 
virtual void set_command_time (uhd::time_spec_t time, const size_t instance)
 
virtual uhd::time_spec_t get_command_time (const size_t instance) const
 
virtual void clear_command_time (const size_t instance)
 
- Public Member Functions inherited from uhd::rfnoc::register_iface_holder
 register_iface_holder (register_iface::sptr reg)
 
virtual ~register_iface_holder ()=default
 
register_iface & regs ()
 

Static Public Attributes

static const uint32_t RB_COMPAT_NUM
 Compat register address (same across all register map versions) More...
 
static const reg_addrs_t REG_ADDRS_V0
 Register map for the single-sample (legacy) DDC (compat major 0) More...
 
static const reg_addrs_t REG_ADDRS_V1
 Register map for the multisample DDC (compat major 1) More...
 
- Static Public Attributes inherited from uhd::rfnoc::node_t
static const size_t ANY_PORT = size_t(~0)
 

Additional Inherited Members

- Public Types inherited from uhd::rfnoc::noc_block_base
using sptr = std::shared_ptr< noc_block_base >
 
using make_args_ptr = std::unique_ptr< make_args_int_t, make_args_deleter >
 Opaque pointer to the constructor arguments with custom deleter. More...
 
- Public Types inherited from uhd::rfnoc::node_t
enum class  action_mode_t { SYNC , ASYNC }
 Action execution modes. More...
 
enum class  forwarding_policy_t {
  ONE_TO_ONE , ONE_TO_FAN , ONE_TO_ALL_IN , ONE_TO_ALL_OUT ,
  ONE_TO_ALL , DROP , USE_MAP
}
 Types of property/action forwarding for those not defined by the block itself. More...
 
using resolver_fn_t = std::function< void(void)>
 
using resolve_callback_t = std::function< void(void)>
 
using graph_mutex_callback_t = std::function< std::recursive_mutex &(void)>
 
using action_handler_t = std::function< void(const res_source_info &, action_info::sptr)>
 
using post_action_handler_t = std::function< void(const res_source_info &, action_info::sptr, action_mode_t)>
 
using forwarding_map_t = std::unordered_map< res_source_info, std::vector< res_source_info > >
 
- Protected Types inherited from uhd::rfnoc::node_t
using prop_ptrs_t = std::vector< property_base_t * >
 
- Protected Member Functions inherited from uhd::rfnoc::noc_block_base
 noc_block_base (make_args_ptr make_args)
 
void set_num_input_ports (const size_t num_ports)
 
void set_num_output_ports (const size_t num_ports)
 
void set_tick_rate (const double tick_rate)
 
void set_mtu_forwarding_policy (const forwarding_policy_t policy)
 
void set_mtu (const res_source_info &edge, const size_t new_mtu)
 
property_base_t * get_mtu_prop_ref (const res_source_info &edge)
 
virtual void deinit ()
 
- Protected Member Functions inherited from uhd::rfnoc::node_t
void register_property (property_base_t *prop, resolve_callback_t &&clean_callback=nullptr)
 
void add_property_resolver (prop_ptrs_t &&inputs, prop_ptrs_t &&outputs, resolver_fn_t &&resolver_fn)
 
void set_prop_forwarding_policy (forwarding_policy_t policy, const std::string &prop_id="")
 
void set_prop_forwarding_map (const forwarding_map_t &map)
 
template<typename prop_data_t >
void set_property (const std::string &id, const prop_data_t &val, const res_source_info &src_info)
 
template<typename prop_data_t >
const prop_data_t & get_property (const std::string &id, const res_source_info &src_info)
 
void register_action_handler (const std::string &id, action_handler_t &&handler)
 
void set_action_forwarding_policy (forwarding_policy_t policy, const std::string &action_key="")
 
void set_action_forwarding_map (const forwarding_map_t &map)
 
void post_action (const res_source_info &edge_info, action_info::sptr action, action_mode_t mode=action_mode_t::SYNC)
 
virtual bool check_topology (const std::vector< size_t > &connected_inputs, const std::vector< size_t > &connected_outputs)
 
- Protected Member Functions inherited from uhd::rfnoc::register_iface_holder
void update_reg_iface (register_iface::sptr new_iface=nullptr)
 
- Static Protected Attributes inherited from uhd::rfnoc::node_t
static dirtifier_t ALWAYS_DIRTY
 A dirtifyer object, useful for properties that always need updating. More...
 

Member Function Documentation

◆ get_freq()

virtual double uhd::rfnoc::ddc_block_control::get_freq ( const size_t  chan) const
pure virtual

Return the current DDS frequency

Returns
The current frequency of the DDS

◆ get_frequency_range()

virtual uhd::freq_range_t uhd::rfnoc::ddc_block_control::get_frequency_range ( const size_t  chan) const
pure virtual

Return the range of frequencies that chan can be set to.

The frequency shifter is the first component in the DDC, and thus can shift frequencies (digitally) between -get_input_rate()/2 and +get_input_rate()/2.

The returned values are in Hz (not normalized frequencies) and are valid inputs for set_freq().

Returns
The range of frequencies that the DDC can shift the input by

◆ get_input_rate()

virtual double uhd::rfnoc::ddc_block_control::get_input_rate ( const size_t  chan) const
pure virtual

Return the sampling rate at this block's input

Parameters
chanThe channel for which the rate is being queried
Returns
the sampling rate at this block's input

◆ get_output_rate()

virtual double uhd::rfnoc::ddc_block_control::get_output_rate ( const size_t  chan) const
pure virtual

Return the sampling rate at this block's output

This is equivalent to calling get_input_rate() divided by the decimation.

Parameters
chanThe channel for which the rate is being queried
Returns
the sampling rate at this block's input

◆ get_output_rates()

virtual uhd::meta_range_t uhd::rfnoc::ddc_block_control::get_output_rates ( const size_t  chan) const
pure virtual

Return a range of valid output rates, based on the current input rate

Note the return value is only valid as long as the input rate does not change.

◆ issue_stream_cmd()

virtual void uhd::rfnoc::ddc_block_control::issue_stream_cmd ( const uhd::stream_cmd_t &  stream_cmd,
const size_t  port 
)
pure virtual

Issue stream command: Instruct the RX part of the radio to send samples

Parameters
stream_cmdThe actual stream command to execute
portThe port for which the stream command is meant

◆ set_freq() [1/3]

double uhd::rfnoc::ddc_block_control::set_freq ( const double  freq,
const size_t  chan,
const boost::optional< uhd::time_spec_t >  time 
)
inline

◆ set_freq() [2/3]

virtual double uhd::rfnoc::ddc_block_control::set_freq ( const double  freq,
const size_t  chan,
const std::optional< uhd::time_spec_t >  time = {} 
)
pure virtual

Set the DDS frequency

This block will shift the signal at the input by this frequency before decimation. The frequency is specified in Hz rather than as a normalized frequency; the valid range is from -get_input_rate()/2 to +get_input_rate()/2.

Note: When the sample rate is modified, the frequency shift is kept constant. Because the FPGA internally uses a relative phase increment, changing the input sampling rate will trigger a property propagation to recalculate the phase increment based off of this value.

For the multisample implementation, timed and untimed frequency commands are queued in a 32-entry FPGA queue. While the DDC is actively processing data, queued commands are processed one at a time; an untimed command is applied on the next transfer, while a timed command also waits for its requested timestamp.

This function will coerce the frequency to a valid value, and return the coerced value.

Parameters
freqThe frequency shift in Hz
chanThe channel to which this change shall be applied
timeWhen to apply the new frequency
Returns
The coerced, actual current frequency of the DDS

◆ set_freq() [3/3]

double uhd::rfnoc::ddc_block_control::set_freq ( const double  freq,
const size_t  chan,
const uhd::time_spec_t  time 
)
inline

◆ set_input_rate()

virtual void uhd::rfnoc::ddc_block_control::set_input_rate ( const double  rate,
const size_t  chan 
)
pure virtual

Manually set the sampling rate at this block's input

Parameters
rateThe requested rate
chanThe channel for which the rate is being set

◆ set_output_rate()

virtual double uhd::rfnoc::ddc_block_control::set_output_rate ( const double  rate,
const size_t  chan 
)
pure virtual

Attempt to set the output rate of this block

This will set the decimation such that the input rate is untouched, and that the input rate divided by the new decimation is as close as possible to the requested rate.

Parameters
rateThe requested rate
chanThe channel for which the rate is being queried
Returns
the coerced sampling rate at this block's output

Member Data Documentation

◆ RB_COMPAT_NUM

const uint32_t uhd::rfnoc::ddc_block_control::RB_COMPAT_NUM
static

Compat register address (same across all register map versions)

◆ REG_ADDRS_V0

const reg_addrs_t uhd::rfnoc::ddc_block_control::REG_ADDRS_V0
static

Register map for the single-sample (legacy) DDC (compat major 0)

◆ REG_ADDRS_V1

const reg_addrs_t uhd::rfnoc::ddc_block_control::REG_ADDRS_V1
static

Register map for the multisample DDC (compat major 1)


The documentation for this class was generated from the following file: