diff options
author | Martin Braun <martin.braun@ettus.com> | 2019-05-31 15:08:27 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:22 -0800 |
commit | 6730a0b72b3a6a5b1b94a5d09bfb395d837672da (patch) | |
tree | 643ee003afd92489d8d0bad217e5e3a8655693ca /host | |
parent | 911b89e4f6e2df66d355448c452af0b2fc058fe6 (diff) | |
download | uhd-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.txt | 1 | ||||
-rw-r--r-- | host/include/uhd/rfnoc/radio_control.hpp | 688 | ||||
-rw-r--r-- | host/lib/include/uhdlib/rfnoc/radio_control_impl.hpp | 289 | ||||
-rw-r--r-- | host/lib/rfnoc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/rfnoc/radio_control_impl.cpp | 771 | ||||
-rw-r--r-- | host/tests/CMakeLists.txt | 1 |
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 ) |