aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2019-05-31 15:08:27 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 11:49:22 -0800
commit6730a0b72b3a6a5b1b94a5d09bfb395d837672da (patch)
tree643ee003afd92489d8d0bad217e5e3a8655693ca /host
parent911b89e4f6e2df66d355448c452af0b2fc058fe6 (diff)
downloaduhd-6730a0b72b3a6a5b1b94a5d09bfb395d837672da.tar.gz
uhd-6730a0b72b3a6a5b1b94a5d09bfb395d837672da.tar.bz2
uhd-6730a0b72b3a6a5b1b94a5d09bfb395d837672da.zip
rfnoc: Add radio block controller
Diffstat (limited to 'host')
-rw-r--r--host/include/uhd/rfnoc/CMakeLists.txt1
-rw-r--r--host/include/uhd/rfnoc/radio_control.hpp688
-rw-r--r--host/lib/include/uhdlib/rfnoc/radio_control_impl.hpp289
-rw-r--r--host/lib/rfnoc/CMakeLists.txt1
-rw-r--r--host/lib/rfnoc/radio_control_impl.cpp771
-rw-r--r--host/tests/CMakeLists.txt1
6 files changed, 1751 insertions, 0 deletions
diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt
index 5b81ae6e1..c8308953f 100644
--- a/host/include/uhd/rfnoc/CMakeLists.txt
+++ b/host/include/uhd/rfnoc/CMakeLists.txt
@@ -41,6 +41,7 @@ if(ENABLE_RFNOC)
fir_block_ctrl.hpp
null_block_ctrl.hpp
radio_ctrl.hpp
+ radio_control.hpp
replay_block_ctrl.hpp
siggen_block_ctrl.hpp
window_block_ctrl.hpp
diff --git a/host/include/uhd/rfnoc/radio_control.hpp b/host/include/uhd/rfnoc/radio_control.hpp
new file mode 100644
index 000000000..f966379e1
--- /dev/null
+++ b/host/include/uhd/rfnoc/radio_control.hpp
@@ -0,0 +1,688 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RADIO_BLOCK_CONTROL_HPP
+#define INCLUDED_LIBUHD_RADIO_BLOCK_CONTROL_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/rfnoc/noc_block_base.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/stream_cmd.hpp>
+
+namespace uhd { namespace rfnoc {
+
+/*! Parent class for radio block controllers
+ */
+class radio_control : public noc_block_base
+{
+public:
+ static const std::string ALL_LOS;
+ static const std::string ALL_GAINS;
+ static const size_t ALL_CHANS;
+
+ RFNOC_DECLARE_BLOCK(radio_control)
+
+ /**************************************************************************
+ * Rate-Related API Calls
+ *************************************************************************/
+ //! Set the sample rate
+ //
+ // This function will coerce the rate and return the actual, current value.
+ virtual double set_rate(const double rate) = 0;
+
+ //! Get the sample rate
+ virtual double get_rate() const = 0;
+
+ //! Return a list of valid rates
+ virtual uhd::meta_range_t get_rate_range() const = 0;
+
+ /**************************************************************************
+ * RF-Related API Calls
+ *************************************************************************/
+ /*! Return the selected TX antenna for channel \p chan.
+ *
+ * \return The selected antenna.
+ */
+ virtual std::string get_tx_antenna(const size_t chan) const = 0;
+
+ /*! Return a list of valid TX antenna for channel \p chan.
+ *
+ * \return The selected antenna.
+ */
+ virtual std::vector<std::string> get_tx_antennas(const size_t chan) const = 0;
+
+ /*! Select RX antenna \p for channel \p chan.
+ *
+ * \throws uhd::value_error if \p ant is not a valid value.
+ */
+ virtual void set_tx_antenna(const std::string& ant, const size_t chan) = 0;
+
+ /*! Return the selected RX antenna for channel \p chan.
+ *
+ * \return The selected antenna.
+ */
+ virtual std::string get_rx_antenna(const size_t chan) const = 0;
+
+ /*! Return a list of valid RX antenna for channel \p chan.
+ *
+ * \return The selected antenna.
+ */
+ virtual std::vector<std::string> get_rx_antennas(const size_t chan) const = 0;
+
+ /*! Select RX antenna \p for channel \p chan.
+ *
+ * \throws uhd::value_error if \p ant is not a valid value.
+ */
+ virtual void set_rx_antenna(const std::string& ant, const size_t chan) = 0;
+
+ /*! Return the current transmit LO frequency on channel \p chan.
+ *
+ * \return The current LO frequency.
+ */
+ virtual double get_tx_frequency(const size_t chan) = 0;
+
+ /*! Tune the TX frequency for channel \p chan.
+ *
+ * This function will attempt to tune as close as possible, and return a
+ * coerced value of the actual tuning result.
+ *
+ * If there is a single LO in this radio, and we're doing direct conversion,
+ * then this is the LO frequency.
+ *
+ * \param freq Frequency in Hz
+ * \param chan Channel to tune
+ *
+ * \return The actual frequency.
+ */
+ virtual double set_tx_frequency(const double freq, size_t chan) = 0;
+
+ /*! Set the TX tune args, if supported by the hardware.
+ */
+ virtual void set_tx_tune_args(const uhd::device_addr_t& args, const size_t chan) = 0;
+
+ /*! Return the range of frequencies that \p chan can be tuned to.
+ *
+ * \return The range of frequencies that we can tune the TX chan to
+ */
+ virtual uhd::freq_range_t get_tx_frequency_range(const size_t chan) const = 0;
+
+ /*! Return the current receive LO frequency on channel \p chan.
+ *
+ * \return The current LO frequency.
+ */
+ virtual double get_rx_frequency(const size_t chan) = 0;
+
+ /*! Tune the RX frequency for channel \p chan.
+ *
+ * This function will attempt to tune as close as possible, and return a
+ * coerced value of the actual tuning result.
+ *
+ * If there is a single LO in this radio, and we're doing direct conversion,
+ * then this is the LO frequency.
+ *
+ * \param freq Requested frequency
+ * \param chan Channel number.
+ * \return The actual frequency.
+ */
+ virtual double set_rx_frequency(const double freq, const size_t chan) = 0;
+
+ /*! Set the TX tune args, if supported by the hardware.
+ */
+ virtual void set_rx_tune_args(const uhd::device_addr_t& args, const size_t chan) = 0;
+
+ /*! Return the range of frequencies that \p chan can be tuned to.
+ *
+ * \return The range of frequencies that we can tune the RX chan to
+ */
+ virtual uhd::freq_range_t get_rx_frequency_range(const size_t chan) const = 0;
+
+ /*! Return a list of valid TX gain names
+ */
+ virtual std::vector<std::string> get_tx_gain_names(const size_t chan) const = 0;
+
+ /*! Return a range of valid TX gains
+ */
+ virtual uhd::gain_range_t get_tx_gain_range(const size_t chan) const = 0;
+
+ /*! Return a range of valid TX gains
+ */
+ virtual uhd::gain_range_t get_tx_gain_range(
+ const std::string& name, const size_t chan) const = 0;
+
+ /*! Return the overall transmit gain on channel \p chan
+ *
+ * \return The actual gain value
+ */
+ virtual double get_tx_gain(const size_t chan) = 0;
+
+ /*! Return the transmit gain \p name on channel \p chan
+ *
+ * \return The actual gain value
+ */
+ virtual double get_tx_gain(const std::string& name, const size_t chan) = 0;
+
+ /*! Set the transmit gain on channel \p chan
+ *
+ * This function will attempt to set the gain as close as possible,
+ * and return a coerced value of the actual gain value.
+ *
+ * This method will set the overall gain. To set a specific gain, use
+ * set_tx_gain(const double, const std::string&, const size_t).
+ *
+ * \return The actual gain value
+ */
+ virtual double set_tx_gain(const double gain, const size_t chan) = 0;
+
+ /*! Set the transmit gain \p name on channel \p chan
+ *
+ * This function will attempt to set the gain as close as possible,
+ * and return a coerced value of the actual gain value.
+ *
+ * \return The actual gain value
+ */
+ virtual double set_tx_gain(
+ const double gain, const std::string& name, const size_t chan) = 0;
+
+ /*! Return a list of valid RX gain names
+ */
+ virtual std::vector<std::string> get_rx_gain_names(const size_t chan) const = 0;
+
+ /*! Return a range of valid RX gains
+ */
+ virtual uhd::gain_range_t get_rx_gain_range(const size_t chan) const = 0;
+
+ /*! Return a range of valid RX gains
+ */
+ virtual uhd::gain_range_t get_rx_gain_range(
+ const std::string& name, const size_t chan) const = 0;
+
+ /*! Return the overall receive gain on channel \p chan
+ *
+ * \return The actual gain value
+ */
+ virtual double get_rx_gain(const size_t chan) = 0;
+
+ /*! Return the receive gain \p name on channel \p chan
+ *
+ * \return The actual gain value
+ */
+ virtual double get_rx_gain(const std::string& name, const size_t chan) = 0;
+
+ /*! Set the overall receive gain on channel \p chan
+ *
+ * This function will attempt to set the gain as close as possible,
+ * and return a coerced value of the actual gain value.
+ *
+ * \return The actual gain value
+ */
+ virtual double set_rx_gain(const double gain, const size_t chan) = 0;
+
+ /*! Set the receive gain \p on channel \p chan
+ *
+ * This function will attempt to set the gain as close as possible,
+ * and return a coerced value of the actual gain value.
+ *
+ * \return The actual gain value
+ */
+ virtual double set_rx_gain(
+ const double gain, const std::string& name, const size_t chan) = 0;
+
+ /*! Enable RX AGC on this radio
+ *
+ * \throws uhd::not_implemented_error if this radio doesn't support RX AGC
+ */
+ virtual void set_rx_agc(const bool enable, const size_t chan) = 0;
+
+ /*! Return a list of TX gain profiles for this radio
+ */
+ virtual std::vector<std::string> get_tx_gain_profile_names(const size_t chan) const = 0;
+
+ /*! Return a list of TX gain profiles for this radio
+ */
+ virtual std::vector<std::string> get_rx_gain_profile_names(const size_t chan) const = 0;
+
+ /*! Set the TX gain profile
+ */
+ virtual void set_tx_gain_profile(const std::string& profile, const size_t chan) = 0;
+
+ /*! Set the RX gain profile
+ */
+ virtual void set_rx_gain_profile(const std::string& profile, const size_t chan) = 0;
+
+ /*! Return the TX gain profile
+ */
+ virtual std::string get_tx_gain_profile(const size_t chan) const = 0;
+
+ /*! Set the RX gain profile
+ */
+ virtual std::string get_rx_gain_profile(const size_t chan) const = 0;
+
+ /*! Return a range of valid TX bandwidths
+ */
+ virtual meta_range_t get_tx_bandwidth_range(size_t chan) const = 0;
+
+ /*! Return the analog filter bandwidth channel \p chan
+ *
+ * \return The actual bandwidth value
+ */
+ virtual double get_tx_bandwidth(const size_t chan) = 0;
+
+ /*! Set the analog filter bandwidth channel \p chan
+ *
+ * This function will attempt to set the analog bandwidth.
+ *
+ * \return The actual bandwidth value
+ */
+ virtual double set_tx_bandwidth(const double bandwidth, const size_t chan) = 0;
+
+ /*! Return a range of valid RX bandwidths
+ */
+ virtual meta_range_t get_rx_bandwidth_range(size_t chan) const = 0;
+
+ /*! Return the analog filter bandwidth channel \p chan
+ *
+ * \return The actual bandwidth value
+ */
+ virtual double get_rx_bandwidth(const size_t chan) = 0;
+
+ /*! Set the analog filter bandwidth channel \p chan
+ *
+ * This function will attempt to set the analog bandwidth.
+ *
+ * \return The actual bandwidth value
+ */
+ virtual double set_rx_bandwidth(const double bandwidth, const size_t chan) = 0;
+
+ /**************************************************************************
+ * LO Controls
+ *************************************************************************/
+ /*! Get a list of possible LO stage names
+ *
+ * \param chan the channel index 0 to N-1
+ * \return a vector of strings for possible LO names
+ */
+ virtual std::vector<std::string> get_rx_lo_names(const size_t chan) const = 0;
+
+ /*! Get a list of possible LO sources.
+ *
+ * Channels which do not have controllable LO sources
+ * will return "internal".
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return a vector of strings for possible settings
+ */
+ virtual std::vector<std::string> get_rx_lo_sources(
+ const std::string& name, const size_t chan) const = 0;
+
+ /*!
+ * Get the LO frequency range of the RX LO.
+ * If the channel does not have independently configurable LOs
+ * the rf frequency range will be returned.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return a frequency range object
+ */
+ virtual freq_range_t get_rx_lo_freq_range(
+ const std::string& name, const size_t chan) const = 0;
+
+ /*!
+ * Set the LO source for a channel.
+ * For usrps that support selectable LOs, this function
+ * allows switching between them.
+ * Typical options for source: internal, external.
+ * \param src a string representing the LO source
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1
+ */
+ virtual void set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Get the currently set LO source.
+ * Channels without controllable LO sources will return
+ * "internal"
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return the configured LO source
+ */
+ virtual const std::string get_rx_lo_source(
+ const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Set whether the LO used by the usrp device is exported
+ * For usrps that support exportable LOs, this function
+ * configures if the LO used by chan is exported or not.
+ * \param enabled if true then export the LO
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1 for the source channel
+ */
+ virtual void set_rx_lo_export_enabled(
+ bool enabled, const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Returns true if the currently selected LO is being exported.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ */
+ virtual bool get_rx_lo_export_enabled(
+ const std::string& name, const size_t chan) const = 0;
+
+ /*!
+ * Set the RX LO frequency (Advanced).
+ * \param freq the frequency to set the LO to
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1
+ * \return a coerced LO frequency
+ */
+ virtual double set_rx_lo_freq(
+ double freq, const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Get the current RX LO frequency (Advanced).
+ * If the channel does not have independently configurable LOs
+ * the current rf frequency will be returned.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return the configured LO frequency
+ */
+ virtual double get_rx_lo_freq(const std::string& name, const size_t chan) = 0;
+
+ /*! Get a list of possible LO stage names
+ *
+ * \param chan the channel index 0 to N-1
+ * \return a vector of strings for possible LO names
+ */
+ virtual std::vector<std::string> get_tx_lo_names(const size_t chan) const = 0;
+
+ /*! Get a list of possible LO sources.
+ *
+ * Channels which do not have controllable LO sources
+ * will return "internal".
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return a vector of strings for possible settings
+ */
+ virtual std::vector<std::string> get_tx_lo_sources(
+ const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Get the LO frequency range of the tx LO.
+ * If the channel does not have independently configurable LOs
+ * the rf frequency range will be returned.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return a frequency range object
+ */
+ virtual freq_range_t get_tx_lo_freq_range(
+ const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Set the LO source for a channel.
+ * For usrps that support selectable LOs, this function
+ * allows switching between them.
+ * Typical options for source: internal, external.
+ * \param src a string representing the LO source
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1
+ */
+ virtual void set_tx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Get the currently set LO source.
+ * Channels without controllable LO sources will return
+ * "internal"
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return the configured LO source
+ */
+ virtual const std::string get_tx_lo_source(
+ const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Set whether the LO used by the usrp device is exported
+ * For usrps that support exportable LOs, this function
+ * configures if the LO used by chan is exported or not.
+ * \param enabled if true then export the LO
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1 for the source channel
+ */
+ virtual void set_tx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan) = 0;
+
+ /*!
+ * Returns true if the currently selected LO is being exported.
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ */
+ virtual bool get_tx_lo_export_enabled(const std::string& name, const size_t chan) = 0;
+
+ /*! Set the tx LO frequency (Advanced).
+ *
+ * See also multi_usrp::set_tx_lo_freq().
+ *
+ * \param freq the frequency to set the LO to
+ * \param name the name of the LO stage to update
+ * \param chan the channel index 0 to N-1
+ * \return a coerced LO frequency
+ */
+ virtual double set_tx_lo_freq(
+ const double freq, const std::string& name, const size_t chan) = 0;
+
+ /*! Get the current TX LO frequency (Advanced).
+ *
+ * See also multi_usrp::get_tx_lo_freq()
+ *
+ * If the channel does not have independently configurable LOs
+ * the current RF frequency will be returned.
+ *
+ * \param name the name of the LO stage to query
+ * \param chan the channel index 0 to N-1
+ * \return the configured LO frequency
+ */
+ virtual double get_tx_lo_freq(const std::string& name, const size_t chan) = 0;
+
+ /**************************************************************************
+ * Calibration-Related API Calls
+ *************************************************************************/
+ /*! Set a constant TX DC offset value
+ *
+ * The value is complex to control both I and Q.
+ *
+ * \param offset the dc offset (1.0 is full-scale)
+ * \param chan the channel index
+ */
+ virtual void set_tx_dc_offset(const std::complex<double>& offset, size_t chan) = 0;
+
+ /*! Get the valid range for TX DC offset values.
+ *
+ * \param chan the channel index
+ */
+ virtual meta_range_t get_tx_dc_offset_range(size_t chan) const = 0;
+
+ /*! Set the TX frontend IQ imbalance correction.
+ *
+ * Use this to adjust the magnitude and phase of I and Q.
+ *
+ * \param correction the complex correction
+ * \param chan the channel index 0 to N-1
+ */
+ virtual void set_tx_iq_balance(
+ const std::complex<double>& correction, size_t chan) = 0;
+
+ /*! Enable/disable the automatic RX DC offset correction.
+ * The automatic correction subtracts out the long-run average.
+ *
+ * When disabled, the averaging option operation is halted.
+ * Once halted, the average value will be held constant
+ * until the user re-enables the automatic correction
+ * or overrides the value by manually setting the offset.
+ *
+ * \param enb true to enable automatic DC offset correction
+ * \param chan the channel index
+ */
+ virtual void set_rx_dc_offset(const bool enb, size_t chan = ALL_CHANS) = 0;
+
+ /*! Set a constant RX DC offset value.
+ *
+ * The value is complex to control both I and Q.
+ * Only set this when automatic correction is disabled.
+ * \param offset the dc offset (1.0 is full-scale)
+ * \param chan the channel index 0 to N-1
+ */
+ virtual void set_rx_dc_offset(const std::complex<double>& offset, size_t chan) = 0;
+
+ /*! Get the valid range for RX DC offset values.
+ *
+ * \param chan the channel index
+ */
+ virtual meta_range_t get_rx_dc_offset_range(size_t chan) const = 0;
+
+ /*! Enable/disable the automatic IQ imbalance correction.
+ *
+ * \param enb true to enable automatic IQ balance correction
+ * \param chan the channel index 0 to N-1
+ */
+ virtual void set_rx_iq_balance(const bool enb, size_t chan) = 0;
+
+ /*! Enable/disable the automatic IQ imbalance correction.
+ *
+ * \param enb true to enable automatic IQ balance correction
+ * \param chan the channel index 0 to N-1
+ */
+ virtual void set_rx_iq_balance(
+ const std::complex<double>& correction, size_t chan) = 0;
+
+ /**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+ /*! Returns the list of GPIO banks that are associated with this radio.
+ *
+ * \returns list of GPIO bank names
+ */
+ virtual std::vector<std::string> get_gpio_banks() const = 0;
+
+ /*!
+ * Set a GPIO attribute on a particular GPIO bank.
+ * Possible attribute names:
+ * - CTRL - 1 for ATR mode 0 for GPIO mode
+ * - DDR - 1 for output 0 for input
+ * - OUT - GPIO output level (not ATR mode)
+ * - ATR_0X - ATR idle state
+ * - ATR_RX - ATR receive only state
+ * - ATR_TX - ATR transmit only state
+ * - ATR_XX - ATR full duplex state
+ * \param bank the name of a GPIO bank (e.g., FP0)
+ * \param attr the name of a GPIO attribute (e.g., CTRL)
+ * \param value the new value for this GPIO bank
+ * \param mask the bit mask to effect which pins are changed
+ */
+ virtual void set_gpio_attr(const std::string& bank,
+ const std::string& attr,
+ const uint32_t value,
+ const uint32_t mask) = 0;
+
+ /*!
+ * Get a GPIO attribute on a particular GPIO bank.
+ * Possible attribute names:
+ * - CTRL - 1 for ATR mode 0 for GPIO mode
+ * - DDR - 1 for output 0 for input
+ * - OUT - GPIO output level (not ATR mode)
+ * - ATR_0X - ATR idle state
+ * - ATR_RX - ATR receive only state
+ * - ATR_TX - ATR transmit only state
+ * - ATR_XX - ATR full duplex state
+ * - READBACK - readback input GPIOs
+ * \param bank the name of a GPIO bank
+ * \param attr the name of a GPIO attribute
+ * \return the value set for this attribute
+ */
+ virtual uint32_t get_gpio_attr(const std::string& bank, const std::string& attr) = 0;
+
+ /**************************************************************************
+ * Sensor API
+ *************************************************************************/
+ /*! Return a list of RX sensors
+ *
+ * The names returned in this list can be used as an input to get_rx_sensor()
+ */
+ virtual std::vector<std::string> get_rx_sensor_names(size_t chan) const = 0;
+
+ /*! Return the sensor value for sensor \p name
+ *
+ * \param name Sensor name (e.g. "lo_locked")
+ * \param chan Channel index
+ * \throws uhd::key_error if the sensor does not exist
+ */
+ virtual uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan) = 0;
+
+ /*! Return a list of TX sensors
+ *
+ * The names returned in this list can be used as an input to get_tx_sensor()
+ */
+ virtual std::vector<std::string> get_tx_sensor_names(size_t chan) const = 0;
+
+ /*! Return the sensor value for sensor \p name
+ *
+ * \param name Sensor name (e.g. "lo_locked")
+ * \param chan Channel index
+ * \throws uhd::key_error if the sensor does not exist
+ */
+ virtual uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan) = 0;
+
+ /**************************************************************************
+ * Streaming-Related API Calls
+ *************************************************************************/
+ /*! Issue stream command: Instruct the RX part of the radio to send samples
+ *
+ * \param stream_cmd The actual stream command to execute
+ * \param port The port for which the stream command is meant
+ */
+ virtual void issue_stream_cmd(
+ const uhd::stream_cmd_t& stream_cmd, const size_t port) = 0;
+
+ /**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+ //! Returns this radio's slot name (typically "A" or "B")
+ virtual std::string get_slot_name() const = 0;
+
+ //! Return the channel that corresponds to a frontend's name
+ //
+ // Example: "0" -> 0 (for UBX), or "A" -> 0 (for E310)
+ virtual size_t get_chan_from_dboard_fe(
+ const std::string& fe, const uhd::direction_t direction) const = 0;
+
+ //! Return the frontend name for a channel index
+ //
+ // Example: 0 -> "0" (for UBX), or 0 -> "A" (for E310)
+ virtual std::string get_dboard_fe_from_chan(
+ const size_t chan, const uhd::direction_t direction) const = 0;
+
+ //! Return the name of the frontend, as given by the dboard driver
+ virtual std::string get_fe_name(
+ const size_t chan, const uhd::direction_t direction) const = 0;
+
+ /**************************************************************************
+ * EEPROM API Calls
+ *************************************************************************/
+ //! Update the daughterboard EEPROM
+ //
+ // Note: EEPROMs have finite numbers of write cycles, so don't overuse this
+ // method!
+ virtual void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom) = 0;
+
+ //! Return the content of the daughterboard EEPROM
+ virtual uhd::eeprom_map_t get_db_eeprom() = 0;
+};
+
+}} // namespace uhd::rfnoc
+
+#endif /* INCLUDED_LIBUHD_RADIO_BLOCK_CONTROL_HPP */
diff --git a/host/lib/include/uhdlib/rfnoc/radio_control_impl.hpp b/host/lib/include/uhdlib/rfnoc/radio_control_impl.hpp
new file mode 100644
index 000000000..c5e990343
--- /dev/null
+++ b/host/lib/include/uhdlib/rfnoc/radio_control_impl.hpp
@@ -0,0 +1,289 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/rfnoc/defaults.hpp>
+#include <uhd/rfnoc/radio_control.hpp>
+#include <unordered_map>
+#include <mutex>
+
+#define RFNOC_RADIO_CONSTRUCTOR(CLASS_NAME) \
+ CLASS_NAME##_impl(make_args_ptr make_args) : radio_control_impl(std::move(make_args))
+
+namespace uhd { namespace rfnoc {
+
+/*! Base class of radio controllers
+ *
+ * All radio control classes should derive from this class (e.g., the X300 radio
+ * controller, etc.)
+ *
+ * Many of the radio_control API calls have virtual (default) implementations
+ * here, but they can be overridden.
+ */
+class radio_control_impl : public radio_control
+{
+public:
+ /**************************************************************************
+ * Structors
+ *************************************************************************/
+ radio_control_impl(make_args_ptr make_args);
+
+ virtual void deinit() {}
+
+ virtual ~radio_control_impl() {}
+
+ /**************************************************************************
+ * Stream control API calls
+ *************************************************************************/
+ void issue_stream_cmd(const uhd::stream_cmd_t& stream_cmd, const size_t port);
+
+ /**************************************************************************
+ * Rate-Related API Calls
+ *************************************************************************/
+ virtual double set_rate(const double rate);
+ virtual double get_rate() const;
+ virtual meta_range_t get_rate_range() const;
+
+ /**************************************************************************
+ * RF-specific API calls
+ *************************************************************************/
+ // Setters
+ virtual void set_tx_antenna(const std::string &ant, const size_t chan);
+ virtual void set_rx_antenna(const std::string &ant, const size_t chan);
+ virtual double set_tx_frequency(const double freq, const size_t chan);
+ virtual double set_rx_frequency(const double freq, const size_t chan);
+ virtual void set_tx_tune_args(const uhd::device_addr_t&, const size_t chan);
+ virtual void set_rx_tune_args(const uhd::device_addr_t&, const size_t chan);
+ virtual double set_tx_gain(const double gain, const size_t chan);
+ virtual double set_tx_gain(const double gain, const std::string& name, const size_t chan);
+ virtual double set_rx_gain(const double gain, const size_t chan);
+ virtual double set_rx_gain(const double gain, const std::string& name, const size_t chan);
+ virtual void set_rx_agc(const bool enable, const size_t chan);
+ virtual double set_tx_bandwidth(const double bandwidth, const size_t chan);
+ virtual double set_rx_bandwidth(const double bandwidth, const size_t chan);
+ virtual void set_tx_gain_profile(const std::string& profile, const size_t chan);
+ virtual void set_rx_gain_profile(const std::string& profile, const size_t chan);
+
+ // Getters
+ virtual std::string get_tx_antenna(const size_t chan) const;
+ virtual std::string get_rx_antenna(const size_t chan) const;
+ virtual std::vector<std::string> get_tx_antennas(const size_t chan) const;
+ virtual std::vector<std::string> get_rx_antennas(const size_t chan) const;
+ virtual double get_tx_frequency(const size_t);
+ virtual double get_rx_frequency(const size_t);
+ virtual uhd::freq_range_t get_tx_frequency_range(const size_t chan) const;
+ virtual uhd::freq_range_t get_rx_frequency_range(const size_t chan) const;
+ virtual std::vector<std::string> get_tx_gain_names(const size_t) const;
+ virtual std::vector<std::string> get_rx_gain_names(const size_t) const;
+ virtual double get_tx_gain(const size_t);
+ virtual double get_tx_gain(const std::string&, size_t);
+ virtual double get_rx_gain(const size_t);
+ virtual double get_rx_gain(const std::string&, size_t);
+ virtual uhd::gain_range_t get_tx_gain_range(const size_t) const;
+ virtual uhd::gain_range_t get_tx_gain_range(const std::string&, const size_t) const;
+ virtual uhd::gain_range_t get_rx_gain_range(const size_t) const;
+ virtual uhd::gain_range_t get_rx_gain_range(const std::string&, const size_t) const;
+ virtual std::vector<std::string> get_tx_gain_profile_names(const size_t chan) const;
+ virtual std::vector<std::string> get_rx_gain_profile_names(const size_t chan) const;
+ virtual std::string get_tx_gain_profile(const size_t chan) const;
+ virtual std::string get_rx_gain_profile(const size_t chan) const;
+ virtual double get_tx_bandwidth(const size_t);
+ virtual double get_rx_bandwidth(const size_t);
+ virtual meta_range_t get_tx_bandwidth_range(size_t chan) const;
+ virtual meta_range_t get_rx_bandwidth_range(size_t chan) const;
+
+ /**************************************************************************
+ * LO Controls
+ *************************************************************************/
+ virtual std::vector<std::string> get_rx_lo_names(const size_t chan) const;
+ virtual std::vector<std::string> get_rx_lo_sources(
+ const std::string& name, const size_t chan) const;
+ virtual freq_range_t get_rx_lo_freq_range(
+ const std::string& name, const size_t chan) const;
+ virtual void set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan);
+ virtual const std::string get_rx_lo_source(
+ const std::string& name, const size_t chan);
+ virtual void set_rx_lo_export_enabled(
+ bool enabled, const std::string& name, const size_t chan);
+ virtual bool get_rx_lo_export_enabled(
+ const std::string& name, const size_t chan) const;
+ virtual double set_rx_lo_freq(
+ double freq, const std::string& name, const size_t chan);
+ virtual double get_rx_lo_freq(const std::string& name, const size_t chan);
+ virtual std::vector<std::string> get_tx_lo_names(const size_t chan) const;
+ virtual std::vector<std::string> get_tx_lo_sources(
+ const std::string& name, const size_t chan);
+ virtual freq_range_t get_tx_lo_freq_range(
+ const std::string& name, const size_t chan);
+ virtual void set_tx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan);
+ virtual const std::string get_tx_lo_source(
+ const std::string& name, const size_t chan);
+ virtual void set_tx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan);
+ virtual bool get_tx_lo_export_enabled(const std::string& name, const size_t chan);
+ virtual double set_tx_lo_freq(
+ const double freq, const std::string& name, const size_t chan);
+ virtual double get_tx_lo_freq(const std::string& name, const size_t chan);
+
+ /**************************************************************************
+ * Calibration-Related API Calls
+ *************************************************************************/
+ virtual void set_tx_dc_offset(const std::complex<double>& offset, size_t chan);
+ virtual meta_range_t get_tx_dc_offset_range(size_t chan) const;
+ virtual void set_tx_iq_balance(const std::complex<double>& correction, size_t chan);
+ virtual void set_rx_dc_offset(const bool enb, size_t chan = ALL_CHANS);
+ virtual void set_rx_dc_offset(const std::complex<double>& offset, size_t chan);
+ virtual meta_range_t get_rx_dc_offset_range(size_t chan) const;
+ virtual void set_rx_iq_balance(const bool enb, size_t chan);
+ virtual void set_rx_iq_balance(const std::complex<double>& correction, size_t chan);
+
+ /**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+ virtual std::vector<std::string> get_gpio_banks() const;
+ virtual void set_gpio_attr(const std::string& bank,
+ const std::string& attr,
+ const uint32_t value,
+ const uint32_t mask);
+ virtual uint32_t get_gpio_attr(const std::string& bank, const std::string& attr);
+
+ /**************************************************************************
+ * Sensor API
+ *************************************************************************/
+ virtual std::vector<std::string> get_rx_sensor_names(size_t chan) const;
+ virtual uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan);
+ virtual std::vector<std::string> get_tx_sensor_names(size_t chan) const;
+ virtual uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan);
+
+ /**************************************************************************
+ * Identification API
+ *************************************************************************/
+ virtual std::string get_fe_name(
+ const size_t chan, const uhd::direction_t direction) const
+ {
+ return get_dboard_fe_from_chan(chan, direction);
+ }
+
+ /**************************************************************************
+ * EEPROM API
+ *************************************************************************/
+ virtual void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom);
+ virtual uhd::eeprom_map_t get_db_eeprom();
+
+ /***********************************************************************
+ * Reg Map
+ **********************************************************************/
+ static const uint16_t MAJOR_COMPAT;
+ static const uint16_t MINOR_COMPAT;
+
+ /*! Register map common to all radios
+ *
+ * See rfnoc_block_radio_regs.vh for details
+ */
+ struct regmap {
+ static const uint32_t REG_COMPAT_NUM = 0x00; // Compatibility number register offset
+ static const uint32_t REG_RADIO_WIDTH = 0x1000 + 0x04; // Upper 16 bits is sample width, lower 16 bits is NSPC
+
+ static const uint32_t RADIO_BASE_ADDR = 0x1000;
+ static const uint32_t REG_CHAN_OFFSET = 128;
+ static const uint32_t RADIO_ADDR_W = 7; // Address space size per radio
+
+ // General Radio Registers
+ static const uint32_t REG_LOOPBACK_EN = 0x00; // Loopback enable (connect Tx output to Rx input)
+
+ // Note on the RX and TX Control Registers: These are per-channel,
+ // which means the values here are offsets. The base address per
+ // channel is RADIO_BASE_ADDR + i * REG_CHAN_OFFSET, where i is the
+ // channel index.
+
+ // RX Control Registers
+ static const uint32_t REG_RX_STATUS = 0x10; // Status of Rx radio
+ static const uint32_t REG_RX_CMD = 0x14; // The next radio command to execute
+ static const uint32_t REG_RX_CMD_NUM_WORDS_LO = 0x18; // Number of radio words for the next command (low word)
+ static const uint32_t REG_RX_CMD_NUM_WORDS_HI = 0x1C; // Number of radio words for the next command (high word)
+ static const uint32_t REG_RX_CMD_TIME_LO = 0x20; // Time for the next command (low word)
+ static const uint32_t REG_RX_CMD_TIME_HI = 0x24; // Time for the next command (high word)
+ static const uint32_t REG_RX_MAX_WORDS_PER_PKT = 0x28; // Maximum packet length to build from Rx data
+ static const uint32_t REG_RX_ERR_PORT = 0x2C; // Port ID for error reporting
+ static const uint32_t REG_RX_ERR_REM_PORT = 0x30; // Remote port ID for error reporting
+ static const uint32_t REG_RX_ERR_REM_EPID = 0x34; // Remote EPID (endpoint ID) for error reporting
+ static const uint32_t REG_RX_ERR_ADDR = 0x38; // Offset to which to write error code (ADDR+0) and time (ADDR+8)
+ static const uint32_t REG_RX_DATA = 0x3C;
+
+ // TX Control Registers
+ static const uint32_t REG_TX_IDLE_VALUE = 0x40; // Value to output when transmitter is idle
+ static const uint32_t REG_TX_ERROR_POLICY = 0x44; // Tx error policy
+ static const uint32_t REG_TX_ERR_PORT = 0x48; // Port ID for error reporting
+ static const uint32_t REG_TX_ERR_REM_PORT = 0x4C; // Remote port ID for error reporting
+ static const uint32_t REG_TX_ERR_REM_EPID = 0x50; // Remote EPID (endpoint ID) for error reporting
+ static const uint32_t REG_TX_ERR_ADDR = 0x54; // Offset to which to write error code (ADDR+0) and time (ADDR+8)
+
+ static const uint32_t RX_CMD_STOP = 0; // Stop acquiring at end of next packet
+ static const uint32_t RX_CMD_FINITE = 1; // Acquire NUM_SAMPS then stop
+ static const uint32_t RX_CMD_CONTINUOUS = 2; // Acquire until stopped
+
+ static const uint32_t RX_CMD_TIMED_POS = 31;
+
+ static const uint32_t REG_SPI_W = 0x80000 + 168*8; // FIXME
+ static const uint32_t REG_SPI_R = 0x80000 + 17*8; // FIXME
+
+ static const uint32_t PERIPH_BASE = 0x80000;
+ static const uint32_t PERIPH_REG_OFFSET = 8;
+ };
+
+ //! Tree path to the dboard-specific properties
+ static const uhd::fs_path DB_PATH;
+ //! Tree path to the radio frontends' properties
+ static const uhd::fs_path FE_PATH;
+
+protected:
+ //! Properties for samp_rate (one per port)
+ std::vector<property_t<double>> _samp_rate_in;
+ //! Properties for samp_rate (one per port)
+ std::vector<property_t<double>> _samp_rate_out;
+
+private:
+ //! Receiver for the async messages
+ //
+ // This block will receive all async messages. The following async messages
+ // are expected to show up:
+ // - Overrun info
+ // - Underrun info
+ // - Late data packets
+ void async_message_handler(uint32_t addr, const std::vector<uint32_t>& data);
+
+ //! FPGA compat number
+ const uint32_t _fpga_compat;
+
+ //! Copy of the REG_RADIO_WIDTH register
+ const uint32_t _radio_width;
+
+ //! Sample width (total width, sc16 == 32 bits per complex sample)
+ const uint32_t _samp_width;
+
+ //! Samples per cycle
+ const uint32_t _spc;
+
+ std::vector<property_t<int>> _spp_prop;
+ //! Properties for type_in (one per port)
+ std::vector<property_t<io_type_t>> _type_in;
+ //! Properties for type_out (one per port)
+ std::vector<property_t<io_type_t>> _type_out;
+
+ mutable std::mutex _cache_mutex;
+ double _rate = 1.0;
+ std::unordered_map<size_t, std::string> _tx_antenna;
+ std::unordered_map<size_t, std::string> _rx_antenna;
+ std::unordered_map<size_t, double> _tx_freq;
+ std::unordered_map<size_t, double> _rx_freq;
+ std::unordered_map<size_t, double> _tx_gain;
+ std::unordered_map<size_t, double> _rx_gain;
+ std::unordered_map<size_t, double> _tx_bandwidth;
+ std::unordered_map<size_t, double> _rx_bandwidth;
+};
+
+}} // namespace uhd::rfnoc
diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt
index 366e2d062..127ad6ffd 100644
--- a/host/lib/rfnoc/CMakeLists.txt
+++ b/host/lib/rfnoc/CMakeLists.txt
@@ -58,6 +58,7 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/null_block_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/window_block_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/radio_control_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/siggen_block_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_block_ctrl_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/replay_block_ctrl_impl.cpp
diff --git a/host/lib/rfnoc/radio_control_impl.cpp b/host/lib/rfnoc/radio_control_impl.cpp
new file mode 100644
index 000000000..d58016b4d
--- /dev/null
+++ b/host/lib/rfnoc/radio_control_impl.cpp
@@ -0,0 +1,771 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/exception.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/rfnoc/radio_control_impl.hpp>
+#include <uhdlib/utils/compat_check.hpp>
+#include <map>
+#include <tuple>
+
+using namespace uhd::rfnoc;
+
+namespace {
+
+inline uint32_t get_addr(const uint32_t base_addr, const size_t chan)
+{
+ return radio_control_impl::regmap::RADIO_BASE_ADDR + base_addr
+ + radio_control_impl::regmap::REG_CHAN_OFFSET * chan;
+}
+
+const std::string DEFAULT_GAIN_PROFILE("default");
+
+} // namespace
+
+const std::string radio_control::ALL_LOS = "all";
+const std::string radio_control::ALL_GAINS = "";
+const size_t radio_control::ALL_CHANS = size_t(~0);
+
+const uint16_t radio_control_impl::MAJOR_COMPAT = 0;
+const uint16_t radio_control_impl::MINOR_COMPAT = 0;
+
+const uint32_t radio_control_impl::regmap::REG_COMPAT_NUM;
+const uint32_t radio_control_impl::regmap::REG_RADIO_WIDTH;
+const uint32_t radio_control_impl::regmap::RADIO_BASE_ADDR;
+const uint32_t radio_control_impl::regmap::REG_CHAN_OFFSET;
+const uint32_t radio_control_impl::regmap::RADIO_ADDR_W;
+const uint32_t radio_control_impl::regmap::REG_LOOPBACK_EN;
+const uint32_t radio_control_impl::regmap::REG_RX_STATUS;
+const uint32_t radio_control_impl::regmap::REG_RX_CMD;
+const uint32_t radio_control_impl::regmap::REG_RX_CMD_NUM_WORDS_LO;
+const uint32_t radio_control_impl::regmap::REG_RX_CMD_NUM_WORDS_HI;
+const uint32_t radio_control_impl::regmap::REG_RX_CMD_TIME_LO;
+const uint32_t radio_control_impl::regmap::REG_RX_CMD_TIME_HI;
+const uint32_t radio_control_impl::regmap::REG_RX_MAX_WORDS_PER_PKT;
+const uint32_t radio_control_impl::regmap::REG_RX_ERR_PORT;
+const uint32_t radio_control_impl::regmap::REG_RX_ERR_REM_PORT;
+const uint32_t radio_control_impl::regmap::REG_RX_ERR_REM_EPID;
+const uint32_t radio_control_impl::regmap::REG_RX_ERR_ADDR;
+const uint32_t radio_control_impl::regmap::REG_TX_IDLE_VALUE;
+const uint32_t radio_control_impl::regmap::REG_TX_ERROR_POLICY;
+const uint32_t radio_control_impl::regmap::REG_TX_ERR_PORT;
+const uint32_t radio_control_impl::regmap::REG_TX_ERR_REM_PORT;
+const uint32_t radio_control_impl::regmap::REG_TX_ERR_REM_EPID;
+const uint32_t radio_control_impl::regmap::REG_TX_ERR_ADDR;
+const uint32_t radio_control_impl::regmap::RX_CMD_STOP;
+const uint32_t radio_control_impl::regmap::RX_CMD_FINITE;
+const uint32_t radio_control_impl::regmap::RX_CMD_CONTINUOUS;
+const uint32_t radio_control_impl::regmap::RX_CMD_TIMED_POS;
+
+const uhd::fs_path radio_control_impl::DB_PATH("dboard");
+const uhd::fs_path radio_control_impl::FE_PATH("frontends");
+
+/****************************************************************************
+ * Structors
+ ***************************************************************************/
+radio_control_impl::radio_control_impl(make_args_ptr make_args)
+ : radio_control(std::move(make_args))
+ , _fpga_compat(regs().peek32(regmap::REG_COMPAT_NUM))
+ , _radio_width(regs().peek32(regmap::REG_RADIO_WIDTH))
+ , _samp_width(_radio_width >> 16)
+ , _spc(_radio_width & 0xFFFF)
+{
+ uhd::assert_fpga_compat(MAJOR_COMPAT,
+ MINOR_COMPAT,
+ _fpga_compat,
+ get_unique_id(),
+ get_unique_id(),
+ false /* Let it slide if minors mismatch */
+ );
+ RFNOC_LOG_TRACE(
+ "Loading radio with SPC=" << _spc << ", num_inputs=" << get_num_input_ports()
+ << ", num_outputs=" << get_num_output_ports());
+ set_prop_forwarding_policy(forwarding_policy_t::DROP);
+ set_action_forwarding_policy(forwarding_policy_t::DROP);
+ regs().register_async_msg_handler(
+ [this](uint32_t addr, const std::vector<uint32_t>& data) {
+ this->async_message_handler(addr, data);
+ });
+ register_action_handler(ACTION_KEY_STREAM_CMD,
+ [this](const res_source_info& src, action_info::sptr action) {
+ stream_cmd_action_info::sptr stream_cmd_action =
+ std::dynamic_pointer_cast<stream_cmd_action_info>(action);
+ if (!stream_cmd_action) {
+ RFNOC_LOG_WARNING("Received invalid stream command action!");
+ return;
+ }
+ RFNOC_LOG_TRACE(
+ "Received stream command: " << stream_cmd_action->stream_cmd.stream_mode
+ << " to " << src.to_string());
+ if (src.type != res_source_info::OUTPUT_EDGE) {
+ RFNOC_LOG_WARNING("Received stream command, but not to output port! Ignoring.");
+ return;
+ }
+ const size_t port = src.instance;
+ if (port > get_num_output_ports()) {
+ RFNOC_LOG_WARNING("Received stream command to invalid output port!");
+ return;
+ }
+ issue_stream_cmd(stream_cmd_action->stream_cmd, port);
+ });
+ // Register spp properties and resolvers
+ _spp_prop.reserve(get_num_output_ports());
+ _samp_rate_in.reserve(get_num_input_ports());
+ _samp_rate_out.reserve(get_num_output_ports());
+ _type_in.reserve(get_num_input_ports());
+ _type_out.reserve(get_num_output_ports());
+ for (size_t chan = 0; chan < get_num_output_ports(); ++chan) {
+ _spp_prop.push_back(property_t<int>(
+ PROP_KEY_SPP, DEFAULT_SPP, {res_source_info::USER, chan}));
+ _samp_rate_in.push_back(property_t<double>(
+ PROP_KEY_SAMP_RATE, get_tick_rate(), {res_source_info::INPUT_EDGE, chan}));
+ _samp_rate_out.push_back(property_t<double>(
+ PROP_KEY_SAMP_RATE, get_tick_rate(), {res_source_info::OUTPUT_EDGE, chan}));
+ _type_in.push_back(property_t<io_type_t>(
+ PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::INPUT_EDGE, chan}));
+ _type_out.push_back(property_t<io_type_t>(
+ PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::OUTPUT_EDGE, chan}));
+
+ register_property(&_spp_prop.back(), [this, chan, &spp = _spp_prop.back()]() {
+ const uint32_t words_per_pkt = spp.get();
+ RFNOC_LOG_TRACE(
+ "Setting words_per_pkt to " << words_per_pkt << " on chan " << chan);
+ regs().poke32(
+ get_addr(regmap::REG_RX_MAX_WORDS_PER_PKT, chan), words_per_pkt);
+ });
+ register_property(&_samp_rate_in.back());
+ register_property(&_samp_rate_out.back());
+ register_property(&_type_in.back());
+ register_property(&_type_out.back());
+ add_property_resolver({&_spp_prop.back()},
+ {&_spp_prop.back()},
+ [this, chan, &spp = _spp_prop.back()]() {
+ RFNOC_LOG_TRACE("Calling resolver for spp@" << chan);
+ if (spp.get() % _spc) {
+ spp = spp.get() - (spp.get() % _spc);
+ RFNOC_LOG_WARNING(
+ "spp must be a multiple of the block bus width! Coercing to "
+ << spp.get());
+ }
+ if (spp.get() <= 0) {
+ spp = DEFAULT_SPP;
+ RFNOC_LOG_WARNING(
+ "spp must be greater than zero! Coercing to " << spp.get());
+ }
+ });
+ add_property_resolver({&_samp_rate_in.back(), &_samp_rate_out.back()},
+ {&_samp_rate_in.back(), &_samp_rate_out.back()},
+ [this, chan,
+ &samp_rate_in = _samp_rate_in.at(chan),
+ &samp_rate_out = _samp_rate_out.at(chan)]() {
+ RFNOC_LOG_TRACE("Calling resolver for samp_rate@" << chan);
+ samp_rate_in = set_rate(samp_rate_in.get());
+ samp_rate_out = samp_rate_in.get();
+ });
+ // Resolvers for type: These are constants
+ add_property_resolver({&_type_in.back()},
+ {&_type_in.back()},
+ [& type_in = _type_in.back()]() { type_in.set(IO_TYPE_SC16); });
+ add_property_resolver({&_type_out.back()},
+ {&_type_out.back()},
+ [& type_out = _type_out.back()]() { type_out.set(IO_TYPE_SC16); });
+ }
+}
+
+/******************************************************************************
+ * Rate-Related API Calls
+ *****************************************************************************/
+double radio_control_impl::set_rate(const double rate)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ _rate = rate;
+ return rate;
+ // FIXME:
+ ////_tick_rate = rate;
+ ////_time64->set_tick_rate(_tick_rate);
+ ////_time64->self_test();
+ //// set_command_tick_rate(rate);
+}
+
+double radio_control_impl::get_rate() const
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _rate;
+}
+
+uhd::meta_range_t radio_control_impl::get_rate_range() const
+{
+ RFNOC_LOG_TRACE("Using default implementation of get_rx_rate_range()");
+ uhd::meta_range_t result;
+ result.push_back(get_rate());
+ return result;
+}
+
+/****************************************************************************
+ * RF API
+ ***************************************************************************/
+void radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ _tx_antenna[chan] = ant;
+}
+
+void radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ _rx_antenna[chan] = ant;
+}
+
+double radio_control_impl::set_tx_frequency(const double freq, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _tx_freq[chan] = freq;
+}
+
+void radio_control_impl::set_tx_tune_args(const uhd::device_addr_t&, const size_t)
+{
+ RFNOC_LOG_TRACE("tune_args not supported by this radio.");
+}
+
+double radio_control_impl::set_rx_frequency(const double freq, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _rx_freq[chan] = freq;
+}
+
+void radio_control_impl::set_rx_tune_args(const uhd::device_addr_t&, const size_t)
+{
+ RFNOC_LOG_TRACE("tune_args not supported by this radio.");
+}
+
+std::vector<std::string> radio_control_impl::get_tx_gain_names(const size_t) const
+{
+ return {ALL_GAINS};
+}
+
+std::vector<std::string> radio_control_impl::get_rx_gain_names(const size_t) const
+{
+ return {ALL_GAINS};
+}
+
+uhd::gain_range_t radio_control_impl::get_tx_gain_range(const size_t chan) const
+{
+ RFNOC_LOG_DEBUG("Using default implementation of get_tx_gain_range()");
+ uhd::gain_range_t result;
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ result.push_back(_rx_gain.at(chan));
+ return result;
+}
+
+uhd::gain_range_t radio_control_impl::get_tx_gain_range(
+ const std::string& name, const size_t chan) const
+{
+ if (name != ALL_GAINS) {
+ throw uhd::value_error(
+ std::string("get_tx_gain_range(): Unknown gain name `") + name + "'!");
+ }
+ return get_tx_gain_range(chan);
+}
+
+uhd::gain_range_t radio_control_impl::get_rx_gain_range(const size_t chan) const
+{
+ RFNOC_LOG_DEBUG("Using default implementation of get_rx_gain_range()");
+ uhd::gain_range_t result;
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ result.push_back(_rx_gain.at(chan));
+ return result;
+}
+
+uhd::gain_range_t radio_control_impl::get_rx_gain_range(const std::string& name, const size_t chan) const
+{
+ if (name != ALL_GAINS) {
+ throw uhd::value_error(
+ std::string("get_rx_gain_range(): Unknown gain name `") + name + "'!");
+ }
+ return get_rx_gain_range(chan);
+}
+
+double radio_control_impl::set_tx_gain(const double gain, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ _tx_gain[chan] = gain;
+ return gain;
+}
+
+double radio_control_impl::set_tx_gain(const double gain, const std::string& name, const size_t chan)
+{
+ if (name != ALL_GAINS) {
+ throw uhd::key_error(
+ std::string("set_tx_gain(): Gain name `") + name + "' is not defined!");
+ }
+ return set_tx_gain(gain, chan);
+}
+
+double radio_control_impl::set_rx_gain(const double gain, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ _rx_gain[chan] = gain;
+ return gain;
+}
+
+double radio_control_impl::set_rx_gain(const double gain, const std::string& name, const size_t chan)
+{
+ if (name != ALL_GAINS) {
+ throw uhd::key_error(
+ std::string("set_rx_gain(): Gain name `") + name + "' is not defined!");
+ }
+ return set_rx_gain(gain, chan);
+}
+
+void radio_control_impl::set_rx_agc(const bool, const size_t)
+{
+ throw uhd::not_implemented_error("set_rx_agc() is not supported on this radio!");
+}
+
+void radio_control_impl::set_tx_gain_profile(const std::string& profile, const size_t)
+{
+ if (profile != DEFAULT_GAIN_PROFILE) {
+ throw uhd::value_error(
+ std::string("set_tx_gain_profile(): Unknown gain profile: `") + profile
+ + "'");
+ }
+}
+
+void radio_control_impl::set_rx_gain_profile(const std::string& profile, const size_t)
+{
+ if (profile != DEFAULT_GAIN_PROFILE) {
+ throw uhd::value_error(
+ std::string("set_rx_gain_profile(): Unknown gain profile: `") + profile
+ + "'");
+ }
+}
+
+std::vector<std::string> radio_control_impl::get_tx_gain_profile_names(const size_t) const
+{
+ return {DEFAULT_GAIN_PROFILE};
+}
+
+std::vector<std::string> radio_control_impl::get_rx_gain_profile_names(const size_t) const
+{
+ return {DEFAULT_GAIN_PROFILE};
+}
+
+std::string radio_control_impl::get_tx_gain_profile(const size_t) const
+{
+ return DEFAULT_GAIN_PROFILE;
+}
+
+std::string radio_control_impl::get_rx_gain_profile(const size_t) const
+{
+ return DEFAULT_GAIN_PROFILE;
+}
+
+double radio_control_impl::set_tx_bandwidth(const double bandwidth, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _tx_bandwidth[chan] = bandwidth;
+}
+
+double radio_control_impl::set_rx_bandwidth(const double bandwidth, const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _rx_bandwidth[chan] = bandwidth;
+}
+
+//void radio_control_impl::set_time_sync(const uhd::time_spec_t& time)
+//{
+ //// FIXME
+//}
+
+std::string radio_control_impl::get_tx_antenna(const size_t chan) const
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _tx_antenna.at(chan);
+}
+
+std::string radio_control_impl::get_rx_antenna(const size_t chan) const
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _rx_antenna.at(chan);
+}
+
+std::vector<std::string> radio_control_impl::get_tx_antennas(const size_t chan) const
+{
+ RFNOC_LOG_DEBUG("get_tx_antennas(): Using default implementation.");
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return {_tx_antenna.at(chan)};
+}
+
+std::vector<std::string> radio_control_impl::get_rx_antennas(const size_t chan) const
+{
+ RFNOC_LOG_DEBUG("get_rx_antennas(): Using default implementation.");
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return {_rx_antenna.at(chan)};
+}
+
+double radio_control_impl::get_tx_frequency(const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _tx_freq.at(chan);
+}
+
+double radio_control_impl::get_rx_frequency(const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _rx_freq.at(chan);
+}
+
+uhd::freq_range_t radio_control_impl::get_tx_frequency_range(const size_t) const
+{
+ RFNOC_LOG_WARNING(
+ "get_tx_frequency_range() not implemented! Returning current rate only.");
+ uhd::freq_range_t result;
+ result.push_back(get_rate());
+ return result;
+}
+
+uhd::freq_range_t radio_control_impl::get_rx_frequency_range(const size_t) const
+{
+ RFNOC_LOG_WARNING(
+ "get_rx_frequency_range() not implemented! Returning current rate only.");
+ uhd::freq_range_t result;
+ result.push_back(get_rate());
+ return result;
+}
+
+double radio_control_impl::get_tx_gain(const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _tx_gain.at(chan);
+}
+
+double radio_control_impl::get_rx_gain(const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _rx_gain.at(chan);
+}
+
+double radio_control_impl::get_tx_gain(const std::string& name, const size_t chan)
+{
+ if (name != ALL_GAINS) {
+ throw uhd::value_error(std::string("get_tx_gain(): Unknown gain name `") + name + "'");
+ }
+ return get_tx_gain(chan);
+}
+
+double radio_control_impl::get_rx_gain(const std::string& name, const size_t chan)
+{
+ if (name != ALL_GAINS) {
+ throw uhd::value_error(std::string("get_rx_gain(): Unknown gain name `") + name + "'");
+ }
+ return get_rx_gain(chan);
+}
+
+double radio_control_impl::get_tx_bandwidth(const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _tx_bandwidth.at(chan);
+}
+
+double radio_control_impl::get_rx_bandwidth(const size_t chan)
+{
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ return _rx_bandwidth.at(chan);
+}
+
+uhd::meta_range_t radio_control_impl::get_tx_bandwidth_range(size_t chan) const
+{
+ RFNOC_LOG_DEBUG("get_tx_bandwidth_range(): Using default implementation.");
+ uhd::meta_range_t result;
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ result.push_back(_rx_bandwidth.at(chan));
+ return result;
+}
+
+uhd::meta_range_t radio_control_impl::get_rx_bandwidth_range(size_t chan) const
+{
+ RFNOC_LOG_DEBUG("get_tx_bandwidth_range(): Using default implementation.");
+ uhd::meta_range_t result;
+ std::lock_guard<std::mutex> l(_cache_mutex);
+ result.push_back(_rx_bandwidth.at(chan));
+ return result;
+}
+
+/******************************************************************************
+ * LO Default API
+ *****************************************************************************/
+std::vector<std::string> radio_control_impl::get_rx_lo_names(const size_t) const
+{
+ return {};
+}
+
+std::vector<std::string> radio_control_impl::get_rx_lo_sources(
+ const std::string&, const size_t) const
+{
+ return {"internal"};
+}
+
+uhd::freq_range_t radio_control_impl::get_rx_lo_freq_range(
+ const std::string&, const size_t) const
+{
+ return uhd::freq_range_t();
+}
+
+void radio_control_impl::set_rx_lo_source(
+ const std::string&, const std::string&, const size_t)
+{
+ throw uhd::not_implemented_error("set_rx_lo_source is not supported on this radio");
+}
+
+const std::string radio_control_impl::get_rx_lo_source(const std::string&, const size_t)
+{
+ return "internal";
+}
+
+void radio_control_impl::set_rx_lo_export_enabled(bool, const std::string&, const size_t)
+{
+ throw uhd::not_implemented_error(
+ "set_rx_lo_export_enabled is not supported on this radio");
+}
+
+bool radio_control_impl::get_rx_lo_export_enabled(const std::string&, const size_t) const
+{
+ return false;
+}
+double radio_control_impl::set_rx_lo_freq(double, const std::string&, const size_t)
+{
+ throw uhd::not_implemented_error("set_rx_lo_freq is not supported on this radio");
+}
+
+double radio_control_impl::get_rx_lo_freq(const std::string&, const size_t chan)
+{
+ return get_rx_frequency(chan);
+}
+
+std::vector<std::string> radio_control_impl::get_tx_lo_names(const size_t) const
+{
+ return {};
+}
+
+std::vector<std::string> radio_control_impl::get_tx_lo_sources(const std::string&, const size_t)
+{
+ return {"internal"};
+}
+
+uhd::freq_range_t radio_control_impl::get_tx_lo_freq_range(const std::string&, const size_t)
+{
+ return uhd::freq_range_t();
+}
+
+void radio_control_impl::set_tx_lo_source(
+ const std::string&, const std::string&, const size_t)
+{
+ throw uhd::not_implemented_error("set_tx_lo_source is not supported on this radio");
+}
+const std::string radio_control_impl::get_tx_lo_source(const std::string&, const size_t)
+{
+ return "internal";
+}
+
+void radio_control_impl::set_tx_lo_export_enabled(
+ const bool, const std::string&, const size_t)
+{
+ throw uhd::not_implemented_error(
+ "set_tx_lo_export_enabled is not supported on this radio");
+}
+
+bool radio_control_impl::get_tx_lo_export_enabled(const std::string&, const size_t)
+{
+ return false;
+}
+
+double radio_control_impl::set_tx_lo_freq(const double, const std::string&, const size_t)
+{
+ throw uhd::not_implemented_error("set_tx_lo_freq is not supported on this radio");
+}
+
+double radio_control_impl::get_tx_lo_freq(const std::string&, const size_t chan)
+{
+ return get_tx_frequency(chan);
+}
+
+/******************************************************************************
+ * Calibration-Related API Calls
+ *****************************************************************************/
+void radio_control_impl::set_tx_dc_offset(const std::complex<double>&, size_t)
+{
+ throw uhd::not_implemented_error("set_tx_dc_offset() is not supported on this radio");
+}
+
+uhd::meta_range_t radio_control_impl::get_tx_dc_offset_range(size_t) const
+{
+ return uhd::meta_range_t(0, 0);
+}
+
+void radio_control_impl::set_tx_iq_balance(const std::complex<double>&, size_t)
+{
+ throw uhd::not_implemented_error(
+ "set_tx_iq_balance() is not supported on this radio");
+}
+
+void radio_control_impl::set_rx_dc_offset(const bool enb, size_t)
+{
+ RFNOC_LOG_DEBUG("set_rx_dc_offset() has no effect on this radio");
+ if (enb) {
+ throw uhd::not_implemented_error(
+ "set_rx_dc_offset() is not supported on this radio");
+ }
+}
+
+void radio_control_impl::set_rx_dc_offset(const std::complex<double>&, size_t)
+{
+ throw uhd::not_implemented_error("set_rx_dc_offset() is not supported on this radio");
+}
+
+uhd::meta_range_t radio_control_impl::get_rx_dc_offset_range(size_t) const
+{
+ return uhd::meta_range_t(0, 0);
+}
+
+void radio_control_impl::set_rx_iq_balance(const bool enb, size_t)
+{
+ RFNOC_LOG_DEBUG("set_rx_iq_balance() has no effect on this radio");
+ if (enb) {
+ throw uhd::not_implemented_error(
+ "set_rx_iq_balance() is not supported on this radio");
+ }
+}
+
+void radio_control_impl::set_rx_iq_balance(const std::complex<double>&, size_t)
+{
+ throw uhd::not_implemented_error(
+ "set_rx_iq_balance() is not supported on this radio");
+}
+
+/******************************************************************************
+ * GPIO Controls
+ *****************************************************************************/
+std::vector<std::string> radio_control_impl::get_gpio_banks() const
+{
+ return {};
+}
+
+void radio_control_impl::set_gpio_attr(
+ const std::string&, const std::string&, const uint32_t, const uint32_t)
+{
+ throw uhd::not_implemented_error("set_gpio_attr() not implemented on this radio!");
+}
+
+uint32_t radio_control_impl::get_gpio_attr(const std::string&, const std::string&)
+{
+ throw uhd::not_implemented_error("get_gpio_attr() not implemented on this radio!");
+}
+
+/**************************************************************************
+ * Sensor API
+ *************************************************************************/
+std::vector<std::string> radio_control_impl::get_rx_sensor_names(size_t) const
+{
+ return {};
+}
+
+uhd::sensor_value_t radio_control_impl::get_rx_sensor(const std::string& name, size_t)
+{
+ throw uhd::key_error(std::string("Unknown RX sensor: ") + name);
+}
+
+std::vector<std::string> radio_control_impl::get_tx_sensor_names(size_t) const
+{
+ return {};
+}
+
+uhd::sensor_value_t radio_control_impl::get_tx_sensor(const std::string& name, size_t)
+{
+ throw uhd::key_error(std::string("Unknown TX sensor: ") + name);
+}
+
+/**************************************************************************
+ * EEPROM API
+ *************************************************************************/
+void radio_control_impl::set_db_eeprom(const uhd::eeprom_map_t&)
+{
+ throw uhd::not_implemented_error("set_db_eeprom() not implemented for this radio!");
+}
+
+uhd::eeprom_map_t radio_control_impl::get_db_eeprom()
+{
+ return {};
+}
+
+/****************************************************************************
+ * Streaming API
+ ***************************************************************************/
+void radio_control_impl::issue_stream_cmd(
+ const uhd::stream_cmd_t& stream_cmd, const size_t chan)
+{
+ // std::lock_guard<std::mutex> lock(_mutex);
+ RFNOC_LOG_TRACE("radio_control_impl::issue_stream_cmd(chan="
+ << chan << ", mode=" << char(stream_cmd.stream_mode) << ")");
+ //_continuous_streaming[chan] =
+ //(stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+
+ // calculate the command word
+ const std::unordered_map<stream_cmd_t::stream_mode_t, uint32_t, std::hash<size_t>>
+ stream_mode_to_cmd_word{
+ {stream_cmd_t::STREAM_MODE_START_CONTINUOUS, regmap::RX_CMD_CONTINUOUS},
+ {stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, regmap::RX_CMD_STOP},
+ {stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, regmap::RX_CMD_FINITE},
+ {stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, regmap::RX_CMD_FINITE}};
+ const uint32_t cmd_bits = stream_mode_to_cmd_word.at(stream_cmd.stream_mode);
+ const uint32_t cmd_word =
+ cmd_bits
+ | (uint32_t((stream_cmd.stream_now) ? 0 : 1) << regmap::RX_CMD_TIMED_POS);
+
+ if (cmd_bits == regmap::RX_CMD_FINITE) {
+ if (stream_cmd.num_samps == 0) {
+ throw uhd::value_error("When requesting a finite number of samples, the "
+ "number of samples must be greater than zero.");
+ }
+ // FIXME: The num words might be different from num_samps, check the
+ // radio width
+ const uint64_t num_words = stream_cmd.num_samps;
+ constexpr uint64_t max_num_words = 0x00FFFFFFFFFFFF; // 48 bits
+ if (num_words > max_num_words) {
+ RFNOC_LOG_ERROR("Requesting too many samples in a single burst! "
+ "Requested "
+ + std::to_string(stream_cmd.num_samps)
+ + ", maximum "
+ "is "
+ + std::to_string(max_num_words) + "."); // FIXME
+ RFNOC_LOG_INFO(
+ "Note that a decimation block will increase the number of samples "
+ "per burst by the decimation factor. Your application may have "
+ "requested fewer samples.");
+ throw uhd::value_error("Requested too many samples in a single burst.");
+ }
+ regs().poke32(
+ get_addr(regmap::REG_RX_CMD_NUM_WORDS_HI, chan), uint32_t(num_words >> 32));
+ regs().poke32(get_addr(regmap::REG_RX_CMD_NUM_WORDS_LO, chan),
+ uint32_t(num_words & 0xFFFFFFFF));
+ }
+ if (!stream_cmd.stream_now) {
+ const uint64_t ticks = stream_cmd.time_spec.to_ticks(get_tick_rate());
+ regs().poke32(get_addr(regmap::REG_RX_CMD_TIME_HI, chan), uint32_t(ticks >> 32));
+ regs().poke32(get_addr(regmap::REG_RX_CMD_TIME_LO, chan), uint32_t(ticks >> 0));
+ }
+ regs().poke32(get_addr(regmap::REG_RX_CMD, chan), cmd_word);
+}
+
+/******************************************************************************
+ * Private methods
+ *****************************************************************************/
+void radio_control_impl::async_message_handler(
+ uint32_t addr, const std::vector<uint32_t>& data)
+{
+ RFNOC_LOG_TRACE(
+ str(boost::format("Received async message to addr 0x%08X, data length %d words.")
+ % addr % data.size()));
+}
diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt
index bd0964def..d443a4db1 100644
--- a/host/tests/CMakeLists.txt
+++ b/host/tests/CMakeLists.txt
@@ -249,6 +249,7 @@ UHD_ADD_NONAPI_TEST(
${CMAKE_SOURCE_DIR}/lib/rfnoc/block_id.cpp
${CMAKE_SOURCE_DIR}/lib/utils/compat_check.cpp
${CMAKE_SOURCE_DIR}/lib/rfnoc/ddc_block_control.cpp
+ ${CMAKE_SOURCE_DIR}/lib/rfnoc/radio_control_impl.cpp
${CMAKE_SOURCE_DIR}/lib/usrp/cores/dsp_core_utils.cpp
)