diff options
Diffstat (limited to 'host/lib/usrp/dboard/magnesium')
12 files changed, 1780 insertions, 1671 deletions
diff --git a/host/lib/usrp/dboard/magnesium/CMakeLists.txt b/host/lib/usrp/dboard/magnesium/CMakeLists.txt index 1029bfacd..df5fe765a 100644 --- a/host/lib/usrp/dboard/magnesium/CMakeLists.txt +++ b/host/lib/usrp/dboard/magnesium/CMakeLists.txt @@ -8,10 +8,10 @@ # set to true. list(APPEND MAGNESIUM_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_ctrl_impl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_ctrl_init.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_ctrl_cpld.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_ctrl_gain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_control.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_control_init.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_control_cpld.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_radio_control_gain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_ad9371_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_bands.cpp ${CMAKE_CURRENT_SOURCE_DIR}/magnesium_cpld_ctrl.cpp diff --git a/host/lib/usrp/dboard/magnesium/magnesium_bands.cpp b/host/lib/usrp/dboard/magnesium/magnesium_bands.cpp index 13cc52d49..ef72aee95 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_bands.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_bands.cpp @@ -7,7 +7,7 @@ // The band plan #include "magnesium_constants.hpp" -#include "magnesium_radio_ctrl_impl.hpp" +#include "magnesium_radio_control.hpp" #include <uhd/utils/math.hpp> /* @@ -93,11 +93,10 @@ namespace { } // namespace - -magnesium_radio_ctrl_impl::rx_band magnesium_radio_ctrl_impl::_map_freq_to_rx_band( +magnesium_radio_control_impl::rx_band magnesium_radio_control_impl::_map_freq_to_rx_band( const band_map_t band_map, const double freq) { - magnesium_radio_ctrl_impl::rx_band band; + magnesium_radio_control_impl::rx_band band; if (fp_compare_epsilon<double>(freq) < MAGNESIUM_MIN_FREQ) { band = rx_band::INVALID_BAND; @@ -124,10 +123,10 @@ magnesium_radio_ctrl_impl::rx_band magnesium_radio_ctrl_impl::_map_freq_to_rx_ba return band; } -magnesium_radio_ctrl_impl::tx_band magnesium_radio_ctrl_impl::_map_freq_to_tx_band( +magnesium_radio_control_impl::tx_band magnesium_radio_control_impl::_map_freq_to_tx_band( const band_map_t band_map, const double freq) { - magnesium_radio_ctrl_impl::tx_band band; + magnesium_radio_control_impl::tx_band band; if (fp_compare_epsilon<double>(freq) < MAGNESIUM_MIN_FREQ) { band = tx_band::INVALID_BAND; diff --git a/host/lib/usrp/dboard/magnesium/magnesium_constants.hpp b/host/lib/usrp/dboard/magnesium/magnesium_constants.hpp index 9b3bdf800..7d98bca91 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_constants.hpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_constants.hpp @@ -69,6 +69,13 @@ static constexpr char MAGNESIUM_GAIN2[] = "dsa"; //! Amplifier gain static constexpr char MAGNESIUM_AMP[] = "amp"; +static constexpr char MAGNESIUM_FE_NAME[] = "Magnesium"; + +static constexpr char MAGNESIUM_DEFAULT_RX_ANTENNA[] = "RX2"; +static constexpr char MAGNESIUM_DEFAULT_TX_ANTENNA[] = "TX/RX"; + +static constexpr char MAGNESIUM_FPGPIO_BANK[] = "FP0"; + // Note: MAGNESIUM_NUM_CHANS is independent of the number of chans per // RFNoC block. TODO: When we go to one radio per dboard, this comment can // be deleted. @@ -79,4 +86,20 @@ static constexpr double MAGNESIUM_TX_IF_FREQ = 1.95e9; //! Max time we allow for a call to set_freq() to take static constexpr size_t MAGNESIUM_TUNE_TIMEOUT = 15000; // milliseconds +//! Magnesium gain profile options +static const std::vector<std::string> MAGNESIUM_GP_OPTIONS = {"manual", + "default", + "default_rf_filter_bypass_always_on", + "default_rf_filter_bypass_always_off"}; + +namespace n310_regs { + +constexpr uint32_t DB_GPIO_BASE = 0x80000; // FIXME +constexpr uint32_t DB_GPIO_RB = 0x80000; // FIXME +constexpr uint32_t DB_GPIO_OFFSET = 0x100; // FIXME +constexpr uint32_t FP_GPIO = 0x80000; // FIXME +constexpr uint32_t RB_FP_GPIO = 0x80000; // FIXME + +} + #endif /* INCLUDED_LIBUHD_MAGNESIUM_CONSTANTS_HPP */ diff --git a/host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp b/host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp index 67b20f5fa..3e513218a 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_gain_table.cpp @@ -15,8 +15,8 @@ using namespace uhd::rfnoc; using namespace magnesium; namespace { -typedef magnesium_radio_ctrl_impl::rx_band rx_band; -typedef magnesium_radio_ctrl_impl::tx_band tx_band; +typedef magnesium_radio_control_impl::rx_band rx_band; +typedef magnesium_radio_control_impl::tx_band tx_band; const size_t TX_LOWBAND = 0; const size_t TX_HIGHBAND = 1; @@ -454,7 +454,7 @@ gain_tuple_t fine_tune_ad9371_att(const gain_tuple_t gain_tuple, const double ga gain_tuple_t magnesium::get_rx_gain_tuple( - const double gain_index, const magnesium_radio_ctrl_impl::rx_band band) + const double gain_index, const magnesium_radio_control_impl::rx_band band) { UHD_ASSERT_THROW(gain_index <= ALL_RX_MAX_GAIN and gain_index >= ALL_RX_MIN_GAIN); auto& gain_table = rx_gain_tables.at(map_rx_band(band)); @@ -463,7 +463,7 @@ gain_tuple_t magnesium::get_rx_gain_tuple( } gain_tuple_t magnesium::get_tx_gain_tuple( - const double gain_index, const magnesium_radio_ctrl_impl::tx_band band) + const double gain_index, const magnesium_radio_control_impl::tx_band band) { UHD_ASSERT_THROW(gain_index <= ALL_TX_MAX_GAIN and gain_index >= ALL_TX_MIN_GAIN); auto& gain_table = tx_gain_tables.at(map_tx_band(band)); diff --git a/host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp b/host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp index 8769b44ac..6ba91a248 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_gain_table.hpp @@ -7,7 +7,7 @@ #ifndef INCLUDED_LIBUHD_MAGNESIUM_GAIN_TABLE_HPP #define INCLUDED_LIBUHD_MAGNESIUM_GAIN_TABLE_HPP -#include "magnesium_radio_ctrl_impl.hpp" +#include "magnesium_radio_control.hpp" #include <uhd/types/direction.hpp> namespace magnesium { @@ -31,12 +31,12 @@ struct gain_tuple_t /*! Given a gain index, return a tuple of gain-related settings (Rx) */ gain_tuple_t get_rx_gain_tuple( - const double gain_index, const uhd::rfnoc::magnesium_radio_ctrl_impl::rx_band band_); + const double gain_index, const uhd::rfnoc::magnesium_radio_control_impl::rx_band band_); /*! Given a gain index, return a tuple of gain-related settings (Tx) */ gain_tuple_t get_tx_gain_tuple( - const double gain_index, const uhd::rfnoc::magnesium_radio_ctrl_impl::tx_band band_); + const double gain_index, const uhd::rfnoc::magnesium_radio_control_impl::tx_band band_); } /* namespace magnesium */ diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp new file mode 100644 index 000000000..dc78cee7d --- /dev/null +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.cpp @@ -0,0 +1,1151 @@ +// +// Copyright 2017 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "magnesium_radio_control.hpp" +#include "magnesium_constants.hpp" +#include "magnesium_gain_table.hpp" +#include <uhd/exception.hpp> +#include <uhd/rfnoc/node_ctrl_base.hpp> +#include <uhd/rfnoc/registry.hpp> +#include <uhd/transport/chdr.hpp> +#include <uhd/types/direction.hpp> +#include <uhd/types/eeprom.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/math.hpp> +#include <uhd/rfnoc/registry.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/format.hpp> +#include <boost/make_shared.hpp> +#include <cmath> +#include <cstdlib> +#include <sstream> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::rfnoc; +using namespace uhd::math::fp_compare; + +namespace { + +/************************************************************************** + * ADF4351 Controls + *************************************************************************/ +/*! + * \param lo_iface Reference to the LO object + * \param freq Frequency (in Hz) of the tone to be generated from the LO + * \param ref_clock_freq Frequency (in Hz) of the reference clock at the + * PLL input of the LO + * \param int_n_mode Integer-N mode on or off + */ +double _lo_set_frequency(adf435x_iface::sptr lo_iface, + const double freq, + const double ref_clock_freq, + const bool int_n_mode) +{ + UHD_LOG_TRACE("MG/ADF4351", + "Attempting to tune low band LO to " << freq << " Hz with ref clock freq " + << ref_clock_freq); + lo_iface->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED); + lo_iface->set_reference_freq(ref_clock_freq); + lo_iface->set_prescaler(adf435x_iface::PRESCALER_4_5); + const double actual_freq = lo_iface->set_frequency(freq, int_n_mode); + lo_iface->set_output_power( + adf435x_iface::RF_OUTPUT_A, adf435x_iface::OUTPUT_POWER_2DBM); + lo_iface->set_output_power( + adf435x_iface::RF_OUTPUT_B, adf435x_iface::OUTPUT_POWER_2DBM); + lo_iface->set_charge_pump_current(adf435x_iface::CHARGE_PUMP_CURRENT_0_31MA); + return actual_freq; +} + +/*! Configure and enable LO + * + * Will tune it to requested frequency and enable outputs. + * + * \param lo_iface Reference to the LO object + * \param lo_freq Frequency (in Hz) of the tone to be generated from the LO + * \param ref_clock_freq Frequency (in Hz) of the reference clock at the + * PLL input of the LO + * \param int_n_mode Integer-N mode on or off + * \returns the actual frequency the LO is running at + */ +double _lo_enable(adf435x_iface::sptr lo_iface, + const double lo_freq, + const double ref_clock_freq, + const bool int_n_mode) +{ + const double actual_lo_freq = + _lo_set_frequency(lo_iface, lo_freq, ref_clock_freq, int_n_mode); + lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_A, true); + lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_B, true); + lo_iface->commit(); + return actual_lo_freq; +} + +/*! Disable LO + */ +void _lo_disable(adf435x_iface::sptr lo_iface) +{ + lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_A, false); + lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_B, false); + lo_iface->commit(); +} +} // namespace + + +/****************************************************************************** + * Structors + *****************************************************************************/ +magnesium_radio_control_impl::magnesium_radio_control_impl(make_args_ptr make_args) + : radio_control_impl(std::move(make_args)) +{ + RFNOC_LOG_TRACE("Entering magnesium_radio_control_impl ctor..."); + UHD_ASSERT_THROW(get_block_id().get_block_count() < 2); + const char radio_slot_name[2] = {'A', 'B'}; + _radio_slot = radio_slot_name[get_block_id().get_block_count()]; + RFNOC_LOG_TRACE("Radio slot: " << _radio_slot); + _rpc_prefix = (_radio_slot == "A") ? "db_0_" : "db_1_"; + UHD_ASSERT_THROW(get_num_input_ports() == MAGNESIUM_NUM_CHANS); + UHD_ASSERT_THROW(get_num_output_ports() == MAGNESIUM_NUM_CHANS); + UHD_ASSERT_THROW(get_mb_controller()); + _n310_mb_control = std::dynamic_pointer_cast<mpmd_mb_controller>(get_mb_controller()); + UHD_ASSERT_THROW(_n310_mb_control); + _n3xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>( + _n310_mb_control->get_timekeeper(0)); + UHD_ASSERT_THROW(_n3xx_timekeeper); + _rpcc = _n310_mb_control->get_rpc_client(); + UHD_ASSERT_THROW(_rpcc); + + _init_defaults(); + _init_mpm(); + _init_peripherals(); + _init_prop_tree(); +} + +magnesium_radio_control_impl::~magnesium_radio_control_impl() +{ + RFNOC_LOG_TRACE("magnesium_radio_control_impl::dtor() "); +} + + +/****************************************************************************** + * API Calls + *****************************************************************************/ +double magnesium_radio_control_impl::set_rate(double requested_rate) +{ + meta_range_t rates; + for (const double rate : MAGNESIUM_RADIO_RATES) { + rates.push_back(range_t(rate)); + } + + const double rate = rates.clip(requested_rate); + if (!math::frequencies_are_equal(requested_rate, rate)) { + RFNOC_LOG_WARNING("Coercing requested sample rate from " + << (requested_rate / 1e6) << " to " << (rate / 1e6)); + } + + const double current_rate = get_tick_rate(); + if (math::frequencies_are_equal(current_rate, rate)) { + RFNOC_LOG_DEBUG("Rate is already at " << rate << " MHz. Skipping set_rate()"); + return current_rate; + } + + std::lock_guard<std::mutex> l(_set_lock); + // Now commit to device. First, disable LOs. + _lo_disable(_tx_lo); + _lo_disable(_rx_lo); + _master_clock_rate = _ad9371->set_master_clock_rate(rate); + _n3xx_timekeeper->update_tick_rate(_master_clock_rate); + radio_control_impl::set_rate(_master_clock_rate); + // Frequency settings apply to both channels, no loop needed. Will also + // re-enable the lowband LOs if they were used. + set_rx_frequency(get_rx_frequency(0), 0); + set_tx_frequency(get_tx_frequency(0), 0); + // Gain and bandwidth need to be looped: + for (size_t radio_idx = 0; radio_idx < MAGNESIUM_NUM_CHANS; radio_idx++) { + set_rx_gain(radio_control_impl::get_rx_gain(radio_idx), radio_idx); + set_tx_gain(radio_control_impl::get_rx_gain(radio_idx), radio_idx); + set_rx_bandwidth(get_rx_bandwidth(radio_idx), radio_idx); + set_tx_bandwidth(get_tx_bandwidth(radio_idx), radio_idx); + } + set_tick_rate(_master_clock_rate); + return _master_clock_rate; +} + +void magnesium_radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan) +{ + if (ant != get_tx_antenna(chan)) { + throw uhd::value_error( + str(boost::format("[%s] Requesting invalid TX antenna value: %s") + % get_unique_id() % ant)); + } + // We can't actually set the TX antenna, so let's stop here. +} + +void magnesium_radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan) +{ + UHD_ASSERT_THROW(chan <= MAGNESIUM_NUM_CHANS); + if (std::find(MAGNESIUM_RX_ANTENNAS.begin(), MAGNESIUM_RX_ANTENNAS.end(), ant) + == MAGNESIUM_RX_ANTENNAS.end()) { + throw uhd::value_error( + str(boost::format("[%s] Requesting invalid RX antenna value: %s") + % get_unique_id() % ant)); + } + RFNOC_LOG_TRACE("Setting RX antenna to " << ant << " for chan " << chan); + magnesium_cpld_ctrl::chan_sel_t chan_sel = chan == 0 ? magnesium_cpld_ctrl::CHAN1 + : magnesium_cpld_ctrl::CHAN2; + _update_atr_switches(chan_sel, RX_DIRECTION, ant); + + radio_control_impl::set_rx_antenna(ant, chan); +} + +double magnesium_radio_control_impl::set_tx_frequency(const double req_freq, const size_t chan) +{ + const double freq = MAGNESIUM_FREQ_RANGE.clip(req_freq); + RFNOC_LOG_TRACE("set_tx_frequency(f=" << freq << ", chan=" << chan << ")"); + _desired_rf_freq[TX_DIRECTION] = freq; + std::lock_guard<std::mutex> l(_set_lock); + // We need to set the switches on both channels, because they share an LO. + // This way, if we tune channel 0 it will not put channel 1 into a bad + // state. + _update_tx_freq_switches(freq, _tx_bypass_amp, magnesium_cpld_ctrl::BOTH); + const std::string ad9371_source = this->get_tx_lo_source(MAGNESIUM_LO1, chan); + const std::string adf4351_source = this->get_tx_lo_source(MAGNESIUM_LO2, chan); + UHD_ASSERT_THROW(adf4351_source == "internal"); + double coerced_if_freq = freq; + + if (_map_freq_to_tx_band(_tx_band_map, freq) == tx_band::LOWBAND) { + _is_low_band[TX_DIRECTION] = true; + const double desired_low_freq = MAGNESIUM_TX_IF_FREQ - freq; + coerced_if_freq = + this->_set_tx_lo_freq(adf4351_source, MAGNESIUM_LO2, desired_low_freq, chan) + + freq; + RFNOC_LOG_TRACE("coerced_if_freq = " << coerced_if_freq); + } else { + _is_low_band[TX_DIRECTION] = false; + _lo_disable(_tx_lo); + } + // external LO required to tune at 2xdesired_frequency. + const double desired_if_freq = (ad9371_source == "internal") ? coerced_if_freq + : 2 * coerced_if_freq; + + this->_set_tx_lo_freq(ad9371_source, MAGNESIUM_LO1, desired_if_freq, chan); + this->_update_freq(chan, TX_DIRECTION); + this->_update_gain(chan, TX_DIRECTION); + return radio_control_impl::get_tx_frequency(chan); +} + +void magnesium_radio_control_impl::_update_gain(const size_t chan, const uhd::direction_t dir) +{ + const std::string fe = (dir == TX_DIRECTION) ? "tx_frontends" : "rx_frontends"; + const double freq = (dir == TX_DIRECTION) ? this->get_tx_frequency(chan) + : this->get_rx_frequency(chan); + this->_set_all_gain(this->_get_all_gain(chan, dir), freq, chan, dir); +} + +void magnesium_radio_control_impl::_update_freq(const size_t chan, const uhd::direction_t dir) +{ + const std::string ad9371_source = dir == TX_DIRECTION + ? this->get_tx_lo_source(MAGNESIUM_LO1, chan) + : this->get_rx_lo_source(MAGNESIUM_LO1, chan); + + const double ad9371_freq = ad9371_source == "external" ? _ad9371_freq[dir] / 2 + : _ad9371_freq[dir]; + const double rf_freq = _is_low_band[dir] ? ad9371_freq - _adf4351_freq[dir] + : ad9371_freq; + + RFNOC_LOG_TRACE("RF freq = " << rf_freq); + UHD_ASSERT_THROW(fp_compare_epsilon<double>(rf_freq) >= 0); + UHD_ASSERT_THROW(fp_compare_epsilon<double>(std::abs(rf_freq - _desired_rf_freq[dir])) + <= _master_clock_rate / 2); + if (dir == RX_DIRECTION) { + radio_control_impl::set_rx_frequency(rf_freq, chan); + } else if (dir == TX_DIRECTION) { + radio_control_impl::set_tx_frequency(rf_freq, chan); + } else { + UHD_THROW_INVALID_CODE_PATH(); + } +} + +double magnesium_radio_control_impl::set_rx_frequency(const double req_freq, const size_t chan) +{ + const double freq = MAGNESIUM_FREQ_RANGE.clip(req_freq); + RFNOC_LOG_TRACE("set_rx_frequency(f=" << freq << ", chan=" << chan << ")"); + _desired_rf_freq[RX_DIRECTION] = freq; + std::lock_guard<std::mutex> l(_set_lock); + // We need to set the switches on both channels, because they share an LO. + // This way, if we tune channel 0 it will not put channel 1 into a bad + // state. + _update_rx_freq_switches(freq, _rx_bypass_lnas, magnesium_cpld_ctrl::BOTH); + const std::string ad9371_source = this->get_rx_lo_source(MAGNESIUM_LO1, chan); + const std::string adf4351_source = this->get_rx_lo_source(MAGNESIUM_LO2, chan); + UHD_ASSERT_THROW(adf4351_source == "internal"); + double coerced_if_freq = freq; + + if (_map_freq_to_rx_band(_rx_band_map, freq) == rx_band::LOWBAND) { + _is_low_band[RX_DIRECTION] = true; + const double desired_low_freq = MAGNESIUM_RX_IF_FREQ - freq; + coerced_if_freq = + this->_set_rx_lo_freq(adf4351_source, MAGNESIUM_LO2, desired_low_freq, chan) + + freq; + RFNOC_LOG_TRACE("coerced_if_freq = " << coerced_if_freq); + } else { + _is_low_band[RX_DIRECTION] = false; + _lo_disable(_rx_lo); + } + // external LO required to tune at 2xdesired_frequency. + const double desired_if_freq = ad9371_source == "internal" ? coerced_if_freq + : 2 * coerced_if_freq; + + this->_set_rx_lo_freq(ad9371_source, MAGNESIUM_LO1, desired_if_freq, chan); + + this->_update_freq(chan, RX_DIRECTION); + this->_update_gain(chan, RX_DIRECTION); + + return radio_control_impl::get_rx_frequency(chan); +} + +double magnesium_radio_control_impl::set_rx_bandwidth( + const double bandwidth, const size_t chan) +{ + std::lock_guard<std::mutex> l(_set_lock); + _ad9371->set_bandwidth(bandwidth, chan, RX_DIRECTION); + // FIXME: setting analog bandwidth on AD9371 take no effect. + // Remove this warning when ADI can confirm that it works. + RFNOC_LOG_WARNING("set_rx_bandwidth take no effect on AD9371. " + "Default analog bandwidth is 100MHz"); + return AD9371_RX_MAX_BANDWIDTH; +} + +double magnesium_radio_control_impl::set_tx_bandwidth( + const double bandwidth, const size_t chan) +{ + std::lock_guard<std::mutex> l(_set_lock); + _ad9371->set_bandwidth(bandwidth, chan, TX_DIRECTION); + // FIXME: setting analog bandwidth on AD9371 take no effect. + // Remove this warning when ADI can confirm that it works. + RFNOC_LOG_WARNING("set_tx_bandwidth take no effect on AD9371. " + "Default analog bandwidth is 100MHz"); + return AD9371_TX_MAX_BANDWIDTH; +} + +void magnesium_radio_control_impl::set_tx_gain_profile( + const std::string& profile, const size_t) +{ + if (std::find( + MAGNESIUM_GP_OPTIONS.begin(), MAGNESIUM_GP_OPTIONS.end(), profile) + == MAGNESIUM_GP_OPTIONS.end()) { + RFNOC_LOG_ERROR("Invalid TX gain profile: " << profile); + throw uhd::key_error("Invalid TX gain profile!"); + } + _gain_profile[TX_DIRECTION] = profile; +} + +void magnesium_radio_control_impl::set_rx_gain_profile( + const std::string& profile, const size_t) +{ + if (std::find( + MAGNESIUM_GP_OPTIONS.begin(), MAGNESIUM_GP_OPTIONS.end(), profile) + == MAGNESIUM_GP_OPTIONS.end()) { + RFNOC_LOG_ERROR("Invalid RX gain profile: " << profile); + throw uhd::key_error("Invalid RX gain profile!"); + } + _gain_profile[RX_DIRECTION] = profile; +} + +double magnesium_radio_control_impl::set_tx_gain(const double gain, const size_t chan) +{ + std::lock_guard<std::mutex> l(_set_lock); + RFNOC_LOG_TRACE("set_tx_gain(gain=" << gain << ", chan=" << chan << ")"); + const double coerced_gain = + _set_all_gain(gain, this->get_tx_frequency(chan), chan, TX_DIRECTION); + radio_control_impl::set_tx_gain(coerced_gain, chan); + return coerced_gain; +} + +double magnesium_radio_control_impl::_set_tx_gain( + const std::string& name, const double gain, const size_t chan) +{ + std::lock_guard<std::mutex> l(_set_lock); + RFNOC_LOG_TRACE( + "_set_tx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")"); + RFNOC_LOG_TRACE( + "_set_tx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")"); + double clip_gain = 0; + if (name == MAGNESIUM_GAIN1) { + clip_gain = uhd::clip(gain, AD9371_MIN_TX_GAIN, AD9371_MAX_TX_GAIN); + _ad9371_att[TX_DIRECTION] = clip_gain; + } else if (name == MAGNESIUM_GAIN2) { + clip_gain = uhd::clip(gain, DSA_MIN_GAIN, DSA_MAX_GAIN); + _dsa_att[TX_DIRECTION] = clip_gain; + } else if (name == MAGNESIUM_AMP) { + clip_gain = gain > 0.0 ? AMP_MAX_GAIN : AMP_MIN_GAIN; + _amp_bypass[TX_DIRECTION] = clip_gain == 0.0; + } else { + throw uhd::value_error("Could not find gain element " + name); + } + RFNOC_LOG_TRACE("_set_tx_gain calling update gain"); + this->_set_all_gain(this->_get_all_gain(chan, TX_DIRECTION), + this->get_tx_frequency(chan), + chan, + TX_DIRECTION); + return clip_gain; +} + +double magnesium_radio_control_impl::_get_tx_gain( + const std::string& name, const size_t /*chan*/ +) +{ + std::lock_guard<std::mutex> l(_set_lock); + if (name == MAGNESIUM_GAIN1) { + return _ad9371_att[TX_DIRECTION]; + } else if (name == MAGNESIUM_GAIN2) { + return _dsa_att[TX_DIRECTION]; + } else if (name == MAGNESIUM_AMP) { + return _amp_bypass[TX_DIRECTION] ? AMP_MIN_GAIN : AMP_MAX_GAIN; + } else { + throw uhd::value_error("Could not find gain element " + name); + } +} + +double magnesium_radio_control_impl::set_rx_gain(const double gain, const size_t chan) +{ + std::lock_guard<std::mutex> l(_set_lock); + RFNOC_LOG_TRACE("set_rx_gain(gain=" << gain << ", chan=" << chan << ")"); + const double coerced_gain = + _set_all_gain(gain, this->get_rx_frequency(chan), chan, RX_DIRECTION); + radio_control_impl::set_rx_gain(coerced_gain, chan); + return coerced_gain; +} + +double magnesium_radio_control_impl::_set_rx_gain( + const std::string& name, const double gain, const size_t chan) +{ + std::lock_guard<std::mutex> l(_set_lock); + RFNOC_LOG_TRACE( + "_set_rx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")"); + double clip_gain = 0; + if (name == MAGNESIUM_GAIN1) { + clip_gain = uhd::clip(gain, AD9371_MIN_RX_GAIN, AD9371_MAX_RX_GAIN); + _ad9371_att[RX_DIRECTION] = clip_gain; + } else if (name == MAGNESIUM_GAIN2) { + clip_gain = uhd::clip(gain, DSA_MIN_GAIN, DSA_MAX_GAIN); + _dsa_att[RX_DIRECTION] = clip_gain; + } else if (name == MAGNESIUM_AMP) { + clip_gain = gain > 0.0 ? AMP_MAX_GAIN : AMP_MIN_GAIN; + _amp_bypass[RX_DIRECTION] = clip_gain == 0.0; + } else { + throw uhd::value_error("Could not find gain element " + name); + } + RFNOC_LOG_TRACE("_set_rx_gain calling update gain"); + this->_set_all_gain(this->_get_all_gain(chan, RX_DIRECTION), + this->get_rx_frequency(chan), + chan, + RX_DIRECTION); + return clip_gain; // not really any coerced here (only clip) for individual gain +} + +double magnesium_radio_control_impl::_get_rx_gain( + const std::string& name, const size_t /*chan*/ +) +{ + std::lock_guard<std::mutex> l(_set_lock); + + if (name == MAGNESIUM_GAIN1) { + return _ad9371_att[RX_DIRECTION]; + } else if (name == MAGNESIUM_GAIN2) { + return _dsa_att[RX_DIRECTION]; + } else if (name == MAGNESIUM_AMP) { + return _amp_bypass[RX_DIRECTION] ? AMP_MIN_GAIN : AMP_MAX_GAIN; + } else { + throw uhd::value_error("Could not find gain element " + name); + } +} + +double magnesium_radio_control_impl::set_tx_gain( + const double gain, const std::string& name, const size_t chan) +{ + if (get_tx_gain_profile(chan) == "manual") { + if (name == "all" || name == ALL_GAINS) { + RFNOC_LOG_ERROR("Setting overall gain is not supported in manual gain mode!"); + throw uhd::key_error( + "Setting overall gain is not supported in manual gain mode!"); + } + if (name != MAGNESIUM_GAIN1 && name != MAGNESIUM_GAIN2 && name != MAGNESIUM_AMP) { + RFNOC_LOG_ERROR("Invalid TX gain name: " << name); + throw uhd::key_error("Invalid TX gain name!"); + } + const double coerced_gain = get_tx_gain_range(name, chan).clip(gain, true); + if (name == MAGNESIUM_GAIN1) { + _ad9371_att[TX_DIRECTION] = AD9371_MAX_TX_GAIN - coerced_gain; + } else if (name == MAGNESIUM_GAIN2) { + _dsa_set_att(AD9371_MAX_TX_GAIN - coerced_gain, chan, TX_DIRECTION); + } else if (name == MAGNESIUM_AMP) { + _amp_bypass[TX_DIRECTION] = (coerced_gain == AMP_MIN_GAIN); + } else { + throw uhd::value_error("Could not find gain element " + name); + } + _set_all_gain(coerced_gain /* this value doesn't actuall matter */, + get_tx_frequency(chan), + chan, + TX_DIRECTION); + return coerced_gain; + } + + if (name == "all" || name == ALL_GAINS) { + return set_tx_gain(gain, chan); + } + RFNOC_LOG_ERROR("Setting individual TX gains is only supported in manual gain mode!"); + throw uhd::key_error( + "Setting individual TX gains is only supported in manual gain mode!"); +} + +double magnesium_radio_control_impl::set_rx_gain( + const double gain, const std::string& name, const size_t chan) +{ + if (get_rx_gain_profile(chan) == "manual") { + if (name == "all" || name == ALL_GAINS) { + RFNOC_LOG_ERROR("Setting overall gain is not supported in manual gain mode!"); + throw uhd::key_error( + "Setting overall gain is not supported in manual gain mode!"); + } + if (name != MAGNESIUM_GAIN1 && name != MAGNESIUM_GAIN2 && name != MAGNESIUM_AMP) { + RFNOC_LOG_ERROR("Invalid RX gain name: " << name); + throw uhd::key_error("Invalid RX gain name!"); + } + const double coerced_gain = get_rx_gain_range(name, chan).clip(gain, true); + if (name == MAGNESIUM_GAIN1) { + _ad9371_att[RX_DIRECTION] = AD9371_MAX_RX_GAIN - coerced_gain; + } else if (name == MAGNESIUM_GAIN2) { + _dsa_set_att(AD9371_MAX_RX_GAIN - coerced_gain, chan, RX_DIRECTION); + } else if (name == MAGNESIUM_AMP) { + _amp_bypass[RX_DIRECTION] = (coerced_gain == AMP_MIN_GAIN); + } else { + throw uhd::value_error("Could not find gain element " + name); + } + _set_all_gain(coerced_gain /* this value doesn't actuall matter */, + get_rx_frequency(chan), + chan, + RX_DIRECTION); + return coerced_gain; + } + + if (name == "all" || name == ALL_GAINS) { + return set_rx_gain(gain, chan); + } + RFNOC_LOG_ERROR("Setting individual RX gains is only supported in manual gain mode!"); + throw uhd::key_error( + "Setting individual RX gains is only supported in manual gain mode!"); +} + +std::vector<std::string> magnesium_radio_control_impl::get_tx_antennas(const size_t) const +{ + return {"TX/RX"}; +} + +std::vector<std::string> magnesium_radio_control_impl::get_rx_antennas(const size_t) const +{ + return MAGNESIUM_RX_ANTENNAS; +} + +uhd::freq_range_t magnesium_radio_control_impl::get_tx_frequency_range(const size_t) const +{ + return meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0); +} + +uhd::freq_range_t magnesium_radio_control_impl::get_rx_frequency_range(const size_t) const +{ + return meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0); +} + +std::vector<std::string> magnesium_radio_control_impl::get_tx_gain_names(const size_t) const +{ + return {MAGNESIUM_GAIN1, MAGNESIUM_GAIN2, MAGNESIUM_AMP}; +} + +std::vector<std::string> magnesium_radio_control_impl::get_rx_gain_names(const size_t) const +{ + return {MAGNESIUM_GAIN1, MAGNESIUM_GAIN2, MAGNESIUM_AMP}; +} + +double magnesium_radio_control_impl::get_tx_gain( + const std::string& name, const size_t chan) +{ + if (name == MAGNESIUM_GAIN1 || name == MAGNESIUM_GAIN2 || name == MAGNESIUM_AMP) { + return _get_tx_gain(name, chan); + } + if (name == "all" || name == ALL_GAINS) { + return radio_control_impl::get_tx_gain(chan); + } + RFNOC_LOG_ERROR("Invalid TX gain name: " << name); + throw uhd::key_error("Invalid TX gain name!"); +} + +double magnesium_radio_control_impl::get_rx_gain( + const std::string& name, const size_t chan) +{ + if (name == MAGNESIUM_GAIN1 || name == MAGNESIUM_GAIN2 || name == MAGNESIUM_AMP) { + return _get_rx_gain(name, chan); + } + if (name == "all" || name == ALL_GAINS) { + return radio_control_impl::get_rx_gain(chan); + } + RFNOC_LOG_ERROR("Invalid RX gain name: " << name); + throw uhd::key_error("Invalid RX gain name!"); +} + +uhd::gain_range_t magnesium_radio_control_impl::get_tx_gain_range(const size_t chan) const +{ + if (get_tx_gain_profile(chan) == "manual") { + return meta_range_t(0.0, 0.0, 0.0); + } + return meta_range_t(ALL_TX_MIN_GAIN, ALL_TX_MAX_GAIN, ALL_TX_GAIN_STEP); +} + +uhd::gain_range_t magnesium_radio_control_impl::get_tx_gain_range( + const std::string& name, const size_t chan) const +{ + if (get_tx_gain_profile(chan) == "manual") { + if (name == "all" || name == ALL_GAINS) { + return meta_range_t(0.0, 0.0, 0.0); + } + if (name == MAGNESIUM_GAIN1) { + return meta_range_t( + AD9371_MIN_TX_GAIN, AD9371_MAX_TX_GAIN, AD9371_TX_GAIN_STEP); + } + if (name == MAGNESIUM_GAIN2) { + return meta_range_t(DSA_MIN_GAIN, DSA_MAX_GAIN, DSA_GAIN_STEP); + } + if (name == MAGNESIUM_AMP) { + return meta_range_t(AMP_MIN_GAIN, AMP_MAX_GAIN, AMP_GAIN_STEP); + } + RFNOC_LOG_ERROR("Invalid TX gain name: " << name); + throw uhd::key_error("Invalid TX gain name!"); + } + if (name == "all" || name == ALL_GAINS) { + return get_tx_gain_range(chan); + } + if (name == MAGNESIUM_GAIN1 || name == MAGNESIUM_GAIN2 || name == MAGNESIUM_AMP) { + return meta_range_t(0.0, 0.0, 0.0); + } + RFNOC_LOG_ERROR("Invalid TX gain name: " << name); + throw uhd::key_error("Invalid TX gain name!"); +} + +uhd::gain_range_t magnesium_radio_control_impl::get_rx_gain_range(const size_t chan) const +{ + if (get_rx_gain_profile(chan) == "manual") { + return meta_range_t(0.0, 0.0, 0.0); + } + return meta_range_t(ALL_RX_MIN_GAIN, ALL_RX_MAX_GAIN, ALL_RX_GAIN_STEP); +} + +uhd::gain_range_t magnesium_radio_control_impl::get_rx_gain_range( + const std::string& name, const size_t chan) const +{ + if (get_rx_gain_profile(chan) == "manual") { + if (name == "all" || name == ALL_GAINS) { + return meta_range_t(0.0, 0.0, 0.0); + } + if (name == MAGNESIUM_GAIN1) { + return meta_range_t( + AD9371_MIN_RX_GAIN, AD9371_MAX_RX_GAIN, AD9371_RX_GAIN_STEP); + } + if (name == MAGNESIUM_GAIN2) { + return meta_range_t(DSA_MIN_GAIN, DSA_MAX_GAIN, DSA_GAIN_STEP); + } + if (name == MAGNESIUM_AMP) { + return meta_range_t(AMP_MIN_GAIN, AMP_MAX_GAIN, AMP_GAIN_STEP); + } + RFNOC_LOG_ERROR("Invalid RX gain name: " << name); + throw uhd::key_error("Invalid RX gain name!"); + } + if (name == "all" || name == ALL_GAINS) { + return get_rx_gain_range(chan); + } + if (name == MAGNESIUM_GAIN1 || name == MAGNESIUM_GAIN2 || name == MAGNESIUM_AMP) { + return meta_range_t(0.0, 0.0, 0.0); + } + RFNOC_LOG_ERROR("Invalid RX gain name: " << name); + throw uhd::key_error("Invalid RX gain name!"); +} + +std::vector<std::string> magnesium_radio_control_impl::get_tx_gain_profile_names( + const size_t) const +{ + return MAGNESIUM_GP_OPTIONS; +} + +std::vector<std::string> magnesium_radio_control_impl::get_rx_gain_profile_names(const size_t ) const +{ + return MAGNESIUM_GP_OPTIONS; +} + +std::string magnesium_radio_control_impl::get_tx_gain_profile(const size_t) const +{ + return _gain_profile.at(TX_DIRECTION); +} + +std::string magnesium_radio_control_impl::get_rx_gain_profile(const size_t) const +{ + return _gain_profile.at(RX_DIRECTION); +} + +meta_range_t magnesium_radio_control_impl::get_tx_bandwidth_range(size_t) const +{ + return meta_range_t(AD9371_TX_MIN_BANDWIDTH, AD9371_TX_MAX_BANDWIDTH); +} + +meta_range_t magnesium_radio_control_impl::get_rx_bandwidth_range(size_t) const +{ + return meta_range_t(AD9371_TX_MIN_BANDWIDTH, AD9371_TX_MAX_BANDWIDTH); +} + + +/****************************************************************************** + * LO Controls + *****************************************************************************/ +std::vector<std::string> magnesium_radio_control_impl::get_rx_lo_names( + const size_t /*chan*/ + ) const +{ + return std::vector<std::string>{MAGNESIUM_LO1, MAGNESIUM_LO2}; +} + +std::vector<std::string> magnesium_radio_control_impl::get_rx_lo_sources( + const std::string& name, const size_t /*chan*/ + ) const +{ + if (name == MAGNESIUM_LO2) { + return std::vector<std::string>{"internal"}; + } else if (name == MAGNESIUM_LO1) { + return std::vector<std::string>{"internal", "external"}; + } else { + throw uhd::value_error("Could not find LO stage " + name); + } +} + +freq_range_t magnesium_radio_control_impl::get_rx_lo_freq_range( + const std::string& name, const size_t /*chan*/ +) const +{ + if (name == MAGNESIUM_LO1) { + return freq_range_t{ADF4351_MIN_FREQ, ADF4351_MAX_FREQ}; + } else if (name == MAGNESIUM_LO2) { + return freq_range_t{AD9371_MIN_FREQ, AD9371_MAX_FREQ}; + } else { + throw uhd::value_error("Could not find LO stage " + name); + } +} + +void magnesium_radio_control_impl::set_rx_lo_source( + const std::string& src, const std::string& name, const size_t /*chan*/ +) +{ + // TODO: checking what options are there + std::lock_guard<std::mutex> l(_set_lock); + RFNOC_LOG_TRACE("Setting RX LO " << name << " to " << src); + + if (name == MAGNESIUM_LO1) { + _ad9371->set_lo_source(src, RX_DIRECTION); + } else { + RFNOC_LOG_ERROR( + "RX LO " << name << " does not support setting source to " << src); + } +} + +const std::string magnesium_radio_control_impl::get_rx_lo_source( + const std::string& name, const size_t /*chan*/ +) const +{ + if (name == MAGNESIUM_LO1) { + // TODO: should we use this from cache? + return _ad9371->get_lo_source(RX_DIRECTION); + } + return "internal"; +} + +double magnesium_radio_control_impl::_set_rx_lo_freq(const std::string source, + const std::string name, + const double freq, + const size_t chan) +{ + double coerced_lo_freq = freq; + if (source != "internal") { + RFNOC_LOG_WARNING( + "LO source is not internal. This set frequency will be ignored"); + if (name == MAGNESIUM_LO1) { + // handle ad9371 external LO case + coerced_lo_freq = freq; + _ad9371_freq[RX_DIRECTION] = coerced_lo_freq; + } + } else { + if (name == MAGNESIUM_LO1) { + coerced_lo_freq = _ad9371->set_frequency(freq, chan, RX_DIRECTION); + _ad9371_freq[RX_DIRECTION] = coerced_lo_freq; + } else if (name == MAGNESIUM_LO2) { + // TODO: no hardcode the init_n_mode + coerced_lo_freq = _lo_enable(_rx_lo, freq, _master_clock_rate, false); + _adf4351_freq[RX_DIRECTION] = coerced_lo_freq; + } else { + RFNOC_LOG_WARNING("There's no LO with this name of " + << name + << " in the system. This set rx lo freq will be ignored"); + }; + } + return coerced_lo_freq; +} + +double magnesium_radio_control_impl::set_rx_lo_freq( + double freq, const std::string& name, const size_t chan) +{ + RFNOC_LOG_TRACE("set_rx_lo_freq(freq=" << freq << ", name=" << name << ")"); + std::lock_guard<std::mutex> l(_set_lock); + std::string source = this->get_rx_lo_source(name, chan); + const double coerced_lo_freq = this->_set_rx_lo_freq(source, name, freq, chan); + this->_update_freq(chan, RX_DIRECTION); + this->_update_gain(chan, RX_DIRECTION); + return coerced_lo_freq; +} + +double magnesium_radio_control_impl::get_rx_lo_freq( + const std::string& name, const size_t chan) +{ + RFNOC_LOG_TRACE("get_rx_lo_freq(name=" << name << ")"); + std::string source = this->get_rx_lo_source(name, chan); + if (name == MAGNESIUM_LO1) { + return _ad9371_freq.at(RX_DIRECTION); + } else if (name == "adf4531") { + return _adf4351_freq.at(RX_DIRECTION); + } else { + RFNOC_LOG_ERROR("get_rx_lo_freq(): No such LO: " << name); + } + UHD_THROW_INVALID_CODE_PATH(); +} + +// TX LO +std::vector<std::string> magnesium_radio_control_impl::get_tx_lo_names( + const size_t /*chan*/ +) const +{ + return std::vector<std::string>{MAGNESIUM_LO1, MAGNESIUM_LO2}; +} + +std::vector<std::string> magnesium_radio_control_impl::get_tx_lo_sources( + const std::string& name, const size_t /*chan*/ +) const +{ + if (name == MAGNESIUM_LO2) { + return std::vector<std::string>{"internal"}; + } else if (name == MAGNESIUM_LO1) { + return std::vector<std::string>{"internal", "external"}; + } else { + throw uhd::value_error("Could not find LO stage " + name); + } +} + +freq_range_t magnesium_radio_control_impl::get_tx_lo_freq_range( + const std::string& name, const size_t /*chan*/ +) +{ + if (name == MAGNESIUM_LO2) { + return freq_range_t{ADF4351_MIN_FREQ, ADF4351_MAX_FREQ}; + } else if (name == MAGNESIUM_LO1) { + return freq_range_t{AD9371_MIN_FREQ, AD9371_MAX_FREQ}; + } else { + throw uhd::value_error("Could not find LO stage " + name); + } +} + +void magnesium_radio_control_impl::set_tx_lo_source( + const std::string& src, const std::string& name, const size_t /*chan*/ +) +{ + // TODO: checking what options are there + std::lock_guard<std::mutex> l(_set_lock); + RFNOC_LOG_TRACE("set_tx_lo_source(name=" << name << ", src=" << src << ")"); + if (name == MAGNESIUM_LO1) { + _ad9371->set_lo_source(src, TX_DIRECTION); + } else { + RFNOC_LOG_ERROR( + "TX LO " << name << " does not support setting source to " << src); + } +} + +const std::string magnesium_radio_control_impl::get_tx_lo_source( + const std::string& name, const size_t /*chan*/ +) +{ + if (name == MAGNESIUM_LO1) { + // TODO: should we use this from cache? + return _ad9371->get_lo_source(TX_DIRECTION); + } + return "internal"; +} + +double magnesium_radio_control_impl::_set_tx_lo_freq(const std::string source, + const std::string name, + const double freq, + const size_t chan) +{ + double coerced_lo_freq = freq; + if (source != "internal") { + RFNOC_LOG_WARNING( + "LO source is not internal. This set frequency will be ignored"); + if (name == MAGNESIUM_LO1) { + // handle ad9371 external LO case + coerced_lo_freq = freq; + _ad9371_freq[TX_DIRECTION] = coerced_lo_freq; + } + } else { + if (name == MAGNESIUM_LO1) { + coerced_lo_freq = _ad9371->set_frequency(freq, chan, TX_DIRECTION); + _ad9371_freq[TX_DIRECTION] = coerced_lo_freq; + } else if (name == MAGNESIUM_LO2) { + // TODO: no hardcode the int_n_mode + const bool int_n_mode = false; + coerced_lo_freq = _lo_enable(_tx_lo, freq, _master_clock_rate, int_n_mode); + _adf4351_freq[TX_DIRECTION] = coerced_lo_freq; + } else { + RFNOC_LOG_WARNING("There's no LO with this name of " + << name + << " in the system. This set tx lo freq will be ignored"); + }; + } + return coerced_lo_freq; +} + +double magnesium_radio_control_impl::set_tx_lo_freq( + double freq, const std::string& name, const size_t chan) +{ + RFNOC_LOG_TRACE("set_tx_lo_freq(freq=" << freq << ", name=" << name << ")"); + std::string source = this->get_tx_lo_source(name, chan); + const double return_freq = this->_set_tx_lo_freq(source, name, freq, chan); + this->_update_freq(chan, TX_DIRECTION); + this->_update_gain(chan, TX_DIRECTION); + return return_freq; +} + +double magnesium_radio_control_impl::get_tx_lo_freq(const std::string& name, const size_t chan) +{ + RFNOC_LOG_TRACE("get_tx_lo_freq(name=" << name << ")"); + std::string source = this->get_tx_lo_source(name, chan); + if (name == MAGNESIUM_LO1) { + return _ad9371_freq[TX_DIRECTION]; + } else if (name == MAGNESIUM_LO2) { + return _adf4351_freq[TX_DIRECTION]; + } else { + RFNOC_LOG_ERROR("get_tx_lo_freq(): No such LO: " << name); + }; + + UHD_THROW_INVALID_CODE_PATH(); +} + +void magnesium_radio_control_impl::_remap_band_limits( + const std::string band_map, const uhd::direction_t dir) +{ + const size_t dflt_band_size = (dir == RX_DIRECTION) ? _rx_band_map.size() + : _tx_band_map.size(); + + std::vector<std::string> band_map_split; + double band_lim; + + RFNOC_LOG_DEBUG("Using user specified frequency band limits"); + boost::split(band_map_split, band_map, boost::is_any_of(";")); + if (band_map_split.size() != dflt_band_size) { + throw uhd::runtime_error(( + boost::format( + "size %s of given frequency band map doesn't match the required size: %s") + % band_map_split.size() % dflt_band_size) + .str()); + } + RFNOC_LOG_DEBUG("newly used band limits: "); + for (size_t i = 0; i < band_map_split.size(); i++) { + try { + band_lim = std::stod(band_map_split.at(i)); + } catch (...) { + throw uhd::value_error( + (boost::format("error while converting given frequency string %s " + "to a double value") + % band_map_split.at(i)) + .str()); + } + RFNOC_LOG_DEBUG("band " << i << " limit: " << band_lim << "Hz"); + if (dir == RX_DIRECTION) + _rx_band_map.at(i) = band_lim; + else + _tx_band_map.at(i) = band_lim; + } +} + + +bool magnesium_radio_control_impl::get_lo_lock_status(const direction_t dir) +{ + if (not(bool(_rpcc))) { + RFNOC_LOG_WARNING("Reported no LO lock due to lack of RPC connection."); + return false; + } + + const std::string trx = (dir == RX_DIRECTION) ? "rx" : "tx"; + const size_t chan = 0; // They're the same after all + const double freq = (dir == RX_DIRECTION) ? get_rx_frequency(chan) + : get_tx_frequency(chan); + + bool lo_lock = + _rpcc->request_with_token<bool>(_rpc_prefix + "get_ad9371_lo_lock", trx); + RFNOC_LOG_TRACE("AD9371 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No")); + if (lo_lock and _map_freq_to_rx_band(_rx_band_map, freq) == rx_band::LOWBAND) { + lo_lock = + lo_lock + && _rpcc->request_with_token<bool>(_rpc_prefix + "get_lowband_lo_lock", trx); + RFNOC_LOG_TRACE( + "ADF4351 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No")); + } + + return lo_lock; +} + +/************************************************************************** + * GPIO Controls + *************************************************************************/ +std::vector<std::string> magnesium_radio_control_impl::get_gpio_banks() const +{ + return {MAGNESIUM_FPGPIO_BANK}; +} + +void magnesium_radio_control_impl::set_gpio_attr( + const std::string& bank, const std::string& attr, const uint32_t value) +{ + if (bank != MAGNESIUM_FPGPIO_BANK) { + RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank); + throw uhd::key_error("Invalid GPIO bank!"); + } + if (!gpio_atr::gpio_attr_rev_map.count(attr)) { + RFNOC_LOG_ERROR("Invalid GPIO attr: " << attr); + throw uhd::key_error("Invalid GPIO attr!"); + } + + const gpio_atr::gpio_attr_t gpio_attr = gpio_atr::gpio_attr_rev_map.at(attr); + + if (gpio_attr == gpio_atr::GPIO_READBACK) { + RFNOC_LOG_WARNING("Cannot set READBACK attr."); + return; + } + + _fp_gpio->set_gpio_attr(gpio_attr, value); +} + +uint32_t magnesium_radio_control_impl::get_gpio_attr( + const std::string& bank, const std::string& attr) +{ + if (bank != MAGNESIUM_FPGPIO_BANK) { + RFNOC_LOG_ERROR("Invalid GPIO bank: " << bank); + throw uhd::key_error("Invalid GPIO bank!"); + } + + return _fp_gpio->get_attr_reg(usrp::gpio_atr::gpio_attr_rev_map.at(attr)); +} + +/****************************************************************************** + * EEPROM API + *****************************************************************************/ +void magnesium_radio_control_impl::set_db_eeprom(const eeprom_map_t& db_eeprom) +{ + const size_t db_idx = get_block_id().get_block_count(); + _rpcc->notify_with_token("set_db_eeprom", db_idx, db_eeprom); +} + +eeprom_map_t magnesium_radio_control_impl::get_db_eeprom() +{ + const size_t db_idx = get_block_id().get_block_count(); + return this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx); +} + +/************************************************************************** + * Sensor API + *************************************************************************/ +std::vector<std::string> magnesium_radio_control_impl::get_rx_sensor_names(size_t) +{ + auto sensor_names = _rpcc->request_with_token<std::vector<std::string>>( + this->_rpc_prefix + "get_sensors", "RX"); + sensor_names.push_back("lo_locked"); + return sensor_names; +} + +sensor_value_t magnesium_radio_control_impl::get_rx_sensor(const std::string& name, size_t chan) +{ + if (name == "lo_locked") { + return sensor_value_t( + "all_los", this->get_lo_lock_status(RX_DIRECTION), "locked", "unlocked"); + } + return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>( + _rpc_prefix + "get_sensor", "RX", name, chan)); +} + +std::vector<std::string> magnesium_radio_control_impl::get_tx_sensor_names(size_t) +{ + auto sensor_names = _rpcc->request_with_token<std::vector<std::string>>( + this->_rpc_prefix + "get_sensors", "TX"); + sensor_names.push_back("lo_locked"); + return sensor_names; +} + +sensor_value_t magnesium_radio_control_impl::get_tx_sensor(const std::string& name, size_t chan) +{ + if (name == "lo_locked") { + return sensor_value_t( + "all_los", this->get_lo_lock_status(TX_DIRECTION), "locked", "unlocked"); + } + return sensor_value_t(_rpcc->request_with_token<sensor_value_t::sensor_map_t>( + _rpc_prefix + "get_sensor", "TX", name, chan)); +} + +/************************************************************************** + * node_t API Calls + *************************************************************************/ +void magnesium_radio_control_impl::set_command_time(uhd::time_spec_t time, const size_t chan) +{ + node_t::set_command_time(time, chan); + _wb_ifaces.at(chan)->set_time(time); +} + +/************************************************************************** + * Radio Identification API Calls + *************************************************************************/ +size_t magnesium_radio_control_impl::get_chan_from_dboard_fe( + const std::string& fe, const uhd::direction_t) const +{ + if (fe == "0") { + return 0; + } + if (fe == "1") { + return 1; + } + throw uhd::key_error(std::string("[N300] Invalid frontend: ") + fe); +} + +std::string magnesium_radio_control_impl::get_dboard_fe_from_chan( + const size_t chan, const uhd::direction_t) const +{ + if (chan == 0) { + return "0"; + } + if (chan == 1) { + return "1"; + } + throw uhd::lookup_error( + std::string("[N300] Invalid channel: ") + std::to_string(chan)); +} + +std::string magnesium_radio_control_impl::get_fe_name( + const size_t, const uhd::direction_t) const +{ + return MAGNESIUM_FE_NAME; +} + +// Register the block +UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT( + magnesium_radio_control, RADIO_BLOCK, N300, "Radio", true, "radio_clk", "bus_clk"); diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp index 165e3c996..d7c721c3b 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.hpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control.hpp @@ -14,12 +14,14 @@ #include "magnesium_ad9371_iface.hpp" #include "magnesium_cpld_ctrl.hpp" #include "magnesium_cpld_regs.hpp" +#include <iostream> #include <uhd/types/serial.hpp> #include <uhd/usrp/dboard_manager.hpp> #include <uhd/usrp/gpio_defs.hpp> -#include <uhdlib/rfnoc/radio_ctrl_impl.hpp> -#include <uhdlib/rfnoc/rpc_block_ctrl.hpp> +#include <uhd/types/eeprom.hpp> +#include <uhdlib/rfnoc/radio_control_impl.hpp> #include <uhdlib/usrp/common/adf435x.hpp> +#include <uhdlib/usrp/common/mpmd_mb_controller.hpp> #include <uhdlib/usrp/cores/gpio_atr_3000.hpp> #include <mutex> @@ -29,13 +31,9 @@ namespace uhd { namespace rfnoc { * * This daughterboard is used on the USRP N310 and N300. */ - - -class magnesium_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl +class magnesium_radio_control_impl : public radio_control_impl { public: - typedef boost::shared_ptr<magnesium_radio_ctrl_impl> sptr; - //! Frequency bands for RX. Bands are a function of the analog filter banks enum class rx_band { INVALID_BAND, @@ -68,66 +66,109 @@ public: /************************************************************************ * Structors ***********************************************************************/ - UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(magnesium_radio_ctrl) - virtual ~magnesium_radio_ctrl_impl(); + magnesium_radio_control_impl(make_args_ptr make_args); + virtual ~magnesium_radio_control_impl(); /************************************************************************ - * API calls + * RF API calls ***********************************************************************/ // Note: We use the cached values in radio_ctrl_impl, so most getters are // not reimplemented here double set_rate(double rate); - void set_tx_antenna(const std::string& ant, const size_t chan); - void set_rx_antenna(const std::string& ant, const size_t chan); - + // Setters + void set_tx_antenna(const std::string &ant, const size_t chan); + void set_rx_antenna(const std::string &ant, const size_t chan); double set_tx_frequency(const double freq, const size_t chan); double set_rx_frequency(const double freq, const size_t chan); - double get_rx_frequency(const size_t chan); - double get_tx_frequency(const size_t chan); + double set_tx_gain(const double gain, const size_t chan); + double set_tx_gain(const double gain, const std::string& name, const size_t chan); + double set_rx_gain(const double gain, const size_t chan); + double set_rx_gain(const double gain, const std::string& name, const size_t chan); double set_tx_bandwidth(const double bandwidth, const size_t chan); double set_rx_bandwidth(const double bandwidth, const size_t chan); + void set_tx_gain_profile(const std::string& profile, const size_t chan); + void set_rx_gain_profile(const std::string& profile, const size_t chan); + + // Getters + std::vector<std::string> get_tx_antennas(const size_t chan) const; + std::vector<std::string> get_rx_antennas(const size_t chan) const; + uhd::freq_range_t get_tx_frequency_range(const size_t chan) const; + uhd::freq_range_t get_rx_frequency_range(const size_t chan) const; + std::vector<std::string> get_tx_gain_names(const size_t) const; + std::vector<std::string> get_rx_gain_names(const size_t) const; + double get_tx_gain(const std::string&, size_t); + double get_rx_gain(const std::string&, size_t); + uhd::gain_range_t get_tx_gain_range(const size_t) const; + uhd::gain_range_t get_tx_gain_range(const std::string&, const size_t) const; + uhd::gain_range_t get_rx_gain_range(const size_t) const; + uhd::gain_range_t get_rx_gain_range(const std::string&, const size_t) const; + std::vector<std::string> get_tx_gain_profile_names(const size_t chan) const; + std::vector<std::string> get_rx_gain_profile_names(const size_t chan) const; + std::string get_tx_gain_profile(const size_t chan) const; + std::string get_rx_gain_profile(const size_t chan) const; + uhd::meta_range_t get_tx_bandwidth_range(size_t chan) const; + uhd::meta_range_t get_rx_bandwidth_range(size_t chan) const; - // RX LO - std::vector<std::string> get_rx_lo_names(const size_t chan); + /************************************************************************** + * LO Controls + *************************************************************************/ + std::vector<std::string> get_rx_lo_names(const size_t chan) const; std::vector<std::string> get_rx_lo_sources( - const std::string& name, const size_t chan); - freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan); - + const std::string& name, const size_t chan) const; + freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan) const; void set_rx_lo_source( const std::string& src, const std::string& name, const size_t chan); - const std::string get_rx_lo_source(const std::string& name, const size_t chan); - + const std::string get_rx_lo_source(const std::string& name, const size_t chan) const; double set_rx_lo_freq(double freq, const std::string& name, const size_t chan); double get_rx_lo_freq(const std::string& name, const size_t chan); - - // TX LO - std::vector<std::string> get_tx_lo_names(const size_t chan); + std::vector<std::string> get_tx_lo_names(const size_t chan) const; std::vector<std::string> get_tx_lo_sources( - const std::string& name, const size_t chan); + const std::string& name, const size_t chan) const; freq_range_t get_tx_lo_freq_range(const std::string& name, const size_t chan); - void set_tx_lo_source( const std::string& src, const std::string& name, const size_t chan); const std::string get_tx_lo_source(const std::string& name, const size_t chan); - - double set_tx_lo_freq(double freq, const std::string& name, const size_t chan); + double set_tx_lo_freq(const double freq, const std::string& name, const size_t chan); double get_tx_lo_freq(const std::string& name, const size_t chan); - // gain - double set_tx_gain(const double gain, const size_t chan); - double set_rx_gain(const double gain, const size_t chan); - void set_tx_gain_source( - const std::string& src, const std::string& name, const size_t chan); - std::string get_tx_gain_source(const std::string& name, const size_t chan); - void set_rx_gain_source( - const std::string& src, const std::string& name, const size_t chan); - std::string get_rx_gain_source(const std::string& name, const size_t chan); + /************************************************************************** + * GPIO Controls + *************************************************************************/ + std::vector<std::string> get_gpio_banks() const; + void set_gpio_attr( + const std::string& bank, const std::string& attr, const uint32_t value); + uint32_t get_gpio_attr(const std::string& bank, const std::string& attr); + + /************************************************************************** + * EEPROM API + *************************************************************************/ + void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom); + uhd::eeprom_map_t get_db_eeprom(); + + /************************************************************************** + * Sensor API + *************************************************************************/ + std::vector<std::string> get_rx_sensor_names(size_t chan); + uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan); + std::vector<std::string> get_tx_sensor_names(size_t chan); + uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan); - size_t get_chan_from_dboard_fe(const std::string& fe, const direction_t dir); - std::string get_dboard_fe_from_chan(const size_t chan, const direction_t dir); + /************************************************************************** + * Radio Identification API Calls + *************************************************************************/ + std::string get_slot_name() const { return _radio_slot; } + size_t get_chan_from_dboard_fe( + const std::string& fe, const uhd::direction_t direction) const; + std::string get_dboard_fe_from_chan( + const size_t chan, const uhd::direction_t direction) const; + std::string get_fe_name( + const size_t chan, const uhd::direction_t direction) const; - void set_rpc_client(uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args); + /************************************************************************** + * node_t API Calls + *************************************************************************/ + void set_command_time(uhd::time_spec_t time, const size_t chan); private: /************************************************************************** @@ -157,6 +198,10 @@ private: //! Initialize property tree void _init_prop_tree(); + //! Init RPC interaction + void _init_mpm(); + + //! Set up sensor property nodes void _init_mpm_sensors(const direction_t dir, const size_t chan_idx); //! Map a frequency in Hz to an rx_band value. Will return @@ -240,12 +285,21 @@ private: //! Additional block args; gets set during set_rpc_client() uhd::device_addr_t _block_args; + //! Reference to the MB controller + mpmd_mb_controller::sptr _n310_mb_control; + + //! Reference to the MB timekeeper + uhd::rfnoc::mpmd_mb_controller::mpmd_timekeeper::sptr _n3xx_timekeeper; + //! Reference to the RPC client uhd::rpc_client::sptr _rpcc; //! Reference to the SPI core uhd::spi_iface::sptr _spi; + //! Reference to wb_iface compat adapters (one per channel) + std::vector<uhd::timed_wb_iface::sptr> _wb_ifaces; + //! Reference to the TX LO adf435x_iface::sptr _tx_lo; diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_cpld.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_cpld.cpp index 679816af8..41f99cd68 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_cpld.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_cpld.cpp @@ -6,14 +6,14 @@ #include "magnesium_constants.hpp" #include "magnesium_cpld_ctrl.hpp" -#include "magnesium_radio_ctrl_impl.hpp" +#include "magnesium_radio_control.hpp" #include <uhd/utils/log.hpp> using namespace uhd; using namespace uhd::usrp; using namespace uhd::rfnoc; -void magnesium_radio_ctrl_impl::_identify_with_leds(const int identify_duration) +void magnesium_radio_control_impl::_identify_with_leds(const int identify_duration) { auto end_time = std::chrono::steady_clock::now() + std::chrono::seconds(identify_duration); @@ -36,7 +36,7 @@ void magnesium_radio_ctrl_impl::_identify_with_leds(const int identify_duration) _cpld->reset(); } -void magnesium_radio_ctrl_impl::_update_atr_switches( +void magnesium_radio_control_impl::_update_atr_switches( const magnesium_cpld_ctrl::chan_sel_t chan, const direction_t dir, const std::string& ant) @@ -51,8 +51,7 @@ void magnesium_radio_ctrl_impl::_update_atr_switches( // RX SW1. In all other cases, a TX state toggle (on to idle or vice // versa) won't trigger a change of the TRX switch. auto sw_trx = _sw_trx[chan]; - UHD_LOG_TRACE( - unique_id(), "Updating all RX-ATR related switches for antenna==" << ant); + RFNOC_LOG_TRACE("Updating all RX-ATR related switches for antenna==" << ant); if (ant == "TX/RX") { rx_sw1 = magnesium_cpld_ctrl::RX_SW1_TRXSWITCHOUTPUT; sw_trx = magnesium_cpld_ctrl::SW_TRX_RXCHANNELPATH; @@ -96,7 +95,7 @@ void magnesium_radio_ctrl_impl::_update_atr_switches( ); } if (dir == TX_DIRECTION or dir == DX_DIRECTION) { - UHD_LOG_TRACE(unique_id(), "Updating all TX-ATR related switches..."); + RFNOC_LOG_TRACE("Updating all TX-ATR related switches..."); _cpld->set_tx_atr_bits(chan, magnesium_cpld_ctrl::ON, true, /* LED on */ @@ -117,11 +116,11 @@ void magnesium_radio_ctrl_impl::_update_atr_switches( }; } -void magnesium_radio_ctrl_impl::_update_rx_freq_switches(const double freq, +void magnesium_radio_control_impl::_update_rx_freq_switches(const double freq, const bool bypass_lnas, const magnesium_cpld_ctrl::chan_sel_t chan_sel) { - UHD_LOG_TRACE(unique_id(), + RFNOC_LOG_TRACE( "Update all RX freq related switches. f=" << freq << " Hz, " "bypass LNAS: " @@ -142,7 +141,7 @@ void magnesium_radio_ctrl_impl::_update_rx_freq_switches(const double freq, not bypass_lnas and (band == rx_band::BAND4 or band == rx_band::BAND5 or band == rx_band::BAND6); const bool rx_lna2_enable = not bypass_lnas and not rx_lna1_enable; - UHD_LOG_TRACE(unique_id(), + RFNOC_LOG_TRACE( " Enabling LNA1: " << (rx_lna1_enable ? "Yes" : "No") << " Enabling LNA2: " << (rx_lna2_enable ? "Yes" : "No")); // All the defaults are OK when using the bypass path. @@ -199,7 +198,7 @@ void magnesium_radio_ctrl_impl::_update_rx_freq_switches(const double freq, rx_sw6 = magnesium_cpld_ctrl::RX_SW6_UPPERFILTERBANKFROMSWITCH4; break; case rx_band::INVALID_BAND: - UHD_LOG_ERROR(unique_id(), "Cannot map RX frequency to band: " << freq); + RFNOC_LOG_ERROR("Cannot map RX frequency to band: " << freq); break; default: UHD_THROW_INVALID_CODE_PATH(); @@ -222,16 +221,15 @@ void magnesium_radio_ctrl_impl::_update_rx_freq_switches(const double freq, enable_lowband_mixer); } -void magnesium_radio_ctrl_impl::_update_tx_freq_switches(const double freq, +void magnesium_radio_control_impl::_update_tx_freq_switches(const double freq, const bool bypass_amp, const magnesium_cpld_ctrl::chan_sel_t chan_sel) { - UHD_LOG_TRACE(unique_id(), - "Update all TX freq related switches. f=" << freq - << " Hz, " - "bypass amp: " - << (bypass_amp ? "Yes" : "No") - << ", chan=" << chan_sel); + RFNOC_LOG_TRACE("Update all TX freq related switches. f=" + << freq + << " Hz, " + "bypass amp: " + << (bypass_amp ? "Yes" : "No") << ", chan=" << chan_sel); auto tx_sw1 = magnesium_cpld_ctrl::TX_SW1_SHUTDOWNTXSW1; auto tx_sw2 = magnesium_cpld_ctrl::TX_SW2_TOTXFILTERLP6400MHZ; auto tx_sw3 = magnesium_cpld_ctrl::TX_SW3_BYPASSPATHTOTRXSW; @@ -279,7 +277,7 @@ void magnesium_radio_ctrl_impl::_update_tx_freq_switches(const double freq, tx_sw3 = magnesium_cpld_ctrl::TX_SW3_TOTXFILTERBANKS; break; case tx_band::INVALID_BAND: - UHD_LOG_ERROR(unique_id(), "Cannot map TX frequency to band: " << freq); + RFNOC_LOG_ERROR("Cannot map TX frequency to band: " << freq); break; default: UHD_THROW_INVALID_CODE_PATH(); diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_gain.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_gain.cpp index b66bd2efd..f515b2e33 100644 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_gain.cpp +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_gain.cpp @@ -6,63 +6,62 @@ #include "magnesium_constants.hpp" #include "magnesium_gain_table.hpp" -#include "magnesium_radio_ctrl_impl.hpp" -#include <uhd/exception.hpp> +#include "magnesium_radio_control.hpp" #include <uhd/utils/log.hpp> +#include <uhd/exception.hpp> using namespace uhd; using namespace uhd::usrp; using namespace uhd::rfnoc; using namespace magnesium; -double magnesium_radio_ctrl_impl::_set_all_gain( +double magnesium_radio_control_impl::_set_all_gain( const double gain, const double freq, const size_t chan, const direction_t dir) { - UHD_LOG_TRACE(unique_id(), - __func__ << "(gain=" << gain - << "dB, " - "freq=" - << freq - << " Hz, " - "chan=" - << chan - << ", " - "dir=" - << dir << ")"); + RFNOC_LOG_TRACE(__func__ << "(gain=" << gain + << "dB, " + "freq=" + << freq + << " Hz, " + "chan=" + << chan + << ", " + "dir=" + << dir); const size_t ad9371_chan = chan; auto chan_sel = static_cast<magnesium_cpld_ctrl::chan_sel_t>(chan); gain_tuple_t gain_tuple; std::string gp = _gain_profile[dir]; - UHD_LOG_TRACE(unique_id(), "Gain profile: " << gp); + RFNOC_LOG_TRACE("Gain profile: " << gp); if (gp == "manual") { - UHD_LOG_TRACE(unique_id(), "Manual gain mode. Getting gain from property tree."); + RFNOC_LOG_TRACE("Manual gain mode. Getting gain from property tree."); gain_tuple = {DSA_MAX_GAIN - _dsa_att[dir], ((dir == RX_DIRECTION) ? AD9371_MAX_RX_GAIN : AD9371_MAX_TX_GAIN) - _ad9371_att[dir], _amp_bypass[dir]}; } else if (gp.find("default") != gp.npos) { - UHD_LOG_TRACE(unique_id(), "Getting gain from gain table."); + RFNOC_LOG_TRACE("Getting gain from gain table."); gain_tuple = (dir == RX_DIRECTION) ? get_rx_gain_tuple(gain, _map_freq_to_rx_band(_rx_band_map, freq)) : get_tx_gain_tuple(gain, _map_freq_to_tx_band(_tx_band_map, freq)); if (gp == "default_rf_filter_bypass_always_on") { - UHD_LOG_TRACE(unique_id(), "Enable filter bypass for all gains"); + RFNOC_LOG_TRACE("Enable filter bypass for all gains"); gain_tuple.bypass = true; } else if (gp == "default_rf_filter_bypass_always_off") { - UHD_LOG_TRACE(unique_id(), "Disable filter bypass for all gains"); + RFNOC_LOG_TRACE("Disable filter bypass for all gains"); gain_tuple.bypass = false; } } else { - UHD_LOG_ERROR(unique_id(), "Unsupported gain mode: " << gp); + RFNOC_LOG_ERROR("Unsupported gain mode: " << gp); throw uhd::value_error( - str(boost::format("[%s] Unsupported gain mode: %s") % unique_id() % gp)); + str(boost::format("[%s] Unsupported gain mode: %s") % get_unique_id() % gp)); } const double ad9371_gain = ((dir == RX_DIRECTION) ? AD9371_MAX_RX_GAIN : AD9371_MAX_TX_GAIN) - gain_tuple.ad9371_att; - UHD_LOG_TRACE(unique_id(), + RFNOC_LOG_TRACE( "AD9371 attenuation==" << gain_tuple.ad9371_att << " dB, " "AD9371 gain==" @@ -86,10 +85,10 @@ double magnesium_radio_ctrl_impl::_set_all_gain( return gain; } -double magnesium_radio_ctrl_impl::_get_all_gain( +double magnesium_radio_control_impl::_get_all_gain( const size_t /* chan */, const direction_t dir) { - UHD_LOG_TRACE(unique_id(), "Getting all gain "); + RFNOC_LOG_TRACE("_get_all_gain()"); if (dir == RX_DIRECTION) { return _all_rx_gain; } @@ -99,11 +98,12 @@ double magnesium_radio_ctrl_impl::_get_all_gain( /****************************************************************************** * DSA Controls *****************************************************************************/ -double magnesium_radio_ctrl_impl::_dsa_set_att( +double magnesium_radio_control_impl::_dsa_set_att( const double att, const size_t chan, const direction_t dir) { - UHD_LOG_TRACE(unique_id(), - __func__ << "(att=" << att << "dB, chan=" << chan << ", dir=" << dir << ")") + RFNOC_LOG_TRACE( + __func__ << "(att=" + << "att dB, chan=" << chan << ", dir=" << dir << ")") const uint32_t dsa_val = 2 * att; _set_dsa_val(chan, dir, dsa_val); @@ -116,7 +116,7 @@ double magnesium_radio_ctrl_impl::_dsa_set_att( return att; } -double magnesium_radio_ctrl_impl::_dsa_get_att( +double magnesium_radio_control_impl::_dsa_get_att( const size_t /*chan*/, const direction_t dir) { if (dir == RX_DIRECTION) { @@ -125,21 +125,19 @@ double magnesium_radio_ctrl_impl::_dsa_get_att( return _dsa_tx_att; } -void magnesium_radio_ctrl_impl::_set_dsa_val( +void magnesium_radio_control_impl::_set_dsa_val( const size_t chan, const direction_t dir, const uint32_t dsa_val) { // The DSA register holds 12 bits. The lower 6 bits are for RX, the upper // 6 bits are for TX. if (dir == RX_DIRECTION or dir == DX_DIRECTION) { - UHD_LOG_TRACE(unique_id(), - __func__ << "(chan=" << chan << ", dir=RX" - << ", dsa_val=" << dsa_val << ")") + RFNOC_LOG_TRACE(__func__ << "(chan=" << chan << ", dir=RX" + << ", dsa_val=" << dsa_val << ")") _gpio[chan]->set_gpio_out(dsa_val, 0x003F); } if (dir == TX_DIRECTION or dir == DX_DIRECTION) { - UHD_LOG_TRACE(unique_id(), - __func__ << "(chan=" << chan << ", dir=TX" - << ", dsa_val=" << dsa_val << ")") + RFNOC_LOG_TRACE(__func__ << "(chan=" << chan << ", dir=TX" + << ", dsa_val=" << dsa_val << ")") _gpio[chan]->set_gpio_out(dsa_val << 6, 0x0FC0); } } diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp new file mode 100644 index 000000000..db2ec9494 --- /dev/null +++ b/host/lib/usrp/dboard/magnesium/magnesium_radio_control_init.cpp @@ -0,0 +1,446 @@ +// +// Copyright 2017 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include "magnesium_constants.hpp" +#include "magnesium_radio_control.hpp" +#include <uhd/transport/chdr.hpp> +#include <uhd/types/eeprom.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/log.hpp> +#include <uhdlib/usrp/cores/spi_core_3000.hpp> +#include <uhdlib/rfnoc/reg_iface_adapter.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/case_conv.hpp> +#include <boost/algorithm/string/split.hpp> +#include <boost/make_shared.hpp> +#include <string> +#include <vector> + +using namespace uhd; +using namespace uhd::rfnoc; + +namespace { + +enum slave_select_t { SEN_CPLD = 1, SEN_TX_LO = 2, SEN_RX_LO = 4, SEN_PHASE_DAC = 8 }; + +constexpr double MAGNESIUM_DEFAULT_FREQ = 2.5e9; // Hz +constexpr double MAGNESIUM_DEFAULT_BANDWIDTH = 100e6; // Hz + +} // namespace + +void magnesium_radio_control_impl::_init_defaults() +{ + RFNOC_LOG_TRACE("_init_defaults()"); + for (size_t chan = 0; chan < get_num_output_ports(); chan++) { + radio_control_impl::set_rx_frequency(MAGNESIUM_DEFAULT_FREQ, chan); + radio_control_impl::set_rx_gain(0, chan); + radio_control_impl::set_rx_antenna(MAGNESIUM_DEFAULT_RX_ANTENNA, chan); + radio_control_impl::set_rx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan); + } + + for (size_t chan = 0; chan < get_num_input_ports(); chan++) { + radio_control_impl::set_tx_frequency(MAGNESIUM_DEFAULT_FREQ, chan); + radio_control_impl::set_tx_gain(0, chan); + radio_control_impl::set_tx_antenna(MAGNESIUM_DEFAULT_TX_ANTENNA, chan); + radio_control_impl::set_tx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan); + } + + const auto block_args = get_block_args(); + if (block_args.has_key("tx_gain_profile")) { + RFNOC_LOG_INFO("Using user specified TX gain profile: " << block_args.get( + "tx_gain_profile")); + _gain_profile[TX_DIRECTION] = block_args.get("tx_gain_profile"); + } + + if (block_args.has_key("rx_gain_profile")) { + RFNOC_LOG_INFO("Using user specified RX gain profile: " << block_args.get( + "rx_gain_profile")); + _gain_profile[RX_DIRECTION] = block_args.get("rx_gain_profile"); + } + + if (block_args.has_key("rx_band_map")) { + RFNOC_LOG_INFO("Using user specified RX band limits"); + _remap_band_limits(block_args.get("rx_band_map"), RX_DIRECTION); + } + + if (block_args.has_key("tx_band_map")) { + RFNOC_LOG_INFO("Using user specified TX band limits"); + _remap_band_limits(block_args.get("tx_band_map"), TX_DIRECTION); + } +} + +void magnesium_radio_control_impl::_init_peripherals() +{ + RFNOC_LOG_TRACE("Initializing peripherals..."); + RFNOC_LOG_TRACE("Initializing SPI core..."); + _spi = spi_core_3000::make( + [this](uint32_t addr, uint32_t data){ regs().poke32(addr, data, get_command_time(0)); }, + [this](uint32_t addr){ return regs().peek32(addr, get_command_time(0)); }, + regmap::REG_SPI_W, + 8, + regmap::REG_SPI_R); + RFNOC_LOG_TRACE("Initializing CPLD..."); + RFNOC_LOG_TRACE("Creating new CPLD object..."); + spi_config_t spi_config; + spi_config.use_custom_divider = true; + spi_config.divider = 125; + spi_config.mosi_edge = spi_config_t::EDGE_RISE; + spi_config.miso_edge = spi_config_t::EDGE_FALL; + RFNOC_LOG_TRACE("Making CPLD object..."); + _cpld = std::make_shared<magnesium_cpld_ctrl>( + [this, spi_config](const uint32_t transaction) { // Write functor + this->_spi->write_spi(SEN_CPLD, spi_config, transaction, 24); + }, + [this, spi_config](const uint32_t transaction) { // Read functor + return this->_spi->read_spi(SEN_CPLD, spi_config, transaction, 24); + }); + _update_atr_switches( + magnesium_cpld_ctrl::BOTH, DX_DIRECTION, radio_control_impl::get_rx_antenna(0)); + RFNOC_LOG_TRACE("Initializing TX LO..."); + _tx_lo = adf435x_iface::make_adf4351([this]( + const std::vector<uint32_t> transactions) { + for (const uint32_t transaction : transactions) { + this->_spi->write_spi(SEN_TX_LO, spi_config_t::EDGE_RISE, transaction, 32); + } + }); + RFNOC_LOG_TRACE("Initializing RX LO..."); + _rx_lo = adf435x_iface::make_adf4351([this]( + const std::vector<uint32_t> transactions) { + for (const uint32_t transaction : transactions) { + this->_spi->write_spi(SEN_RX_LO, spi_config_t::EDGE_RISE, transaction, 32); + } + }); + + _gpio.clear(); // Following the as-if rule, this can get optimized out + for (size_t radio_idx = 0; radio_idx < get_num_input_ports(); radio_idx++) { + _wb_ifaces.push_back(RFNOC_MAKE_WB_IFACE(0, radio_idx)); + RFNOC_LOG_TRACE("Initializing GPIOs for channel " << radio_idx); + _gpio.emplace_back(usrp::gpio_atr::gpio_atr_3000::make( + _wb_ifaces.back(), + n310_regs::DB_GPIO_BASE + radio_idx * n310_regs::DB_GPIO_OFFSET, + n310_regs::DB_GPIO_RB + radio_idx * n310_regs::DB_GPIO_OFFSET)); + // DSA and AD9371 gain bits do *not* toggle on ATR modes. If we ever + // connect anything else to this core, we might need to set_atr_mode() + // to MODE_ATR on those bits. For now, all bits simply do what they're + // told, and don't toggle on RX/TX state changes. + _gpio.back()->set_atr_mode(usrp::gpio_atr::MODE_GPIO, // Disable ATR mode + usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); + _gpio.back()->set_gpio_ddr(usrp::gpio_atr::DDR_OUTPUT, // Make all GPIOs outputs + usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); + } + RFNOC_LOG_TRACE("Initializing front-panel GPIO control...") + _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make(_wb_ifaces.front(), + n310_regs::FP_GPIO, n310_regs::RB_FP_GPIO); +} + +void magnesium_radio_control_impl::_init_frontend_subtree( + uhd::property_tree::sptr subtree, const size_t chan_idx) +{ + const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx; + const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx; + RFNOC_LOG_TRACE("Adding non-RFNoC block properties for channel " + << chan_idx << " to prop tree path " << tx_fe_path << " and " + << rx_fe_path); + // TX Standard attributes + subtree->create<std::string>(tx_fe_path / "name").set(get_fe_name(chan_idx, TX_DIRECTION)); + subtree->create<std::string>(tx_fe_path / "connection").set("IQ"); + // RX Standard attributes + subtree->create<std::string>(rx_fe_path / "name").set(get_fe_name(chan_idx, RX_DIRECTION)); + subtree->create<std::string>(rx_fe_path / "connection").set("IQ"); + // TX Antenna + subtree->create<std::string>(tx_fe_path / "antenna" / "value") + .add_coerced_subscriber([this, chan_idx](const std::string& ant) { + this->set_tx_antenna(ant, chan_idx); + }) + .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); }); + subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options") + .set_publisher([this](){ return get_tx_antennas(0); }) + .add_coerced_subscriber([](const std::vector<std::string>&) { + throw uhd::runtime_error("Attempting to update antenna options!"); + }); + // RX Antenna + subtree->create<std::string>(rx_fe_path / "antenna" / "value") + .add_coerced_subscriber([this, chan_idx](const std::string& ant) { + this->set_rx_antenna(ant, chan_idx); + }) + .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); }); + subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options") + .set_publisher([this](){ return get_rx_antennas(0); }) + .add_coerced_subscriber([](const std::vector<std::string>&) { + throw uhd::runtime_error("Attempting to update antenna options!"); + }); + // TX frequency + subtree->create<double>(tx_fe_path / "freq" / "value") + .set_coercer([this, chan_idx](const double freq) { + return this->set_tx_frequency(freq, chan_idx); + }) + .set_publisher([this, chan_idx]() { return this->get_tx_frequency(chan_idx); }); + subtree->create<meta_range_t>(tx_fe_path / "freq" / "range") + .set_publisher([this, chan_idx](){ return get_tx_frequency_range(chan_idx); }) + .add_coerced_subscriber([](const meta_range_t&) { + throw uhd::runtime_error("Attempting to update freq range!"); + }); + // RX frequency + subtree->create<double>(rx_fe_path / "freq" / "value") + .set_coercer([this, chan_idx](const double freq) { + return this->set_rx_frequency(freq, chan_idx); + }) + .set_publisher([this, chan_idx]() { return this->get_rx_frequency(chan_idx); }); + subtree->create<meta_range_t>(rx_fe_path / "freq" / "range") + .set_publisher([this, chan_idx](){ return get_rx_frequency_range(chan_idx); }) + .add_coerced_subscriber([](const meta_range_t&) { + throw uhd::runtime_error("Attempting to update freq range!"); + }); + // TX bandwidth + subtree->create<double>(tx_fe_path / "bandwidth" / "value") + .set(AD9371_TX_MAX_BANDWIDTH) + .set_coercer([this, chan_idx](const double bw) { + return this->set_tx_bandwidth(bw, chan_idx); + }) + .set_publisher([this, chan_idx]() { return this->get_tx_bandwidth(chan_idx); }); + subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range") + .set_publisher([this, chan_idx](){ return get_tx_bandwidth_range(chan_idx); }) + .add_coerced_subscriber([](const meta_range_t&) { + throw uhd::runtime_error("Attempting to update bandwidth range!"); + }); + // RX bandwidth + subtree->create<double>(rx_fe_path / "bandwidth" / "value") + .set(AD9371_RX_MAX_BANDWIDTH) + .set_coercer([this, chan_idx](const double bw) { + return this->set_rx_bandwidth(bw, chan_idx); + }) + .set_publisher([this, chan_idx]() { return this->get_rx_bandwidth(chan_idx); }); + subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range") + .set_publisher([this, chan_idx](){ return get_rx_bandwidth_range(chan_idx); }) + .add_coerced_subscriber([](const meta_range_t&) { + throw uhd::runtime_error("Attempting to update bandwidth range!"); + }); + + // TX gains + std::vector<std::string> tx_gain_names = get_tx_gain_names(chan_idx); + tx_gain_names.push_back("all"); + for (const auto gain_name : tx_gain_names) { + subtree->create<double>(tx_fe_path / "gains" / gain_name / "value") + .set_coercer([this, chan_idx, gain_name](const double gain) { + return this->set_tx_gain(gain, gain_name, chan_idx); + }) + .set_publisher( + [this, chan_idx, gain_name]() { return get_tx_gain(gain_name, chan_idx); }); + subtree->create<meta_range_t>(tx_fe_path / "gains" / gain_name / "range") + .add_coerced_subscriber([](const meta_range_t&) { + throw uhd::runtime_error("Attempting to update gain range!"); + }) + .set_publisher([this, gain_name, chan_idx]() { return get_tx_gain_range(gain_name, chan_idx); }); + } + subtree->create<std::vector<std::string>>(tx_fe_path / "gains/all/profile/options") + .set_publisher( + [this, chan_idx]() { return get_tx_gain_profile_names(chan_idx); }); + subtree->create<std::string>(tx_fe_path / "gains/all/profile/value") + .set_coercer([this, chan_idx](const std::string& profile) { + set_tx_gain_profile(profile, chan_idx); + return profile; + }) + .set_publisher([this, chan_idx]() { return get_tx_gain_profile(chan_idx); }); + + // RX gains + std::vector<std::string> rx_gain_names = get_rx_gain_names(chan_idx); + rx_gain_names.push_back("all"); + for (const auto gain_name : rx_gain_names) { + subtree->create<double>(rx_fe_path / "gains" / gain_name / "value") + .set_coercer([this, chan_idx, gain_name](const double gain) { + return this->set_rx_gain(gain, gain_name, chan_idx); + }) + .set_publisher( + [this, chan_idx, gain_name]() { return get_rx_gain(gain_name, chan_idx); }); + subtree->create<meta_range_t>(rx_fe_path / "gains" / gain_name / "range") + .add_coerced_subscriber([](const meta_range_t&) { + throw uhd::runtime_error("Attempting to update gain range!"); + }) + .set_publisher([this, gain_name, chan_idx]() { return get_rx_gain_range(gain_name, chan_idx); }); + } + subtree->create<std::vector<std::string>>(rx_fe_path / "gains/all/profile/options") + .set_publisher( + [this, chan_idx]() { return get_rx_gain_profile_names(chan_idx); }); + subtree->create<std::string>(rx_fe_path / "gains/all/profile/value") + .set_coercer([this, chan_idx](const std::string& profile) { + set_rx_gain_profile(profile, chan_idx); + return profile; + }) + .set_publisher([this, chan_idx]() { return get_rx_gain_profile(chan_idx); }); + + // LO Specific + // RX LO + subtree->create<meta_range_t>(rx_fe_path / "los" / MAGNESIUM_LO1 / "freq/range") + .set_publisher([this, chan_idx]() { + return this->get_rx_lo_freq_range(MAGNESIUM_LO1, chan_idx); + }); + subtree + ->create<std::vector<std::string>>( + rx_fe_path / "los" / MAGNESIUM_LO1 / "source/options") + .set_publisher([this, chan_idx]() { + return this->get_rx_lo_sources(MAGNESIUM_LO1, chan_idx); + }); + subtree->create<std::string>(rx_fe_path / "los" / MAGNESIUM_LO1 / "source/value") + .add_coerced_subscriber([this, chan_idx](std::string src) { + this->set_rx_lo_source(src, MAGNESIUM_LO1, chan_idx); + }) + .set_publisher([this, chan_idx]() { + return this->get_rx_lo_source(MAGNESIUM_LO1, chan_idx); + }); + subtree->create<double>(rx_fe_path / "los" / MAGNESIUM_LO1 / "freq/value") + .set_publisher( + [this, chan_idx]() { return this->get_rx_lo_freq(MAGNESIUM_LO1, chan_idx); }) + .set_coercer([this, chan_idx](const double freq) { + return this->set_rx_lo_freq(freq, MAGNESIUM_LO1, chan_idx); + }); + subtree->create<meta_range_t>(rx_fe_path / "los" / MAGNESIUM_LO2 / "freq/range") + .set_publisher([this, chan_idx]() { + return this->get_rx_lo_freq_range(MAGNESIUM_LO2, chan_idx); + }); + subtree + ->create<std::vector<std::string>>( + rx_fe_path / "los" / MAGNESIUM_LO2 / "source/options") + .set_publisher([this, chan_idx]() { + return this->get_rx_lo_sources(MAGNESIUM_LO2, chan_idx); + }); + + subtree->create<std::string>(rx_fe_path / "los" / MAGNESIUM_LO2 / "source/value") + .add_coerced_subscriber([this, chan_idx](std::string src) { + this->set_rx_lo_source(src, MAGNESIUM_LO2, chan_idx); + }) + .set_publisher([this, chan_idx]() { + return this->get_rx_lo_source(MAGNESIUM_LO2, chan_idx); + }); + subtree->create<double>(rx_fe_path / "los" / MAGNESIUM_LO2 / "freq/value") + .set_publisher( + [this, chan_idx]() { return this->get_rx_lo_freq(MAGNESIUM_LO2, chan_idx); }) + .set_coercer([this, chan_idx](double freq) { + return this->set_rx_lo_freq(freq, MAGNESIUM_LO2, chan_idx); + }); + // TX LO + subtree->create<meta_range_t>(tx_fe_path / "los" / MAGNESIUM_LO1 / "freq/range") + .set_publisher([this, chan_idx]() { + return this->get_rx_lo_freq_range(MAGNESIUM_LO1, chan_idx); + }); + subtree + ->create<std::vector<std::string>>( + tx_fe_path / "los" / MAGNESIUM_LO1 / "source/options") + .set_publisher([this, chan_idx]() { + return this->get_tx_lo_sources(MAGNESIUM_LO1, chan_idx); + }); + subtree->create<std::string>(tx_fe_path / "los" / MAGNESIUM_LO1 / "source/value") + .add_coerced_subscriber([this, chan_idx](std::string src) { + this->set_tx_lo_source(src, MAGNESIUM_LO1, chan_idx); + }) + .set_publisher([this, chan_idx]() { + return this->get_tx_lo_source(MAGNESIUM_LO1, chan_idx); + }); + subtree->create<double>(tx_fe_path / "los" / MAGNESIUM_LO1 / "freq/value ") + .set_publisher( + [this, chan_idx]() { return this->get_tx_lo_freq(MAGNESIUM_LO1, chan_idx); }) + .set_coercer([this, chan_idx](double freq) { + return this->set_tx_lo_freq(freq, MAGNESIUM_LO1, chan_idx); + }); + subtree->create<meta_range_t>(tx_fe_path / "los" / MAGNESIUM_LO2 / "freq/range") + .set_publisher([this, chan_idx]() { + return this->get_tx_lo_freq_range(MAGNESIUM_LO2, chan_idx); + }); + subtree + ->create<std::vector<std::string>>( + tx_fe_path / "los" / MAGNESIUM_LO2 / "source/options") + .set_publisher([this, chan_idx]() { + return this->get_tx_lo_sources(MAGNESIUM_LO2, chan_idx); + }); + subtree->create<std::string>(tx_fe_path / "los" / MAGNESIUM_LO2 / "source/value") + .add_coerced_subscriber([this, chan_idx](std::string src) { + this->set_tx_lo_source(src, MAGNESIUM_LO2, chan_idx); + }) + .set_publisher([this, chan_idx]() { + return this->get_tx_lo_source(MAGNESIUM_LO2, chan_idx); + }); + subtree->create<double>(tx_fe_path / "los" / MAGNESIUM_LO2 / "freq/value") + .set_publisher( + [this, chan_idx]() { return this->get_tx_lo_freq(MAGNESIUM_LO2, chan_idx); }) + .set_coercer([this, chan_idx](double freq) { + return this->set_tx_lo_freq(freq, MAGNESIUM_LO2, chan_idx); + }); + + // Sensors + auto rx_sensor_names = get_rx_sensor_names(chan_idx); + for (const auto& sensor_name : rx_sensor_names) { + RFNOC_LOG_TRACE("Adding RX sensor " << sensor_name); + get_tree()->create<sensor_value_t>(rx_fe_path / "sensors" / sensor_name) + .add_coerced_subscriber([](const sensor_value_t&) { + throw uhd::runtime_error("Attempting to write to sensor!"); + }) + .set_publisher([this, sensor_name, chan_idx]() { + return get_rx_sensor(sensor_name, chan_idx); + }); + } + auto tx_sensor_names = get_tx_sensor_names(chan_idx); + for (const auto& sensor_name : tx_sensor_names) { + RFNOC_LOG_TRACE("Adding TX sensor " << sensor_name); + get_tree()->create<sensor_value_t>(tx_fe_path / "sensors" / sensor_name) + .add_coerced_subscriber([](const sensor_value_t&) { + throw uhd::runtime_error("Attempting to write to sensor!"); + }) + .set_publisher([this, sensor_name, chan_idx]() { + return get_tx_sensor(sensor_name, chan_idx); + }); + } +} + +void magnesium_radio_control_impl::_init_prop_tree() +{ + for (size_t chan_idx = 0; chan_idx < MAGNESIUM_NUM_CHANS; chan_idx++) { + this->_init_frontend_subtree(get_tree()->subtree(DB_PATH), chan_idx); + } + + // DB EEPROM + get_tree()->create<eeprom_map_t>("eeprom") + .add_coerced_subscriber( + [this](const eeprom_map_t& db_eeprom) { set_db_eeprom(db_eeprom); }) + .set_publisher([this]() { return get_db_eeprom(); }); +} + +void magnesium_radio_control_impl::_init_mpm() +{ + auto block_args = get_block_args(); + RFNOC_LOG_TRACE("Instantiating AD9371 control object..."); + _ad9371 = magnesium_ad9371_iface::uptr( + new magnesium_ad9371_iface(_rpcc, (_radio_slot == "A") ? 0 : 1)); + + if (block_args.has_key("identify")) { + const std::string identify_val = block_args.get("identify"); + int identify_duration = std::atoi(identify_val.c_str()); + if (identify_duration == 0) { + identify_duration = 5; + } + RFNOC_LOG_INFO("Running LED identification process for " << identify_duration + << " seconds."); + _identify_with_leds(identify_duration); + } + + // Note: MCR gets set during the init() call (prior to this), which takes + // in arguments from the device args. So if block_args contains a + // master_clock_rate key, then it should better be whatever the device is + // configured to do. + _master_clock_rate = + _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate"); + if (block_args.cast<double>("master_clock_rate", _master_clock_rate) + != _master_clock_rate) { + throw uhd::runtime_error(str( + boost::format("Master clock rate mismatch. Device returns %f MHz, " + "but should have been %f MHz.") + % (_master_clock_rate / 1e6) + % (block_args.cast<double>("master_clock_rate", _master_clock_rate) / 1e6))); + } + RFNOC_LOG_DEBUG("Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz."); + set_tick_rate(_master_clock_rate); + _n3xx_timekeeper->update_tick_rate(_master_clock_rate); + radio_control_impl::set_rate(_master_clock_rate); +} + diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp deleted file mode 100644 index 405d5955e..000000000 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_impl.cpp +++ /dev/null @@ -1,847 +0,0 @@ -// -// Copyright 2017 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "magnesium_radio_ctrl_impl.hpp" -#include "magnesium_constants.hpp" -#include "magnesium_gain_table.hpp" -#include <uhd/exception.hpp> -#include <uhd/rfnoc/node_ctrl_base.hpp> -#include <uhd/transport/chdr.hpp> -#include <uhd/types/direction.hpp> -#include <uhd/types/eeprom.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/log.hpp> -#include <uhd/utils/math.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/format.hpp> -#include <boost/make_shared.hpp> -#include <cmath> -#include <cstdlib> -#include <sstream> - -using namespace uhd; -using namespace uhd::usrp; -using namespace uhd::rfnoc; -using namespace uhd::math::fp_compare; - -namespace { -/************************************************************************** - * ADF4351 Controls - *************************************************************************/ -/*! - * \param lo_iface Reference to the LO object - * \param freq Frequency (in Hz) of the tone to be generated from the LO - * \param ref_clock_freq Frequency (in Hz) of the reference clock at the - * PLL input of the LO - * \param int_n_mode Integer-N mode on or off - */ -double _lo_set_frequency(adf435x_iface::sptr lo_iface, - const double freq, - const double ref_clock_freq, - const bool int_n_mode) -{ - UHD_LOG_TRACE("MG/ADF4351", - "Attempting to tune low band LO to " << freq << " Hz with ref clock freq " - << ref_clock_freq); - lo_iface->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED); - lo_iface->set_reference_freq(ref_clock_freq); - lo_iface->set_prescaler(adf435x_iface::PRESCALER_4_5); - const double actual_freq = lo_iface->set_frequency(freq, int_n_mode); - lo_iface->set_output_power( - adf435x_iface::RF_OUTPUT_A, adf435x_iface::OUTPUT_POWER_2DBM); - lo_iface->set_output_power( - adf435x_iface::RF_OUTPUT_B, adf435x_iface::OUTPUT_POWER_2DBM); - lo_iface->set_charge_pump_current(adf435x_iface::CHARGE_PUMP_CURRENT_0_31MA); - return actual_freq; -} - -/*! Configure and enable LO - * - * Will tune it to requested frequency and enable outputs. - * - * \param lo_iface Reference to the LO object - * \param lo_freq Frequency (in Hz) of the tone to be generated from the LO - * \param ref_clock_freq Frequency (in Hz) of the reference clock at the - * PLL input of the LO - * \param int_n_mode Integer-N mode on or off - * \returns the actual frequency the LO is running at - */ -double _lo_enable(adf435x_iface::sptr lo_iface, - const double lo_freq, - const double ref_clock_freq, - const bool int_n_mode) -{ - const double actual_lo_freq = - _lo_set_frequency(lo_iface, lo_freq, ref_clock_freq, int_n_mode); - lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_A, true); - lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_B, true); - lo_iface->commit(); - return actual_lo_freq; -} - -/*! Disable LO - */ -void _lo_disable(adf435x_iface::sptr lo_iface) -{ - lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_A, false); - lo_iface->set_output_enable(adf435x_iface::RF_OUTPUT_B, false); - lo_iface->commit(); -} -} // namespace - - -/****************************************************************************** - * Structors - *****************************************************************************/ -UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(magnesium_radio_ctrl) -{ - UHD_LOG_TRACE(unique_id(), "Entering magnesium_radio_ctrl_impl ctor..."); - const char radio_slot_name[2] = {'A', 'B'}; - _radio_slot = radio_slot_name[get_block_id().get_block_count()]; - UHD_LOG_TRACE(unique_id(), "Radio slot: " << _radio_slot); - _rpc_prefix = (_radio_slot == "A") ? "db_0_" : "db_1_"; - - _init_defaults(); - _init_peripherals(); - _init_prop_tree(); -} - -magnesium_radio_ctrl_impl::~magnesium_radio_ctrl_impl() -{ - UHD_LOG_TRACE(unique_id(), "magnesium_radio_ctrl_impl::dtor() "); -} - - -/****************************************************************************** - * API Calls - *****************************************************************************/ -double magnesium_radio_ctrl_impl::set_rate(double requested_rate) -{ - meta_range_t rates; - for (const double rate : MAGNESIUM_RADIO_RATES) { - rates.push_back(range_t(rate)); - } - - const double rate = rates.clip(requested_rate); - if (!math::frequencies_are_equal(requested_rate, rate)) { - UHD_LOG_WARNING(unique_id(), - "Coercing requested sample rate from " << (requested_rate / 1e6) << " to " - << (rate / 1e6)); - } - - const double current_rate = get_rate(); - if (math::frequencies_are_equal(current_rate, rate)) { - UHD_LOG_DEBUG( - unique_id(), "Rate is already at " << rate << ". Skipping set_rate()"); - return current_rate; - } - - std::lock_guard<std::mutex> l(_set_lock); - // Now commit to device. First, disable LOs. - _lo_disable(_tx_lo); - _lo_disable(_rx_lo); - const double new_rate = _ad9371->set_master_clock_rate(rate); - // Frequency settings apply to both channels, no loop needed. Will also - // re-enable the lowband LOs if they were used. - set_rx_frequency(get_rx_frequency(0), 0); - set_tx_frequency(get_tx_frequency(0), 0); - // Gain and bandwidth need to be looped: - for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) { - set_rx_gain(get_rx_gain(radio_idx), radio_idx); - set_tx_gain(get_rx_gain(radio_idx), radio_idx); - set_rx_bandwidth(get_rx_bandwidth(radio_idx), radio_idx); - set_tx_bandwidth(get_tx_bandwidth(radio_idx), radio_idx); - } - radio_ctrl_impl::set_rate(new_rate); - return new_rate; -} - -void magnesium_radio_ctrl_impl::set_tx_antenna(const std::string& ant, const size_t chan) -{ - if (ant != get_tx_antenna(chan)) { - throw uhd::value_error( - str(boost::format("[%s] Requesting invalid TX antenna value: %s") - % unique_id() % ant)); - } - // We can't actually set the TX antenna, so let's stop here. -} - -void magnesium_radio_ctrl_impl::set_rx_antenna(const std::string& ant, const size_t chan) -{ - UHD_ASSERT_THROW(chan <= MAGNESIUM_NUM_CHANS); - if (std::find(MAGNESIUM_RX_ANTENNAS.begin(), MAGNESIUM_RX_ANTENNAS.end(), ant) - == MAGNESIUM_RX_ANTENNAS.end()) { - throw uhd::value_error( - str(boost::format("[%s] Requesting invalid RX antenna value: %s") - % unique_id() % ant)); - } - UHD_LOG_TRACE(unique_id(), "Setting RX antenna to " << ant << " for chan " << chan); - magnesium_cpld_ctrl::chan_sel_t chan_sel = chan == 0 ? magnesium_cpld_ctrl::CHAN1 - : magnesium_cpld_ctrl::CHAN2; - _update_atr_switches(chan_sel, RX_DIRECTION, ant); - - radio_ctrl_impl::set_rx_antenna(ant, chan); -} - -double magnesium_radio_ctrl_impl::set_tx_frequency( - const double req_freq, const size_t chan) -{ - const double freq = MAGNESIUM_FREQ_RANGE.clip(req_freq); - UHD_LOG_TRACE(unique_id(), "set_tx_frequency(f=" << freq << ", chan=" << chan << ")"); - _desired_rf_freq[TX_DIRECTION] = freq; - std::lock_guard<std::mutex> l(_set_lock); - // We need to set the switches on both channels, because they share an LO. - // This way, if we tune channel 0 it will not put channel 1 into a bad - // state. - _update_tx_freq_switches(freq, _tx_bypass_amp, magnesium_cpld_ctrl::BOTH); - const std::string ad9371_source = this->get_tx_lo_source(MAGNESIUM_LO1, chan); - const std::string adf4351_source = this->get_tx_lo_source(MAGNESIUM_LO2, chan); - UHD_ASSERT_THROW(adf4351_source == "internal"); - double coerced_if_freq = freq; - - if (_map_freq_to_tx_band(_tx_band_map, freq) == tx_band::LOWBAND) { - _is_low_band[TX_DIRECTION] = true; - const double desired_low_freq = MAGNESIUM_TX_IF_FREQ - freq; - coerced_if_freq = - this->_set_tx_lo_freq(adf4351_source, MAGNESIUM_LO2, desired_low_freq, chan) - + freq; - UHD_LOG_TRACE(unique_id(), "coerced_if_freq = " << coerced_if_freq); - } else { - _is_low_band[TX_DIRECTION] = false; - _lo_disable(_tx_lo); - } - // external LO required to tune at 2xdesired_frequency. - const double desired_if_freq = (ad9371_source == "internal") ? coerced_if_freq - : 2 * coerced_if_freq; - - this->_set_tx_lo_freq(ad9371_source, MAGNESIUM_LO1, desired_if_freq, chan); - this->_update_freq(chan, TX_DIRECTION); - this->_update_gain(chan, TX_DIRECTION); - return radio_ctrl_impl::get_tx_frequency(chan); -} - -void magnesium_radio_ctrl_impl::_update_gain( - const size_t chan, const uhd::direction_t dir) -{ - const std::string fe = (dir == TX_DIRECTION) ? "tx_frontends" : "rx_frontends"; - const double freq = (dir == TX_DIRECTION) ? this->get_tx_frequency(chan) - : this->get_rx_frequency(chan); - this->_set_all_gain(this->_get_all_gain(chan, dir), freq, chan, dir); -} - -void magnesium_radio_ctrl_impl::_update_freq( - const size_t chan, const uhd::direction_t dir) -{ - const std::string ad9371_source = dir == TX_DIRECTION - ? this->get_tx_lo_source(MAGNESIUM_LO1, chan) - : this->get_rx_lo_source(MAGNESIUM_LO1, chan); - - const double ad9371_freq = ad9371_source == "external" ? _ad9371_freq[dir] / 2 - : _ad9371_freq[dir]; - const double rf_freq = _is_low_band[dir] ? ad9371_freq - _adf4351_freq[dir] - : ad9371_freq; - - UHD_LOG_TRACE(unique_id(), "RF freq = " << rf_freq); - UHD_ASSERT_THROW(fp_compare_epsilon<double>(rf_freq) >= 0); - UHD_ASSERT_THROW(fp_compare_epsilon<double>(std::abs(rf_freq - _desired_rf_freq[dir])) - <= _master_clock_rate / 2); - if (dir == RX_DIRECTION) { - radio_ctrl_impl::set_rx_frequency(rf_freq, chan); - } else if (dir == TX_DIRECTION) { - radio_ctrl_impl::set_tx_frequency(rf_freq, chan); - } else { - UHD_THROW_INVALID_CODE_PATH(); - } -} - -double magnesium_radio_ctrl_impl::set_rx_frequency( - const double req_freq, const size_t chan) -{ - const double freq = MAGNESIUM_FREQ_RANGE.clip(req_freq); - UHD_LOG_TRACE(unique_id(), "set_rx_frequency(f=" << freq << ", chan=" << chan << ")"); - _desired_rf_freq[RX_DIRECTION] = freq; - std::lock_guard<std::mutex> l(_set_lock); - // We need to set the switches on both channels, because they share an LO. - // This way, if we tune channel 0 it will not put channel 1 into a bad - // state. - _update_rx_freq_switches(freq, _rx_bypass_lnas, magnesium_cpld_ctrl::BOTH); - const std::string ad9371_source = this->get_rx_lo_source(MAGNESIUM_LO1, chan); - const std::string adf4351_source = this->get_rx_lo_source(MAGNESIUM_LO2, chan); - UHD_ASSERT_THROW(adf4351_source == "internal"); - double coerced_if_freq = freq; - - if (_map_freq_to_rx_band(_rx_band_map, freq) == rx_band::LOWBAND) { - _is_low_band[RX_DIRECTION] = true; - const double desired_low_freq = MAGNESIUM_RX_IF_FREQ - freq; - coerced_if_freq = - this->_set_rx_lo_freq(adf4351_source, MAGNESIUM_LO2, desired_low_freq, chan) - + freq; - UHD_LOG_TRACE(unique_id(), "coerced_if_freq = " << coerced_if_freq); - } else { - _is_low_band[RX_DIRECTION] = false; - _lo_disable(_rx_lo); - } - // external LO required to tune at 2xdesired_frequency. - const double desired_if_freq = ad9371_source == "internal" ? coerced_if_freq - : 2 * coerced_if_freq; - - this->_set_rx_lo_freq(ad9371_source, MAGNESIUM_LO1, desired_if_freq, chan); - - this->_update_freq(chan, RX_DIRECTION); - this->_update_gain(chan, RX_DIRECTION); - - return radio_ctrl_impl::get_rx_frequency(chan); -} - -double magnesium_radio_ctrl_impl::get_tx_frequency(const size_t chan) -{ - UHD_LOG_TRACE(unique_id(), "get_tx_frequency(chan=" << chan << ")"); - return radio_ctrl_impl::get_tx_frequency(chan); -} - -double magnesium_radio_ctrl_impl::get_rx_frequency(const size_t chan) -{ - UHD_LOG_TRACE(unique_id(), "get_rx_frequency(chan=" << chan << ")"); - return radio_ctrl_impl::get_rx_frequency(chan); -} -double magnesium_radio_ctrl_impl::set_rx_bandwidth( - const double bandwidth, const size_t chan) -{ - std::lock_guard<std::mutex> l(_set_lock); - _ad9371->set_bandwidth(bandwidth, chan, RX_DIRECTION); - // FIXME: setting analog bandwidth on AD9371 take no effect. - // Remove this warning when ADI can confirm that it works. - UHD_LOG_WARNING(unique_id(), - "set_rx_bandwidth take no effect on AD9371. " - "Default analog bandwidth is 100MHz"); - return AD9371_RX_MAX_BANDWIDTH; -} - -double magnesium_radio_ctrl_impl::set_tx_bandwidth( - const double bandwidth, const size_t chan) -{ - std::lock_guard<std::mutex> l(_set_lock); - _ad9371->set_bandwidth(bandwidth, chan, TX_DIRECTION); - // FIXME: setting analog bandwidth on AD9371 take no effect. - // Remove this warning when ADI can confirm that it works. - UHD_LOG_WARNING(unique_id(), - "set_tx_bandwidth take no effect on AD9371. " - "Default analog bandwidth is 100MHz"); - return AD9371_TX_MAX_BANDWIDTH; -} - -double magnesium_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan) -{ - std::lock_guard<std::mutex> l(_set_lock); - UHD_LOG_TRACE(unique_id(), "set_tx_gain(gain=" << gain << ", chan=" << chan << ")"); - const double coerced_gain = - _set_all_gain(gain, this->get_tx_frequency(chan), chan, TX_DIRECTION); - radio_ctrl_impl::set_tx_gain(coerced_gain, chan); - return coerced_gain; -} - -double magnesium_radio_ctrl_impl::_set_tx_gain( - const std::string& name, const double gain, const size_t chan) -{ - std::lock_guard<std::mutex> l(_set_lock); - UHD_LOG_TRACE(unique_id(), - "_set_tx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")"); - UHD_LOG_TRACE(unique_id(), - "_set_tx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")"); - double clip_gain = 0; - if (name == MAGNESIUM_GAIN1) { - clip_gain = uhd::clip(gain, AD9371_MIN_TX_GAIN, AD9371_MAX_TX_GAIN); - _ad9371_att[TX_DIRECTION] = clip_gain; - } else if (name == MAGNESIUM_GAIN2) { - clip_gain = uhd::clip(gain, DSA_MIN_GAIN, DSA_MAX_GAIN); - _dsa_att[TX_DIRECTION] = clip_gain; - } else if (name == MAGNESIUM_AMP) { - clip_gain = gain > 0.0 ? AMP_MAX_GAIN : AMP_MIN_GAIN; - _amp_bypass[TX_DIRECTION] = clip_gain == 0.0; - } else { - throw uhd::value_error("Could not find gain element " + name); - } - UHD_LOG_TRACE(unique_id(), "_set_tx_gain calling update gain"); - this->_set_all_gain(this->_get_all_gain(chan, TX_DIRECTION), - this->get_tx_frequency(chan), - chan, - TX_DIRECTION); - return clip_gain; -} - -double magnesium_radio_ctrl_impl::_get_tx_gain( - const std::string& name, const size_t /*chan*/ -) -{ - std::lock_guard<std::mutex> l(_set_lock); - if (name == MAGNESIUM_GAIN1) { - return _ad9371_att[TX_DIRECTION]; - } else if (name == MAGNESIUM_GAIN2) { - return _dsa_att[TX_DIRECTION]; - } else if (name == MAGNESIUM_AMP) { - return _amp_bypass[TX_DIRECTION] ? AMP_MIN_GAIN : AMP_MAX_GAIN; - } else { - throw uhd::value_error("Could not find gain element " + name); - } -} - -double magnesium_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) -{ - std::lock_guard<std::mutex> l(_set_lock); - UHD_LOG_TRACE(unique_id(), "set_rx_gain(gain=" << gain << ", chan=" << chan << ")"); - const double coerced_gain = - _set_all_gain(gain, this->get_rx_frequency(chan), chan, RX_DIRECTION); - radio_ctrl_impl::set_rx_gain(coerced_gain, chan); - return coerced_gain; -} - -double magnesium_radio_ctrl_impl::_set_rx_gain( - const std::string& name, const double gain, const size_t chan) -{ - std::lock_guard<std::mutex> l(_set_lock); - UHD_LOG_TRACE(unique_id(), - "_set_rx_gain(name=" << name << ", gain=" << gain << ", chan=" << chan << ")"); - double clip_gain = 0; - if (name == MAGNESIUM_GAIN1) { - clip_gain = uhd::clip(gain, AD9371_MIN_RX_GAIN, AD9371_MAX_RX_GAIN); - _ad9371_att[RX_DIRECTION] = clip_gain; - } else if (name == MAGNESIUM_GAIN2) { - clip_gain = uhd::clip(gain, DSA_MIN_GAIN, DSA_MAX_GAIN); - _dsa_att[RX_DIRECTION] = clip_gain; - } else if (name == MAGNESIUM_AMP) { - clip_gain = gain > 0.0 ? AMP_MAX_GAIN : AMP_MIN_GAIN; - _amp_bypass[RX_DIRECTION] = clip_gain == 0.0; - } else { - throw uhd::value_error("Could not find gain element " + name); - } - UHD_LOG_TRACE(unique_id(), "_set_rx_gain calling update gain"); - this->_set_all_gain(this->_get_all_gain(chan, RX_DIRECTION), - this->get_rx_frequency(chan), - chan, - RX_DIRECTION); - return clip_gain; // not really any coerced here (only clip) for individual gain -} - -double magnesium_radio_ctrl_impl::_get_rx_gain( - const std::string& name, const size_t /*chan*/ -) -{ - std::lock_guard<std::mutex> l(_set_lock); - - if (name == MAGNESIUM_GAIN1) { - return _ad9371_att[RX_DIRECTION]; - } else if (name == MAGNESIUM_GAIN2) { - return _dsa_att[RX_DIRECTION]; - } else if (name == MAGNESIUM_AMP) { - return _amp_bypass[RX_DIRECTION] ? AMP_MIN_GAIN : AMP_MAX_GAIN; - } else { - throw uhd::value_error("Could not find gain element " + name); - } -} - -std::vector<std::string> magnesium_radio_ctrl_impl::get_rx_lo_names(const size_t /*chan*/ -) -{ - return std::vector<std::string>{MAGNESIUM_LO1, MAGNESIUM_LO2}; -} - -std::vector<std::string> magnesium_radio_ctrl_impl::get_rx_lo_sources( - const std::string& name, const size_t /*chan*/ -) -{ - if (name == MAGNESIUM_LO2) { - return std::vector<std::string>{"internal"}; - } else if (name == MAGNESIUM_LO1) { - return std::vector<std::string>{"internal", "external"}; - } else { - throw uhd::value_error("Could not find LO stage " + name); - } -} - -freq_range_t magnesium_radio_ctrl_impl::get_rx_lo_freq_range( - const std::string& name, const size_t /*chan*/ -) -{ - if (name == MAGNESIUM_LO1) { - return freq_range_t{ADF4351_MIN_FREQ, ADF4351_MAX_FREQ}; - } else if (name == MAGNESIUM_LO2) { - return freq_range_t{AD9371_MIN_FREQ, AD9371_MAX_FREQ}; - } else { - throw uhd::value_error("Could not find LO stage " + name); - } -} - -void magnesium_radio_ctrl_impl::set_rx_lo_source( - const std::string& src, const std::string& name, const size_t /*chan*/ -) -{ - // TODO: checking what options are there - std::lock_guard<std::mutex> l(_set_lock); - UHD_LOG_TRACE(unique_id(), "Setting RX LO " << name << " to " << src); - - if (name == MAGNESIUM_LO1) { - _ad9371->set_lo_source(src, RX_DIRECTION); - } else { - UHD_LOG_ERROR(unique_id(), - "RX LO " << name << " does not support setting source to " << src); - } -} - -const std::string magnesium_radio_ctrl_impl::get_rx_lo_source( - const std::string& name, const size_t /*chan*/ -) -{ - if (name == MAGNESIUM_LO1) { - // TODO: should we use this from cache? - return _ad9371->get_lo_source(RX_DIRECTION); - } - return "internal"; -} - -double magnesium_radio_ctrl_impl::_set_rx_lo_freq(const std::string source, - const std::string name, - const double freq, - const size_t chan) -{ - double coerced_lo_freq = freq; - if (source != "internal") { - UHD_LOG_WARNING( - unique_id(), "LO source is not internal. This set frequency will be ignored"); - if (name == MAGNESIUM_LO1) { - // handle ad9371 external LO case - coerced_lo_freq = freq; - _ad9371_freq[RX_DIRECTION] = coerced_lo_freq; - } - } else { - if (name == MAGNESIUM_LO1) { - coerced_lo_freq = _ad9371->set_frequency(freq, chan, RX_DIRECTION); - _ad9371_freq[RX_DIRECTION] = coerced_lo_freq; - } else if (name == MAGNESIUM_LO2) { - // TODO: no hardcode the init_n_mode - coerced_lo_freq = _lo_enable(_rx_lo, freq, _master_clock_rate, false); - _adf4351_freq[RX_DIRECTION] = coerced_lo_freq; - } else { - UHD_LOG_WARNING(unique_id(), - "There's no LO with this name of " - << name << " in the system. This set rx lo freq will be ignored"); - }; - } - return coerced_lo_freq; -} - -double magnesium_radio_ctrl_impl::set_rx_lo_freq( - double freq, const std::string& name, const size_t chan) -{ - UHD_LOG_TRACE( - unique_id(), "Setting rx lo frequency for " << name << " with freq = " << freq); - std::lock_guard<std::mutex> l(_set_lock); - std::string source = this->get_rx_lo_source(name, chan); - const double coerced_lo_freq = this->_set_rx_lo_freq(source, name, freq, chan); - this->_update_freq(chan, RX_DIRECTION); - this->_update_gain(chan, RX_DIRECTION); - return coerced_lo_freq; -} - -double magnesium_radio_ctrl_impl::get_rx_lo_freq( - const std::string& name, const size_t chan) -{ - UHD_LOG_TRACE(unique_id(), "Getting rx lo frequency for " << name); - std::string source = this->get_rx_lo_source(name, chan); - if (name == MAGNESIUM_LO1) { - return _ad9371_freq[RX_DIRECTION]; - } else if (name == "adf4531") { - return _adf4351_freq[RX_DIRECTION]; - } else { - UHD_LOG_ERROR(unique_id(), - "There's no LO with this name of " - << name << " in the system. This set rx lo freq will be ignored"); - } - UHD_THROW_INVALID_CODE_PATH(); -} - -// TX LO -std::vector<std::string> magnesium_radio_ctrl_impl::get_tx_lo_names(const size_t /*chan*/ -) -{ - return std::vector<std::string>{MAGNESIUM_LO1, MAGNESIUM_LO2}; -} - -std::vector<std::string> magnesium_radio_ctrl_impl::get_tx_lo_sources( - const std::string& name, const size_t /*chan*/ -) -{ - if (name == MAGNESIUM_LO2) { - return std::vector<std::string>{"internal"}; - } else if (name == MAGNESIUM_LO1) { - return std::vector<std::string>{"internal", "external"}; - } else { - throw uhd::value_error("Could not find LO stage " + name); - } -} - -freq_range_t magnesium_radio_ctrl_impl::get_tx_lo_freq_range( - const std::string& name, const size_t /*chan*/ -) -{ - if (name == MAGNESIUM_LO2) { - return freq_range_t{ADF4351_MIN_FREQ, ADF4351_MAX_FREQ}; - } else if (name == MAGNESIUM_LO1) { - return freq_range_t{AD9371_MIN_FREQ, AD9371_MAX_FREQ}; - } else { - throw uhd::value_error("Could not find LO stage " + name); - } -} - -void magnesium_radio_ctrl_impl::set_tx_lo_source( - const std::string& src, const std::string& name, const size_t /*chan*/ -) -{ - // TODO: checking what options are there - std::lock_guard<std::mutex> l(_set_lock); - UHD_LOG_TRACE(unique_id(), "Setting TX LO " << name << " to " << src); - if (name == MAGNESIUM_LO1) { - _ad9371->set_lo_source(src, TX_DIRECTION); - } else { - UHD_LOG_ERROR(unique_id(), - "TX LO " << name << " does not support setting source to " << src); - } -} - -const std::string magnesium_radio_ctrl_impl::get_tx_lo_source( - const std::string& name, const size_t /*chan*/ -) -{ - if (name == MAGNESIUM_LO1) { - // TODO: should we use this from cache? - return _ad9371->get_lo_source(TX_DIRECTION); - } - return "internal"; -} - -double magnesium_radio_ctrl_impl::_set_tx_lo_freq(const std::string source, - const std::string name, - const double freq, - const size_t chan) -{ - double coerced_lo_freq = freq; - if (source != "internal") { - UHD_LOG_WARNING( - unique_id(), "LO source is not internal. This set frequency will be ignored"); - if (name == MAGNESIUM_LO1) { - // handle ad9371 external LO case - coerced_lo_freq = freq; - _ad9371_freq[TX_DIRECTION] = coerced_lo_freq; - } - } else { - if (name == MAGNESIUM_LO1) { - coerced_lo_freq = _ad9371->set_frequency(freq, chan, TX_DIRECTION); - _ad9371_freq[TX_DIRECTION] = coerced_lo_freq; - } else if (name == MAGNESIUM_LO2) { - // TODO: no hardcode the int_n_mode - const bool int_n_mode = false; - coerced_lo_freq = _lo_enable(_tx_lo, freq, _master_clock_rate, int_n_mode); - _adf4351_freq[TX_DIRECTION] = coerced_lo_freq; - } else { - UHD_LOG_WARNING(unique_id(), - "There's no LO with this name of " - << name << " in the system. This set tx lo freq will be ignored"); - }; - } - return coerced_lo_freq; -} - -double magnesium_radio_ctrl_impl::set_tx_lo_freq( - double freq, const std::string& name, const size_t chan) -{ - UHD_LOG_TRACE( - unique_id(), "Setting tx lo frequency for " << name << " with freq = " << freq); - std::string source = this->get_tx_lo_source(name, chan); - const double return_freq = this->_set_tx_lo_freq(source, name, freq, chan); - this->_update_freq(chan, TX_DIRECTION); - this->_update_gain(chan, TX_DIRECTION); - return return_freq; -} - -double magnesium_radio_ctrl_impl::get_tx_lo_freq( - const std::string& name, const size_t chan) -{ - UHD_LOG_TRACE(unique_id(), "Getting tx lo frequency for " << name); - std::string source = this->get_tx_lo_source(name, chan); - if (name == MAGNESIUM_LO1) { - return _ad9371_freq[TX_DIRECTION]; - } else if (name == MAGNESIUM_LO2) { - return _adf4351_freq[TX_DIRECTION]; - } else { - UHD_LOG_ERROR( - unique_id(), "There's no LO with this name of " << name << " in the system."); - }; - - UHD_THROW_INVALID_CODE_PATH(); -} - - -size_t magnesium_radio_ctrl_impl::get_chan_from_dboard_fe( - const std::string& fe, const direction_t /* dir */ -) -{ - return boost::lexical_cast<size_t>(fe); -} - -std::string magnesium_radio_ctrl_impl::get_dboard_fe_from_chan( - const size_t chan, const direction_t /* dir */ -) -{ - return std::to_string(chan); -} - - -void magnesium_radio_ctrl_impl::_remap_band_limits( - const std::string band_map, const uhd::direction_t dir) -{ - const size_t dflt_band_size = (dir == RX_DIRECTION) ? _rx_band_map.size() - : _tx_band_map.size(); - - std::vector<std::string> band_map_split; - double band_lim; - - UHD_LOG_DEBUG(unique_id(), "Using user specified frequency band limits"); - boost::split(band_map_split, band_map, boost::is_any_of(";")); - if (band_map_split.size() != dflt_band_size) { - throw uhd::runtime_error(( - boost::format( - "size %s of given frequency band map doesn't match the required size: %s") - % band_map_split.size() % dflt_band_size) - .str()); - } - UHD_LOG_DEBUG(unique_id(), "newly used band limits: "); - for (size_t i = 0; i < band_map_split.size(); i++) { - try { - band_lim = std::stod(band_map_split.at(i)); - } catch (...) { - throw uhd::value_error( - (boost::format("error while converting given frequency string %s " - "to a double value") - % band_map_split.at(i)) - .str()); - } - UHD_LOG_DEBUG(unique_id(), "band " << i << " limit: " << band_lim << "Hz"); - if (dir == RX_DIRECTION) - _rx_band_map.at(i) = band_lim; - else - _tx_band_map.at(i) = band_lim; - } -} - - -void magnesium_radio_ctrl_impl::set_rpc_client( - uhd::rpc_client::sptr rpcc, const uhd::device_addr_t& block_args) -{ - _rpcc = rpcc; - _block_args = block_args; - UHD_LOG_TRACE(unique_id(), "Instantiating AD9371 control object..."); - _ad9371 = magnesium_ad9371_iface::uptr( - new magnesium_ad9371_iface(_rpcc, (_radio_slot == "A") ? 0 : 1)); - - if (block_args.has_key("identify")) { - const std::string identify_val = block_args.get("identify"); - int identify_duration = std::atoi(identify_val.c_str()); - if (identify_duration == 0) { - identify_duration = 5; - } - UHD_LOG_INFO(unique_id(), - "Running LED identification process for " << identify_duration - << " seconds."); - _identify_with_leds(identify_duration); - } - - if (block_args.has_key("tx_gain_profile")) { - UHD_LOG_INFO(unique_id(), - "Using user specified TX gain profile: " << block_args.get( - "tx_gain_profile")); - _gain_profile[TX_DIRECTION] = block_args.get("tx_gain_profile"); - } - - if (block_args.has_key("rx_gain_profile")) { - UHD_LOG_INFO(unique_id(), - "Using user specified RX gain profile: " << block_args.get( - "rx_gain_profile")); - _gain_profile[RX_DIRECTION] = block_args.get("rx_gain_profile"); - } - - if (block_args.has_key("rx_band_map")) { - UHD_LOG_INFO(unique_id(), "Using user specified RX band limits"); - _remap_band_limits(block_args.get("rx_band_map"), RX_DIRECTION); - } - - if (block_args.has_key("tx_band_map")) { - UHD_LOG_INFO(unique_id(), "Using user specified TX band limits"); - _remap_band_limits(block_args.get("tx_band_map"), TX_DIRECTION); - } - - // Note: MCR gets set during the init() call (prior to this), which takes - // in arguments from the device args. So if block_args contains a - // master_clock_rate key, then it should better be whatever the device is - // configured to do. - _master_clock_rate = - _rpcc->request_with_token<double>(_rpc_prefix + "get_master_clock_rate"); - if (block_args.cast<double>("master_clock_rate", _master_clock_rate) - != _master_clock_rate) { - throw uhd::runtime_error(str( - boost::format("Master clock rate mismatch. Device returns %f MHz, " - "but should have been %f MHz.") - % (_master_clock_rate / 1e6) - % (block_args.cast<double>("master_clock_rate", _master_clock_rate) / 1e6))); - } - UHD_LOG_DEBUG( - unique_id(), "Master Clock Rate is: " << (_master_clock_rate / 1e6) << " MHz."); - radio_ctrl_impl::set_rate(_master_clock_rate); - - // EEPROM paths subject to change FIXME - const size_t db_idx = get_block_id().get_block_count(); - _tree->access<eeprom_map_t>(_root_path / "eeprom") - .add_coerced_subscriber([this, db_idx](const eeprom_map_t& db_eeprom) { - this->_rpcc->notify_with_token("set_db_eeprom", db_idx, db_eeprom); - }) - .set_publisher([this, db_idx]() { - return this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx); - }); - - // Init sensors - for (const auto& dir : std::vector<direction_t>{RX_DIRECTION, TX_DIRECTION}) { - for (size_t chan_idx = 0; chan_idx < MAGNESIUM_NUM_CHANS; chan_idx++) { - _init_mpm_sensors(dir, chan_idx); - } - } -} - -bool magnesium_radio_ctrl_impl::get_lo_lock_status(const direction_t dir) -{ - if (not(bool(_rpcc))) { - UHD_LOG_DEBUG(unique_id(), "Reported no LO lock due to lack of RPC connection."); - return false; - } - - const std::string trx = (dir == RX_DIRECTION) ? "rx" : "tx"; - const size_t chan = 0; // They're the same after all - const double freq = (dir == RX_DIRECTION) ? get_rx_frequency(chan) - : get_tx_frequency(chan); - - bool lo_lock = - _rpcc->request_with_token<bool>(_rpc_prefix + "get_ad9371_lo_lock", trx); - UHD_LOG_TRACE(unique_id(), - "AD9371 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No")); - if (lo_lock and _map_freq_to_rx_band(_rx_band_map, freq) == rx_band::LOWBAND) { - lo_lock = - lo_lock - && _rpcc->request_with_token<bool>(_rpc_prefix + "get_lowband_lo_lock", trx); - UHD_LOG_TRACE(unique_id(), - "ADF4351 " << trx << " LO reports lock: " << (lo_lock ? "Yes" : "No")); - } - - return lo_lock; -} - -UHD_RFNOC_BLOCK_REGISTER(magnesium_radio_ctrl, "MagnesiumRadio"); diff --git a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp b/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp deleted file mode 100644 index 89db61428..000000000 --- a/host/lib/usrp/dboard/magnesium/magnesium_radio_ctrl_init.cpp +++ /dev/null @@ -1,713 +0,0 @@ -// -// Copyright 2017 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include "magnesium_constants.hpp" -#include "magnesium_radio_ctrl_impl.hpp" -#include <uhd/transport/chdr.hpp> -#include <uhd/types/eeprom.hpp> -#include <uhd/types/sensors.hpp> -#include <uhd/utils/log.hpp> -#include <uhdlib/usrp/cores/spi_core_3000.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/algorithm/string/case_conv.hpp> -#include <boost/algorithm/string/split.hpp> -#include <string> -#include <vector> -using namespace uhd; -using namespace uhd::rfnoc; - -namespace { -enum slave_select_t { SEN_CPLD = 1, SEN_TX_LO = 2, SEN_RX_LO = 4, SEN_PHASE_DAC = 8 }; - -constexpr double MAGNESIUM_DEFAULT_FREQ = 2.5e9; // Hz -constexpr double MAGNESIUM_DEFAULT_BANDWIDTH = 100e6; // Hz -constexpr char MAGNESIUM_DEFAULT_RX_ANTENNA[] = "RX2"; -constexpr char MAGNESIUM_DEFAULT_TX_ANTENNA[] = "TX/RX"; - -//! Magnesium gain profile options -const std::vector<std::string> MAGNESIUM_GP_OPTIONS = {"manual", - "default", - "default_rf_filter_bypass_always_on", - "default_rf_filter_bypass_always_off"}; -} // namespace - -//! Helper function to extract single value of port number. -// -// Each GPIO pins can be controlled by each radio output ports. -// This function convert the format of attribute "Radio_N_M" -// to a single value port number = N*number_of_port_per_radio + M - -uint32_t extract_port_number(std::string radio_src_string, uhd::property_tree::sptr ptree) -{ - std::string s_val = "0"; - std::vector<std::string> radio_strings; - boost::algorithm::split(radio_strings, - radio_src_string, - boost::is_any_of("_/"), - boost::token_compress_on); - boost::to_lower(radio_strings[0]); - if (radio_strings.size() < 3) { - throw uhd::runtime_error(str( - boost::format("%s is an invalid GPIO source string.") % radio_src_string)); - } - size_t radio_num = std::stoi(radio_strings[1]); - size_t port_num = std::stoi(radio_strings[2]); - if (radio_strings[0] != "radio") { - throw uhd::runtime_error( - "Front panel GPIO bank can only accept a radio block as its driver."); - } - std::string radio_port_out = "Radio_" + radio_strings[1] + "/ports/out"; - std::string radio_port_path = radio_port_out + "/" + radio_strings[2]; - auto found = ptree->exists(fs_path("xbar") / radio_port_path); - if (not found) { - throw uhd::runtime_error( - str(boost::format("Could not find radio port %s.\n") % radio_port_path)); - } - size_t port_size = ptree->list(fs_path("xbar") / radio_port_out).size(); - return radio_num * port_size + port_num; -} - -void magnesium_radio_ctrl_impl::_init_defaults() -{ - UHD_LOG_TRACE(unique_id(), "Initializing defaults..."); - const size_t num_rx_chans = get_output_ports().size(); - const size_t num_tx_chans = get_input_ports().size(); - - UHD_LOG_TRACE(unique_id(), - "Num TX chans: " << num_tx_chans << " Num RX chans: " << num_rx_chans); - - for (size_t chan = 0; chan < num_rx_chans; chan++) { - radio_ctrl_impl::set_rx_frequency(MAGNESIUM_DEFAULT_FREQ, chan); - radio_ctrl_impl::set_rx_gain(0, chan); - radio_ctrl_impl::set_rx_antenna(MAGNESIUM_DEFAULT_RX_ANTENNA, chan); - radio_ctrl_impl::set_rx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan); - } - - for (size_t chan = 0; chan < num_tx_chans; chan++) { - radio_ctrl_impl::set_tx_frequency(MAGNESIUM_DEFAULT_FREQ, chan); - radio_ctrl_impl::set_tx_gain(0, chan); - radio_ctrl_impl::set_tx_antenna(MAGNESIUM_DEFAULT_TX_ANTENNA, chan); - radio_ctrl_impl::set_tx_bandwidth(MAGNESIUM_DEFAULT_BANDWIDTH, chan); - } - - - /** Update default SPP (overwrites the default value from the XML file) **/ - const size_t max_bytes_header = - uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t); - const size_t default_spp = - (_tree->access<size_t>("mtu/recv").get() - max_bytes_header) - / (2 * sizeof(int16_t)); - UHD_LOG_DEBUG(unique_id(), "Setting default spp to " << default_spp); - _tree->access<int>(get_arg_path("spp") / "value").set(default_spp); -} - -void magnesium_radio_ctrl_impl::_init_peripherals() -{ - UHD_LOG_TRACE(unique_id(), "Initializing peripherals..."); - UHD_LOG_TRACE(unique_id(), "Initializing SPI core..."); - _spi = spi_core_3000::make( - _get_ctrl(0), regs::sr_addr(regs::SPI), regs::rb_addr(regs::RB_SPI)); - UHD_LOG_TRACE(unique_id(), "Initializing CPLD..."); - UHD_LOG_TRACE(unique_id(), "Creating new CPLD object..."); - spi_config_t spi_config; - spi_config.use_custom_divider = true; - spi_config.divider = 125; - spi_config.mosi_edge = spi_config_t::EDGE_RISE; - spi_config.miso_edge = spi_config_t::EDGE_FALL; - UHD_LOG_TRACE(unique_id(), "Making CPLD object..."); - _cpld = std::make_shared<magnesium_cpld_ctrl>( - [this, spi_config](const uint32_t transaction) { // Write functor - this->_spi->write_spi(SEN_CPLD, spi_config, transaction, 24); - }, - [this, spi_config](const uint32_t transaction) { // Read functor - return this->_spi->read_spi(SEN_CPLD, spi_config, transaction, 24); - }); - _update_atr_switches( - magnesium_cpld_ctrl::BOTH, DX_DIRECTION, radio_ctrl_impl::get_rx_antenna(0)); - UHD_LOG_TRACE(unique_id(), "Initializing TX LO..."); - _tx_lo = adf435x_iface::make_adf4351([this]( - const std::vector<uint32_t> transactions) { - for (const uint32_t transaction : transactions) { - this->_spi->write_spi(SEN_TX_LO, spi_config_t::EDGE_RISE, transaction, 32); - } - }); - UHD_LOG_TRACE(unique_id(), "Initializing RX LO..."); - _rx_lo = adf435x_iface::make_adf4351([this]( - const std::vector<uint32_t> transactions) { - for (const uint32_t transaction : transactions) { - this->_spi->write_spi(SEN_RX_LO, spi_config_t::EDGE_RISE, transaction, 32); - } - }); - - _gpio.clear(); // Following the as-if rule, this can get optimized out - for (size_t radio_idx = 0; radio_idx < _get_num_radios(); radio_idx++) { - UHD_LOG_TRACE(unique_id(), "Initializing GPIOs for channel " << radio_idx); - _gpio.emplace_back(usrp::gpio_atr::gpio_atr_3000::make(_get_ctrl(radio_idx), - regs::sr_addr(regs::GPIO), - regs::rb_addr(regs::RB_DB_GPIO))); - // DSA and AD9371 gain bits do *not* toggle on ATR modes. If we ever - // connect anything else to this core, we might need to set_atr_mode() - // to MODE_ATR on those bits. For now, all bits simply do what they're - // told, and don't toggle on RX/TX state changes. - _gpio.back()->set_atr_mode(usrp::gpio_atr::MODE_GPIO, // Disable ATR mode - usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); - _gpio.back()->set_gpio_ddr(usrp::gpio_atr::DDR_OUTPUT, // Make all GPIOs outputs - usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); - } - UHD_LOG_TRACE(unique_id(), "Initializing front-panel GPIO control...") - _fp_gpio = usrp::gpio_atr::gpio_atr_3000::make( - _get_ctrl(0), regs::sr_addr(regs::FP_GPIO), regs::rb_addr(regs::RB_FP_GPIO)); -} - -void magnesium_radio_ctrl_impl::_init_frontend_subtree( - uhd::property_tree::sptr subtree, const size_t chan_idx) -{ - const fs_path tx_fe_path = fs_path("tx_frontends") / chan_idx; - const fs_path rx_fe_path = fs_path("rx_frontends") / chan_idx; - UHD_LOG_TRACE(unique_id(), - "Adding non-RFNoC block properties for channel " - << chan_idx << " to prop tree path " << tx_fe_path << " and " << rx_fe_path); - // TX Standard attributes - subtree->create<std::string>(tx_fe_path / "name") - .set(str(boost::format("Magnesium"))); - subtree->create<std::string>(tx_fe_path / "connection").set("IQ"); - // RX Standard attributes - subtree->create<std::string>(rx_fe_path / "name") - .set(str(boost::format("Magnesium"))); - subtree->create<std::string>(rx_fe_path / "connection").set("IQ"); - // TX Antenna - subtree->create<std::string>(tx_fe_path / "antenna" / "value") - .add_coerced_subscriber([this, chan_idx](const std::string& ant) { - this->set_tx_antenna(ant, chan_idx); - }) - .set_publisher([this, chan_idx]() { return this->get_tx_antenna(chan_idx); }); - subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options") - .set({MAGNESIUM_DEFAULT_TX_ANTENNA}) - .add_coerced_subscriber([](const std::vector<std::string>&) { - throw uhd::runtime_error("Attempting to update antenna options!"); - }); - // RX Antenna - subtree->create<std::string>(rx_fe_path / "antenna" / "value") - .add_coerced_subscriber([this, chan_idx](const std::string& ant) { - this->set_rx_antenna(ant, chan_idx); - }) - .set_publisher([this, chan_idx]() { return this->get_rx_antenna(chan_idx); }); - subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options") - .set(MAGNESIUM_RX_ANTENNAS) - .add_coerced_subscriber([](const std::vector<std::string>&) { - throw uhd::runtime_error("Attempting to update antenna options!"); - }); - // TX frequency - subtree->create<double>(tx_fe_path / "freq" / "value") - .set_coercer([this, chan_idx](const double freq) { - return this->set_tx_frequency(freq, chan_idx); - }) - .set_publisher([this, chan_idx]() { return this->get_tx_frequency(chan_idx); }); - subtree->create<meta_range_t>(tx_fe_path / "freq" / "range") - .set(meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0)) - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update freq range!"); - }); - // RX frequency - subtree->create<double>(rx_fe_path / "freq" / "value") - .set_coercer([this, chan_idx](const double freq) { - return this->set_rx_frequency(freq, chan_idx); - }) - .set_publisher([this, chan_idx]() { return this->get_rx_frequency(chan_idx); }); - subtree->create<meta_range_t>(rx_fe_path / "freq" / "range") - .set(meta_range_t(MAGNESIUM_MIN_FREQ, MAGNESIUM_MAX_FREQ, 1.0)) - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update freq range!"); - }); - // TX bandwidth - subtree->create<double>(tx_fe_path / "bandwidth" / "value") - .set(AD9371_TX_MAX_BANDWIDTH) - .set_coercer([this, chan_idx](const double bw) { - return this->set_tx_bandwidth(bw, chan_idx); - }) - .set_publisher([this, chan_idx]() { return this->get_tx_bandwidth(chan_idx); }); - subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range") - .set(meta_range_t(AD9371_TX_MIN_BANDWIDTH, AD9371_TX_MAX_BANDWIDTH)) - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update bandwidth range!"); - }); - // RX bandwidth - subtree->create<double>(rx_fe_path / "bandwidth" / "value") - .set(AD9371_RX_MAX_BANDWIDTH) - .set_coercer([this, chan_idx](const double bw) { - return this->set_rx_bandwidth(bw, chan_idx); - }); - subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range") - .set(meta_range_t(AD9371_RX_MIN_BANDWIDTH, AD9371_RX_MAX_BANDWIDTH)) - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update bandwidth range!"); - }); - // TX gains - subtree->create<double>(tx_fe_path / "gains" / "all" / "value") - .set_coercer([this, chan_idx]( - const double gain) { return this->set_tx_gain(gain, chan_idx); }) - .set_publisher( - [this, chan_idx]() { return radio_ctrl_impl::get_tx_gain(chan_idx); }); - subtree->create<meta_range_t>(tx_fe_path / "gains" / "all" / "range") - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update gain range!"); - }) - .set_publisher([this]() { - if (_gain_profile[TX_DIRECTION] == "manual") { - return meta_range_t(0.0, 0.0, 0.0); - } else { - return meta_range_t(ALL_TX_MIN_GAIN, ALL_TX_MAX_GAIN, ALL_TX_GAIN_STEP); - } - }); - - subtree->create<std::vector<std::string>>(tx_fe_path / "gains/all/profile/options") - .set(MAGNESIUM_GP_OPTIONS); - - subtree->create<std::string>(tx_fe_path / "gains/all/profile/value") - .set_coercer([this](const std::string& profile) { - // check if given profile is valid, otherwise use default profile - std::string return_profile = profile; - if (std::find( - MAGNESIUM_GP_OPTIONS.begin(), MAGNESIUM_GP_OPTIONS.end(), profile) - == MAGNESIUM_GP_OPTIONS.end()) { - return_profile = "default"; - } - _gain_profile[TX_DIRECTION] = return_profile; - return return_profile; - }) - .set_publisher([this]() { return _gain_profile[TX_DIRECTION]; }); - - // RX gains - subtree->create<double>(rx_fe_path / "gains" / "all" / "value") - .set_coercer([this, chan_idx]( - const double gain) { return this->set_rx_gain(gain, chan_idx); }) - .set_publisher( - [this, chan_idx]() { return radio_ctrl_impl::get_rx_gain(chan_idx); }); - - subtree->create<meta_range_t>(rx_fe_path / "gains" / "all" / "range") - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update gain range!"); - }) - .set_publisher([this]() { - if (_gain_profile[RX_DIRECTION] == "manual") { - return meta_range_t(0.0, 0.0, 0.0); - } else { - return meta_range_t(ALL_RX_MIN_GAIN, ALL_RX_MAX_GAIN, ALL_RX_GAIN_STEP); - } - }); - - subtree->create<std::vector<std::string>>(rx_fe_path / "gains/all/profile/options") - .set(MAGNESIUM_GP_OPTIONS); - - subtree->create<std::string>(rx_fe_path / "gains/all/profile/value") - .set_coercer([this](const std::string& profile) { - // check if given profile is valid, otherwise use default profile - std::string return_profile = profile; - if (std::find( - MAGNESIUM_GP_OPTIONS.begin(), MAGNESIUM_GP_OPTIONS.end(), profile) - == MAGNESIUM_GP_OPTIONS.end()) { - return_profile = "default"; - } - _gain_profile[RX_DIRECTION] = return_profile; - return return_profile; - }) - .set_publisher([this]() { return _gain_profile[RX_DIRECTION]; }); - - // TX mykonos attenuation - subtree->create<double>(tx_fe_path / "gains" / MAGNESIUM_GAIN1 / "value") - .set_coercer([this, chan_idx](const double gain) { - return _set_tx_gain(MAGNESIUM_GAIN1, gain, chan_idx); - }) - .set_publisher( - [this, chan_idx]() { return this->_get_tx_gain(MAGNESIUM_GAIN1, chan_idx); }); - - subtree->create<meta_range_t>(tx_fe_path / "gains" / MAGNESIUM_GAIN1 / "range") - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update gain range!"); - }) - .set_publisher([this]() { - if (_gain_profile[TX_DIRECTION] == "manual") { - return meta_range_t( - AD9371_MIN_TX_GAIN, AD9371_MAX_TX_GAIN, AD9371_TX_GAIN_STEP); - } else { - return meta_range_t(0.0, 0.0, 0.0); - } - }); - // TX DSA - subtree->create<double>(tx_fe_path / "gains" / MAGNESIUM_GAIN2 / "value") - .set_coercer([this, chan_idx](const double gain) { - return this->_set_tx_gain(MAGNESIUM_GAIN2, gain, chan_idx); - }) - .set_publisher( - [this, chan_idx]() { return this->_get_tx_gain(MAGNESIUM_GAIN2, chan_idx); }); - - subtree->create<meta_range_t>(tx_fe_path / "gains" / MAGNESIUM_GAIN2 / "range") - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update gain range!"); - }) - .set_publisher([this]() { - if (_gain_profile[TX_DIRECTION] == "manual") { - return meta_range_t(DSA_MIN_GAIN, DSA_MAX_GAIN, DSA_GAIN_STEP); - } else { - return meta_range_t(0.0, 0.0, 0.0); - } - }); - // TX amp - subtree->create<double>(tx_fe_path / "gains" / MAGNESIUM_AMP / "value") - .set_coercer([this, chan_idx](const double gain) { - return this->_set_tx_gain(MAGNESIUM_AMP, gain, chan_idx); - }) - .set_publisher( - [this, chan_idx]() { return this->_get_tx_gain(MAGNESIUM_AMP, chan_idx); }); - - subtree->create<meta_range_t>(tx_fe_path / "gains" / MAGNESIUM_AMP / "range") - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update gain range!"); - }) - .set_publisher([this]() { - if (_gain_profile[TX_DIRECTION] == "manual") { - return meta_range_t(AMP_MIN_GAIN, AMP_MAX_GAIN, AMP_GAIN_STEP); - } else { - return meta_range_t(0.0, 0.0, 0.0); - } - }); - - // RX mykonos attenuation - subtree->create<double>(rx_fe_path / "gains" / MAGNESIUM_GAIN1 / "value") - .set_coercer([this, chan_idx](const double gain) { - UHD_VAR(gain); - return this->_set_rx_gain(MAGNESIUM_GAIN1, gain, chan_idx); - }) - .set_publisher( - [this, chan_idx]() { return this->_get_rx_gain(MAGNESIUM_GAIN1, chan_idx); }); - - subtree->create<meta_range_t>(rx_fe_path / "gains" / MAGNESIUM_GAIN1 / "range") - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update gain range!"); - }) - .set_publisher([this]() { - if (_gain_profile[RX_DIRECTION] == "manual") { - return meta_range_t( - AD9371_MIN_RX_GAIN, AD9371_MAX_RX_GAIN, AD9371_RX_GAIN_STEP); - } else { - return meta_range_t(0.0, 0.0, 0.0); - } - }); - // RX DSA - subtree->create<double>(rx_fe_path / "gains" / MAGNESIUM_GAIN2 / "value") - .set_coercer([this, chan_idx](const double gain) { - UHD_VAR(gain); - return this->_set_rx_gain(MAGNESIUM_GAIN2, gain, chan_idx); - }) - .set_publisher( - [this, chan_idx]() { return this->_get_rx_gain(MAGNESIUM_GAIN2, chan_idx); }); - - subtree->create<meta_range_t>(rx_fe_path / "gains" / MAGNESIUM_GAIN2 / "range") - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update gain range!"); - }) - .set_publisher([this]() { - if (_gain_profile[RX_DIRECTION] == "manual") { - return meta_range_t(DSA_MIN_GAIN, DSA_MAX_GAIN, DSA_MAX_GAIN); - } else { - return meta_range_t(0.0, 0.0, 0.0); - } - }); - - // RX amp - subtree->create<double>(rx_fe_path / "gains" / MAGNESIUM_AMP / "value") - .set_coercer([this, chan_idx](const double gain) { - return this->_set_rx_gain(MAGNESIUM_AMP, gain, chan_idx); - }) - .set_publisher( - [this, chan_idx]() { return this->_get_rx_gain(MAGNESIUM_AMP, chan_idx); }); - - subtree->create<meta_range_t>(rx_fe_path / "gains" / MAGNESIUM_AMP / "range") - .add_coerced_subscriber([](const meta_range_t&) { - throw uhd::runtime_error("Attempting to update gain range!"); - }) - .set_publisher([this]() { - if (_gain_profile[RX_DIRECTION] == "manual") { - return meta_range_t(AMP_MIN_GAIN, AMP_MAX_GAIN, AMP_GAIN_STEP); - } else { - return meta_range_t(0.0, 0.0, 0.0); - } - }); - - // TX LO lock sensor ////////////////////////////////////////////////////// - // Note: The lowband and AD9371 LO lock sensors are generated - // programmatically in set_rpc_client(). The actual lo_locked publisher is - // also set there. - subtree->create<sensor_value_t>(tx_fe_path / "sensors" / "lo_locked") - .set(sensor_value_t("all_los", false, "locked", "unlocked")) - .add_coerced_subscriber([](const sensor_value_t&) { - throw uhd::runtime_error("Attempting to write to sensor!"); - }) - .set_publisher([this]() { - return sensor_value_t( - "all_los", this->get_lo_lock_status(TX_DIRECTION), "locked", "unlocked"); - }); - // RX LO lock sensor (see not on TX LO lock sensor) - subtree->create<sensor_value_t>(rx_fe_path / "sensors" / "lo_locked") - .set(sensor_value_t("all_los", false, "locked", "unlocked")) - .add_coerced_subscriber([](const sensor_value_t&) { - throw uhd::runtime_error("Attempting to write to sensor!"); - }) - .set_publisher([this]() { - return sensor_value_t( - "all_los", this->get_lo_lock_status(RX_DIRECTION), "locked", "unlocked"); - }); - // LO Specific - // RX LO - subtree->create<meta_range_t>(rx_fe_path / "los" / MAGNESIUM_LO1 / "freq/range") - .set_publisher([this, chan_idx]() { - return this->get_rx_lo_freq_range(MAGNESIUM_LO1, chan_idx); - }); - subtree - ->create<std::vector<std::string>>( - rx_fe_path / "los" / MAGNESIUM_LO1 / "source/options") - .set_publisher([this, chan_idx]() { - return this->get_rx_lo_sources(MAGNESIUM_LO1, chan_idx); - }); - subtree->create<std::string>(rx_fe_path / "los" / MAGNESIUM_LO1 / "source/value") - .add_coerced_subscriber([this, chan_idx](std::string src) { - this->set_rx_lo_source(src, MAGNESIUM_LO1, chan_idx); - }) - .set_publisher([this, chan_idx]() { - return this->get_rx_lo_source(MAGNESIUM_LO1, chan_idx); - }); - subtree->create<double>(rx_fe_path / "los" / MAGNESIUM_LO1 / "freq/value") - .set_publisher( - [this, chan_idx]() { return this->get_rx_lo_freq(MAGNESIUM_LO1, chan_idx); }) - .set_coercer([this, chan_idx](const double freq) { - return this->set_rx_lo_freq(freq, MAGNESIUM_LO1, chan_idx); - }); - - subtree->create<meta_range_t>(rx_fe_path / "los" / MAGNESIUM_LO2 / "freq/range") - .set_publisher([this, chan_idx]() { - return this->get_rx_lo_freq_range(MAGNESIUM_LO2, chan_idx); - }); - subtree - ->create<std::vector<std::string>>( - rx_fe_path / "los" / MAGNESIUM_LO2 / "source/options") - .set_publisher([this, chan_idx]() { - return this->get_rx_lo_sources(MAGNESIUM_LO2, chan_idx); - }); - - subtree->create<std::string>(rx_fe_path / "los" / MAGNESIUM_LO2 / "source/value") - .add_coerced_subscriber([this, chan_idx](std::string src) { - this->set_rx_lo_source(src, MAGNESIUM_LO2, chan_idx); - }) - .set_publisher([this, chan_idx]() { - return this->get_rx_lo_source(MAGNESIUM_LO2, chan_idx); - }); - subtree->create<double>(rx_fe_path / "los" / MAGNESIUM_LO2 / "freq/value") - .set_publisher( - [this, chan_idx]() { return this->get_rx_lo_freq(MAGNESIUM_LO2, chan_idx); }) - .set_coercer([this, chan_idx](double freq) { - return this->set_rx_lo_freq(freq, MAGNESIUM_LO2, chan_idx); - }); - // TX LO - subtree->create<meta_range_t>(tx_fe_path / "los" / MAGNESIUM_LO1 / "freq/range") - .set_publisher([this, chan_idx]() { - return this->get_rx_lo_freq_range(MAGNESIUM_LO1, chan_idx); - }); - subtree - ->create<std::vector<std::string>>( - tx_fe_path / "los" / MAGNESIUM_LO1 / "source/options") - .set_publisher([this, chan_idx]() { - return this->get_tx_lo_sources(MAGNESIUM_LO1, chan_idx); - }); - subtree->create<std::string>(tx_fe_path / "los" / MAGNESIUM_LO1 / "source/value") - .add_coerced_subscriber([this, chan_idx](std::string src) { - this->set_tx_lo_source(src, MAGNESIUM_LO1, chan_idx); - }) - .set_publisher([this, chan_idx]() { - return this->get_tx_lo_source(MAGNESIUM_LO1, chan_idx); - }); - subtree->create<double>(tx_fe_path / "los" / MAGNESIUM_LO1 / "freq/value ") - .set_publisher( - [this, chan_idx]() { return this->get_tx_lo_freq(MAGNESIUM_LO1, chan_idx); }) - .set_coercer([this, chan_idx](double freq) { - return this->set_tx_lo_freq(freq, MAGNESIUM_LO1, chan_idx); - }); - - subtree->create<meta_range_t>(tx_fe_path / "los" / MAGNESIUM_LO2 / "freq/range") - .set_publisher([this, chan_idx]() { - return this->get_tx_lo_freq_range(MAGNESIUM_LO2, chan_idx); - }); - subtree - ->create<std::vector<std::string>>( - tx_fe_path / "los" / MAGNESIUM_LO2 / "source/options") - .set_publisher([this, chan_idx]() { - return this->get_tx_lo_sources(MAGNESIUM_LO2, chan_idx); - }); - - subtree->create<std::string>(tx_fe_path / "los" / MAGNESIUM_LO2 / "source/value") - .add_coerced_subscriber([this, chan_idx](std::string src) { - this->set_tx_lo_source(src, MAGNESIUM_LO2, chan_idx); - }) - .set_publisher([this, chan_idx]() { - return this->get_tx_lo_source(MAGNESIUM_LO2, chan_idx); - }); - subtree->create<double>(tx_fe_path / "los" / MAGNESIUM_LO2 / "freq/value") - .set_publisher( - [this, chan_idx]() { return this->get_tx_lo_freq(MAGNESIUM_LO2, chan_idx); }) - .set_coercer([this, chan_idx](double freq) { - return this->set_tx_lo_freq(freq, MAGNESIUM_LO2, chan_idx); - }); -} - -void magnesium_radio_ctrl_impl::_init_prop_tree() -{ - const fs_path fe_base = fs_path("dboards") / _radio_slot; - for (size_t chan_idx = 0; chan_idx < MAGNESIUM_NUM_CHANS; chan_idx++) { - this->_init_frontend_subtree(_tree->subtree(fe_base), chan_idx); - } - - // EEPROM paths subject to change FIXME - _tree->create<eeprom_map_t>(_root_path / "eeprom").set(eeprom_map_t()); - - // TODO change codec names - _tree->create<int>("rx_codecs" / _radio_slot / "gains"); - _tree->create<int>("tx_codecs" / _radio_slot / "gains"); - _tree->create<std::string>("rx_codecs" / _radio_slot / "name").set("AD9371 Dual ADC"); - _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("AD9371 Dual DAC"); - - // TODO remove this dirty hack - if (not _tree->exists("tick_rate")) { - _tree->create<double>("tick_rate").set_publisher([this]() { - return this->get_rate(); - }); - } - - // *****FP_GPIO************************ - for (const auto& attr : usrp::gpio_atr::gpio_attr_map) { - if (not _tree->exists(fs_path("gpio") / "FP0" / attr.second)) { - switch (attr.first) { - case usrp::gpio_atr::GPIO_SRC: - // FIXME: move this creation of this branch of ptree out side of - // radio impl; - // since there's no data dependency between radio and SRC setting for - // FP0 - _tree - ->create<std::vector<std::string>>( - fs_path("gpio") / "FP0" / attr.second) - .set(std::vector<std::string>( - 32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) - .add_coerced_subscriber( - [this, attr](const std::vector<std::string> str_val) { - uint32_t radio_src_value = 0; - uint32_t master_value = 0; - for (size_t i = 0; i < str_val.size(); i++) { - if (str_val[i] == "PS") { - master_value += 1 << i; - ; - } else { - auto port_num = - extract_port_number(str_val[i], _tree); - radio_src_value = - (1 << (2 * i)) * port_num + radio_src_value; - } - } - _rpcc->notify_with_token( - "set_fp_gpio_master", master_value); - _rpcc->notify_with_token( - "set_fp_gpio_radio_src", radio_src_value); - }); - break; - case usrp::gpio_atr::GPIO_CTRL: - case usrp::gpio_atr::GPIO_DDR: - _tree - ->create<std::vector<std::string>>( - fs_path("gpio") / "FP0" / attr.second) - .set(std::vector<std::string>( - 32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) - .add_coerced_subscriber( - [this, attr](const std::vector<std::string> str_val) { - uint32_t val = 0; - for (size_t i = 0; i < str_val.size(); i++) { - val += usrp::gpio_atr::gpio_attr_value_pair - .at(attr.second) - .at(str_val[i]) - << i; - } - _fp_gpio->set_gpio_attr(attr.first, val); - }); - break; - case usrp::gpio_atr::GPIO_READBACK: { - _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second) - .set_publisher([this]() { return _fp_gpio->read_gpio(); }); - } break; - default: - _tree->create<uint32_t>(fs_path("gpio") / "FP0" / attr.second) - .set(0) - .add_coerced_subscriber([this, attr](const uint32_t val) { - _fp_gpio->set_gpio_attr(attr.first, val); - }); - } - } else { - switch (attr.first) { - case usrp::gpio_atr::GPIO_SRC: - break; - case usrp::gpio_atr::GPIO_CTRL: - case usrp::gpio_atr::GPIO_DDR: - _tree - ->access<std::vector<std::string>>( - fs_path("gpio") / "FP0" / attr.second) - .set(std::vector<std::string>( - 32, usrp::gpio_atr::default_attr_value_map.at(attr.first))) - .add_coerced_subscriber( - [this, attr](const std::vector<std::string> str_val) { - uint32_t val = 0; - for (size_t i = 0; i < str_val.size(); i++) { - val += usrp::gpio_atr::gpio_attr_value_pair - .at(attr.second) - .at(str_val[i]) - << i; - } - _fp_gpio->set_gpio_attr(attr.first, val); - }); - break; - case usrp::gpio_atr::GPIO_READBACK: - break; - default: - _tree->access<uint32_t>(fs_path("gpio") / "FP0" / attr.second) - .set(0) - .add_coerced_subscriber([this, attr](const uint32_t val) { - _fp_gpio->set_gpio_attr(attr.first, val); - }); - } - } - } -} - - -void magnesium_radio_ctrl_impl::_init_mpm_sensors( - const direction_t dir, const size_t chan_idx) -{ - const std::string trx = (dir == RX_DIRECTION) ? "RX" : "TX"; - const fs_path fe_path = fs_path("dboards") / _radio_slot - / (dir == RX_DIRECTION ? "rx_frontends" : "tx_frontends") - / chan_idx; - auto sensor_list = _rpcc->request_with_token<std::vector<std::string>>( - this->_rpc_prefix + "get_sensors", trx); - UHD_LOG_TRACE(unique_id(), - "Chan " << chan_idx << ": Found " << sensor_list.size() << " " << trx - << " sensors."); - for (const auto& sensor_name : sensor_list) { - UHD_LOG_TRACE(unique_id(), "Adding " << trx << " sensor " << sensor_name); - _tree->create<sensor_value_t>(fe_path / "sensors" / sensor_name) - .add_coerced_subscriber([](const sensor_value_t&) { - throw uhd::runtime_error("Attempting to write to sensor!"); - }) - .set_publisher([this, trx, sensor_name, chan_idx]() { - return sensor_value_t( - this->_rpcc->request_with_token<sensor_value_t::sensor_map_t>( - this->_rpc_prefix + "get_sensor", trx, sensor_name, chan_idx)); - }); - } -} |