aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/dboard/rhodium
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2019-07-03 20:15:35 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 12:16:25 -0800
commitc256b9df6502536c2e451e690f1ad5962c664d1a (patch)
treea83ad13e6f5978bbe14bb3ecf8294ba1e3d28db4 /host/lib/usrp/dboard/rhodium
parent9a8435ed998fc5c65257f4c55768750b227ab19e (diff)
downloaduhd-c256b9df6502536c2e451e690f1ad5962c664d1a.tar.gz
uhd-c256b9df6502536c2e451e690f1ad5962c664d1a.tar.bz2
uhd-c256b9df6502536c2e451e690f1ad5962c664d1a.zip
x300/mpmd: Port all RFNoC devices to the new RFNoC framework
Co-Authored-By: Alex Williams <alex.williams@ni.com> Co-Authored-By: Sugandha Gupta <sugandha.gupta@ettus.com> Co-Authored-By: Brent Stapleton <brent.stapleton@ettus.com> Co-Authored-By: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Diffstat (limited to 'host/lib/usrp/dboard/rhodium')
-rw-r--r--host/lib/usrp/dboard/rhodium/CMakeLists.txt8
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_bands.cpp22
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_constants.hpp40
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp723
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control.hpp (renamed from host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp)213
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control_cpld.cpp252
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp611
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_control_lo.cpp713
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_cpld.cpp298
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.cpp677
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp843
-rw-r--r--host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp726
12 files changed, 2504 insertions, 2622 deletions
diff --git a/host/lib/usrp/dboard/rhodium/CMakeLists.txt b/host/lib/usrp/dboard/rhodium/CMakeLists.txt
index 2b2e9744c..3159037c4 100644
--- a/host/lib/usrp/dboard/rhodium/CMakeLists.txt
+++ b/host/lib/usrp/dboard/rhodium/CMakeLists.txt
@@ -6,10 +6,10 @@
if(ENABLE_MPMD)
list(APPEND RHODIUM_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_ctrl_impl.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_ctrl_init.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_ctrl_cpld.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_ctrl_lo.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_control.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_control_init.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_control_cpld.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/rhodium_radio_control_lo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rhodium_bands.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rhodium_cpld_ctrl.cpp
)
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_bands.cpp b/host/lib/usrp/dboard/rhodium/rhodium_bands.cpp
index ffa206195..9e0a1d3d3 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_bands.cpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_bands.cpp
@@ -4,8 +4,8 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//
-#include "rhodium_radio_ctrl_impl.hpp"
#include "rhodium_constants.hpp"
+#include "rhodium_radio_control.hpp"
#include <uhd/utils/math.hpp>
using namespace uhd;
@@ -30,7 +30,7 @@ namespace {
* chosen to allow as much of the full bandwidth through unattenuated.
*
* Switch selection logic for these bands can be found in
- * rhodium_radio_ctrl_impl::_update_rx_freq_switches()
+ * rhodium_radio_control_impl::_update_rx_freq_switches()
*/
constexpr double RHODIUM_RX_BAND0_MIN_FREQ = RHODIUM_MIN_FREQ;
constexpr double RHODIUM_RX_BAND1_MIN_FREQ = 450e6;
@@ -55,7 +55,7 @@ namespace {
* bandwidth through unattenuated.
*
* Switch selection logic for these bands can be found in
- * rhodium_radio_ctrl_impl::_update_tx_freq_switches()
+ * rhodium_radio_control_impl::_update_tx_freq_switches()
*/
constexpr double RHODIUM_TX_BAND0_MIN_FREQ = RHODIUM_MIN_FREQ;
constexpr double RHODIUM_TX_BAND1_MIN_FREQ = 450e6;
@@ -67,9 +67,9 @@ namespace {
constexpr double RHODIUM_TX_BAND7_MIN_FREQ = 4100e6;
}
-rhodium_radio_ctrl_impl::rx_band
-rhodium_radio_ctrl_impl::_map_freq_to_rx_band(const double freq) {
-
+rhodium_radio_control_impl::rx_band rhodium_radio_control_impl::_map_freq_to_rx_band(
+ const double freq)
+{
auto freq_compare = fp_compare_epsilon<double>(freq, RHODIUM_FREQ_COMPARE_EPSILON);
if (freq_compare < RHODIUM_RX_BAND0_MIN_FREQ) {
@@ -95,9 +95,9 @@ rhodium_radio_ctrl_impl::_map_freq_to_rx_band(const double freq) {
}
}
-rhodium_radio_ctrl_impl::tx_band
-rhodium_radio_ctrl_impl::_map_freq_to_tx_band(const double freq) {
-
+rhodium_radio_control_impl::tx_band rhodium_radio_control_impl::_map_freq_to_tx_band(
+ const double freq)
+{
auto freq_compare = fp_compare_epsilon<double>(freq, RHODIUM_FREQ_COMPARE_EPSILON);
if (freq_compare < RHODIUM_TX_BAND0_MIN_FREQ) {
@@ -123,12 +123,12 @@ rhodium_radio_ctrl_impl::_map_freq_to_tx_band(const double freq) {
}
}
-bool rhodium_radio_ctrl_impl::_is_rx_lowband(const double freq)
+bool rhodium_radio_control_impl::_is_rx_lowband(const double freq)
{
return _map_freq_to_rx_band(freq) == rx_band::RX_BAND_0;
}
-bool rhodium_radio_ctrl_impl::_is_tx_lowband(const double freq)
+bool rhodium_radio_control_impl::_is_tx_lowband(const double freq)
{
return _map_freq_to_tx_band(freq) == tx_band::TX_BAND_0;
}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp b/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
index c52a73bca..69e6bf676 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_constants.hpp
@@ -48,6 +48,8 @@ static constexpr double LO_MIN_POWER = 0.0;
static constexpr double LO_MAX_POWER = 63.0;
static constexpr double LO_POWER_STEP = 1.0;
+static constexpr double RHODIUM_DEFAULT_BANDWIDTH = 250e6; // Hz
+
static const std::vector<std::string> RHODIUM_RX_ANTENNAS = {
"TX/RX", "RX2", "CAL", "TERM"
};
@@ -57,10 +59,15 @@ static const std::vector<std::string> RHODIUM_TX_ANTENNAS = {
};
// These names are taken from radio_rhodium.xml
-static constexpr char SPUR_DODGING_ARG_NAME[] = "spur_dodging";
-static constexpr char SPUR_DODGING_THRESHOLD_ARG_NAME[] = "spur_dodging_threshold";
-static constexpr char HIGHBAND_SPUR_REDUCTION_ARG_NAME[] = "highband_spur_reduction";
+static constexpr char SPUR_DODGING_PROP_NAME[] = "spur_dodging";
+static constexpr char SPUR_DODGING_THRESHOLD_PROP_NAME[] = "spur_dodging_threshold";
+static constexpr char HIGHBAND_SPUR_REDUCTION_PROP_NAME[] = "highband_spur_reduction";
+static constexpr char RHODIUM_DEFAULT_SPUR_DOGING_MODE[] = "disabled";
+static constexpr double RHODIUM_DEFAULT_SPUR_DOGING_THRESHOLD = 2e6;
+static constexpr char RHODIUM_DEFAULT_HB_SPUR_REDUCTION_MODE[] = "disabled";
+
+static constexpr char RHODIUM_FPGPIO_BANK[] = "FP0";
static constexpr uint32_t RHODIUM_GPIO_MASK = 0x1F;
static constexpr uint32_t SW10_GPIO_MASK = 0x3;
static constexpr uint32_t LED_GPIO_MASK = 0x1C;
@@ -85,8 +92,9 @@ static constexpr char RHODIUM_LO_GAIN[] = "dsa";
//! LO output power
static constexpr char RHODIUM_LO_POWER[] = "lo";
-static constexpr int NUM_LO_OUTPUT_PORT_NAMES = 4;
+static constexpr char RHODIUM_FE_NAME[] = "Rhodium";
+static constexpr int NUM_LO_OUTPUT_PORT_NAMES = 4;
static constexpr std::array<const char*, NUM_LO_OUTPUT_PORT_NAMES> LO_OUTPUT_PORT_NAMES = {
"LO_OUT_0",
"LO_OUT_1",
@@ -96,4 +104,28 @@ static constexpr std::array<const char*, NUM_LO_OUTPUT_PORT_NAMES> LO_OUTPUT_POR
static constexpr size_t RHODIUM_NUM_CHANS = 1;
+namespace n320_regs {
+
+static constexpr uint32_t PERIPH_BASE = 0x80000;
+static constexpr uint32_t PERIPH_REG_OFFSET = 8;
+
+// db_control registers
+static constexpr uint32_t SR_MISC_OUTS = PERIPH_BASE + 160 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_SPI = PERIPH_BASE + 168 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_LEDS = PERIPH_BASE + 176 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_FP_GPIO = PERIPH_BASE + 184 * PERIPH_REG_OFFSET;
+static constexpr uint32_t SR_DB_GPIO = PERIPH_BASE + 192 * PERIPH_REG_OFFSET;
+
+static constexpr uint32_t RB_MISC_IO = PERIPH_BASE + 16 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_SPI = PERIPH_BASE + 17 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_LEDS = PERIPH_BASE + 18 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_DB_GPIO = PERIPH_BASE + 19 * PERIPH_REG_OFFSET;
+static constexpr uint32_t RB_FP_GPIO = PERIPH_BASE + 20 * PERIPH_REG_OFFSET;
+
+//! Delta between frontend offsets for channel 0 and 1
+constexpr uint32_t SR_TX_FE_BASE = PERIPH_BASE + 208 * PERIPH_REG_OFFSET;
+constexpr uint32_t SR_RX_FE_BASE = PERIPH_BASE + 224 * PERIPH_REG_OFFSET;
+
+} // namespace n320_regs
+
#endif /* INCLUDED_LIBUHD_RHODIUM_CONSTANTS_HPP */
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp
new file mode 100644
index 000000000..a3b072e74
--- /dev/null
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.cpp
@@ -0,0 +1,723 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "rhodium_radio_control.hpp"
+#include "rhodium_constants.hpp"
+#include <uhd/exception.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 <uhdlib/usrp/common/apply_corrections.hpp>
+#include <uhdlib/utils/narrow.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 {
+ constexpr char RX_FE_CONNECTION_LOWBAND[] = "QI";
+ constexpr char RX_FE_CONNECTION_HIGHBAND[] = "IQ";
+ constexpr char TX_FE_CONNECTION_LOWBAND[] = "QI";
+ constexpr char TX_FE_CONNECTION_HIGHBAND[] = "IQ";
+
+ constexpr double DEFAULT_IDENTIFY_DURATION = 5.0; // seconds
+
+ constexpr uint64_t SET_RATE_RPC_TIMEOUT_MS = 10000;
+
+}
+
+
+/******************************************************************************
+ * Structors
+ *****************************************************************************/
+rhodium_radio_control_impl::rhodium_radio_control_impl(make_args_ptr make_args)
+ : radio_control_impl(std::move(make_args))
+{
+ RFNOC_LOG_TRACE("Entering rhodium_radio_control_impl ctor...");
+ UHD_ASSERT_THROW(get_block_id().get_block_count() < 2);
+ const char radio_slot_name[] = {'A', 'B'};
+ _radio_slot = radio_slot_name[get_block_id().get_block_count()];
+ _rpc_prefix =
+ (_radio_slot == "A") ? "db_0_" : "db_1_";
+ RFNOC_LOG_TRACE("Radio slot: " << _radio_slot);
+ UHD_ASSERT_THROW(get_num_input_ports() == RHODIUM_NUM_CHANS);
+ UHD_ASSERT_THROW(get_num_output_ports() == RHODIUM_NUM_CHANS);
+ UHD_ASSERT_THROW(get_mb_controller());
+ _n320_mb_control = std::dynamic_pointer_cast<mpmd_mb_controller>(get_mb_controller());
+ UHD_ASSERT_THROW(_n320_mb_control);
+ _n3xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>(
+ _n320_mb_control->get_timekeeper(0));
+ UHD_ASSERT_THROW(_n3xx_timekeeper);
+ _rpcc = _n320_mb_control->get_rpc_client();
+ UHD_ASSERT_THROW(_rpcc);
+
+ const auto all_dboard_info =
+ _rpcc->request<std::vector<std::map<std::string, std::string>>>(
+ "get_dboard_info");
+ RFNOC_LOG_TRACE("Hardware detected " << all_dboard_info.size() << " daughterboards.");
+
+ // If we two radio blocks, but there is only one dboard plugged in, we skip
+ // initialization. The board needs to be in slot A
+ if (all_dboard_info.size() > get_block_id().get_block_count()) {
+ _init_defaults();
+ _init_mpm();
+ _init_peripherals();
+ _init_prop_tree();
+ }
+
+ // Properties
+ for (auto& samp_rate_prop : _samp_rate_in) {
+ samp_rate_prop.set(_master_clock_rate);
+ }
+ for (auto& samp_rate_prop : _samp_rate_out) {
+ samp_rate_prop.set(_master_clock_rate);
+ }
+}
+
+rhodium_radio_control_impl::~rhodium_radio_control_impl()
+{
+ RFNOC_LOG_TRACE("rhodium_radio_control_impl::dtor() ");
+}
+
+
+/******************************************************************************
+ * RF API Calls
+ *****************************************************************************/
+double rhodium_radio_control_impl::set_rate(double requested_rate)
+{
+ meta_range_t rates;
+ for (const double rate : RHODIUM_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) << " MHz to " << (rate / 1e6)
+ << " MHz, the closest possible rate.");
+ }
+
+ const double current_rate = get_rate();
+ if (math::frequencies_are_equal(current_rate, rate)) {
+ RFNOC_LOG_DEBUG(
+ "Rate is already at " << (rate / 1e6) << " MHz. Skipping set_rate()");
+ return current_rate;
+ }
+
+ RFNOC_LOG_TRACE("Updating master clock rate to " << rate);
+ _master_clock_rate = _rpcc->request_with_token<double>(
+ SET_RATE_RPC_TIMEOUT_MS, "db_0_set_master_clock_rate", rate);
+ _n3xx_timekeeper->update_tick_rate(_master_clock_rate);
+ radio_control_impl::set_rate(_master_clock_rate);
+ // The lowband LO frequency will change with the master clock rate, so
+ // update the tuning of the device.
+ set_tx_frequency(get_tx_frequency(0), 0);
+ set_rx_frequency(get_rx_frequency(0), 0);
+
+ set_tick_rate(_master_clock_rate);
+ return _master_clock_rate;
+}
+
+void rhodium_radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_antenna(ant=" << ant << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ if (!uhd::has(RHODIUM_TX_ANTENNAS, ant)) {
+ RFNOC_LOG_ERROR("Invalid TX antenna value: " << ant);
+ throw uhd::value_error("Requesting invalid TX antenna value!");
+ }
+
+ _update_tx_output_switches(ant);
+ // _update_atr will set the cached antenna value, so no need to do
+ // it here. See comments in _update_antenna for more info.
+ _update_atr(ant, TX_DIRECTION);
+}
+
+void rhodium_radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan)
+{
+ RFNOC_LOG_TRACE("Setting RX antenna to " << ant);
+ UHD_ASSERT_THROW(chan == 0);
+
+ if (!uhd::has(RHODIUM_RX_ANTENNAS, ant)) {
+ RFNOC_LOG_ERROR("Invalid RX antenna value: " << ant);
+ throw uhd::value_error("Requesting invalid RX antenna value!");
+ }
+
+ _update_rx_input_switches(ant);
+ // _update_atr will set the cached antenna value, so no need to do
+ // it here. See comments in _update_antenna for more info.
+ _update_atr(ant, RX_DIRECTION);
+}
+
+void rhodium_radio_control_impl::_set_tx_fe_connection(const std::string& conn)
+{
+ RFNOC_LOG_TRACE("set_tx_fe_connection(conn=" << conn << ")");
+ if (conn != _tx_fe_connection) {
+ _tx_fe_core->set_mux(conn);
+ _tx_fe_connection = conn;
+ }
+}
+
+void rhodium_radio_control_impl::_set_rx_fe_connection(const std::string& conn)
+{
+ RFNOC_LOG_TRACE("set_rx_fe_connection(conn=" << conn << ")");
+ if (conn != _rx_fe_connection) {
+ _rx_fe_core->set_fe_connection(conn);
+ _rx_fe_connection = conn;
+ }
+}
+
+std::string rhodium_radio_control_impl::_get_tx_fe_connection() const
+{
+ return _tx_fe_connection;
+}
+
+std::string rhodium_radio_control_impl::_get_rx_fe_connection() const
+{
+ return _rx_fe_connection;
+}
+
+double rhodium_radio_control_impl::set_tx_frequency(const double freq, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_frequency(f=" << freq << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ const auto old_freq = get_tx_frequency(0);
+ double coerced_target_freq = uhd::clip(freq, RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ);
+
+ if (freq != coerced_target_freq) {
+ RFNOC_LOG_DEBUG("Requested frequency is outside supported range. Coercing to "
+ << coerced_target_freq);
+ }
+
+ const bool is_highband = !_is_tx_lowband(coerced_target_freq);
+
+ const double target_lo_freq = is_highband ?
+ coerced_target_freq : _get_lowband_lo_freq() - coerced_target_freq;
+ const double actual_lo_freq =
+ set_tx_lo_freq(target_lo_freq, RHODIUM_LO1, chan);
+ const double coerced_freq = is_highband ?
+ actual_lo_freq : _get_lowband_lo_freq() - actual_lo_freq;
+ const auto conn = is_highband ?
+ TX_FE_CONNECTION_HIGHBAND : TX_FE_CONNECTION_LOWBAND;
+
+ // update the cached frequency value now so calls to set gain and update
+ // switches will read the new frequency
+ radio_control_impl::set_tx_frequency(coerced_freq, chan);
+
+ _set_tx_fe_connection(conn);
+ set_tx_gain(radio_control_impl::get_tx_gain(chan), 0);
+
+ if (_get_highband_spur_reduction_enabled(TX_DIRECTION)) {
+ if (_get_timed_command_enabled() and _is_tx_lowband(old_freq) != not is_highband) {
+ RFNOC_LOG_WARNING(
+ "Timed tuning commands that transition between lowband and highband, 450 "
+ "MHz, do not function correctly when highband_spur_reduction is enabled! "
+ "Disable highband_spur_reduction or avoid using timed tuning commands.");
+ }
+ RFNOC_LOG_TRACE("TX Lowband LO is " << (is_highband ? "disabled" : "enabled"));
+ _rpcc->notify_with_token(_rpc_prefix + "enable_tx_lowband_lo", (!is_highband));
+ }
+ _update_tx_freq_switches(coerced_freq);
+ const bool enable_corrections = is_highband
+ and (get_tx_lo_source(RHODIUM_LO1, 0) == "internal");
+ _update_corrections(actual_lo_freq, TX_DIRECTION, enable_corrections);
+ // if TX lowband/highband changed and antenna is TX/RX,
+ // the ATR and SW1 need to be updated
+ _update_tx_output_switches(get_tx_antenna(0));
+ _update_atr(get_tx_antenna(0), TX_DIRECTION);
+
+ return coerced_freq;
+}
+
+double rhodium_radio_control_impl::set_rx_frequency(const double freq, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_frequency(f=" << freq << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ const auto old_freq = get_rx_frequency(0);
+ double coerced_target_freq = uhd::clip(freq, RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ);
+
+ if (freq != coerced_target_freq) {
+ RFNOC_LOG_DEBUG("Requested frequency is outside supported range. Coercing to "
+ << coerced_target_freq);
+ }
+
+ const bool is_highband = !_is_rx_lowband(coerced_target_freq);
+
+ const double target_lo_freq = is_highband ?
+ coerced_target_freq : _get_lowband_lo_freq() - coerced_target_freq;
+ const double actual_lo_freq =
+ set_rx_lo_freq(target_lo_freq, RHODIUM_LO1, chan);
+ const double coerced_freq = is_highband ?
+ actual_lo_freq : _get_lowband_lo_freq() - actual_lo_freq;
+ const auto conn = is_highband ?
+ RX_FE_CONNECTION_HIGHBAND : RX_FE_CONNECTION_LOWBAND;
+
+ // update the cached frequency value now so calls to set gain and update
+ // switches will read the new frequency
+ radio_control_impl::set_rx_frequency(coerced_freq, chan);
+
+ _set_rx_fe_connection(conn);
+ set_rx_gain(radio_control_impl::get_rx_gain(chan), 0);
+
+ if (_get_highband_spur_reduction_enabled(RX_DIRECTION)) {
+ if (_get_timed_command_enabled() and _is_rx_lowband(old_freq) != not is_highband) {
+ RFNOC_LOG_WARNING(
+ "Timed tuning commands that transition between lowband and highband, 450 "
+ "MHz, do not function correctly when highband_spur_reduction is enabled! "
+ "Disable highband_spur_reduction or avoid using timed tuning commands.");
+ }
+ RFNOC_LOG_TRACE("RX Lowband LO is " << (is_highband ? "disabled" : "enabled"));
+ _rpcc->notify_with_token(_rpc_prefix + "enable_rx_lowband_lo", (!is_highband));
+ }
+ _update_rx_freq_switches(coerced_freq);
+ const bool enable_corrections = is_highband
+ and (get_rx_lo_source(RHODIUM_LO1, 0) == "internal");
+ _update_corrections(actual_lo_freq, RX_DIRECTION, enable_corrections);
+
+ return coerced_freq;
+}
+
+void rhodium_radio_control_impl::set_tx_tune_args(
+ const uhd::device_addr_t& args, const size_t chan)
+{
+ UHD_ASSERT_THROW(chan == 0);
+ _tune_args[uhd::TX_DIRECTION] = args;
+}
+
+void rhodium_radio_control_impl::set_rx_tune_args(
+ const uhd::device_addr_t& args, const size_t chan)
+{
+ UHD_ASSERT_THROW(chan == 0);
+ _tune_args[uhd::RX_DIRECTION] = args;
+}
+
+double rhodium_radio_control_impl::set_tx_gain(const double gain, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_gain(gain=" << gain << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ auto freq = this->get_tx_frequency(chan);
+ auto index = get_tx_gain_range(chan).clip(gain);
+
+ auto old_band = _is_tx_lowband(_tx_frequency_at_last_gain_write) ?
+ rhodium_cpld_ctrl::gain_band_t::LOW :
+ rhodium_cpld_ctrl::gain_band_t::HIGH;
+ auto new_band = _is_tx_lowband(freq) ?
+ rhodium_cpld_ctrl::gain_band_t::LOW :
+ rhodium_cpld_ctrl::gain_band_t::HIGH;
+
+ // The CPLD requires a rewrite of the gain control command on a change of lowband or highband
+ if (radio_control_impl::get_tx_gain(chan) != index or old_band != new_band) {
+ RFNOC_LOG_TRACE("Writing new TX gain index: " << index);
+ _cpld->set_gain_index(index, new_band, TX_DIRECTION);
+ _tx_frequency_at_last_gain_write = freq;
+ radio_control_impl::set_tx_gain(index, chan);
+ } else {
+ RFNOC_LOG_TRACE(
+ "No change in index or band, skipped writing TX gain index: " << index);
+ }
+
+ return index;
+}
+
+double rhodium_radio_control_impl::set_rx_gain(const double gain, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_gain(gain=" << gain << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+
+ auto freq = this->get_rx_frequency(chan);
+ auto index = get_rx_gain_range(chan).clip(gain);
+
+ auto old_band = _is_rx_lowband(_rx_frequency_at_last_gain_write) ?
+ rhodium_cpld_ctrl::gain_band_t::LOW :
+ rhodium_cpld_ctrl::gain_band_t::HIGH;
+ auto new_band = _is_rx_lowband(freq) ?
+ rhodium_cpld_ctrl::gain_band_t::LOW :
+ rhodium_cpld_ctrl::gain_band_t::HIGH;
+
+ // The CPLD requires a rewrite of the gain control command on a change of lowband or highband
+ if (radio_control_impl::get_rx_gain(chan) != index or old_band != new_band) {
+ RFNOC_LOG_TRACE("Writing new RX gain index: " << index);
+ _cpld->set_gain_index(index, new_band, RX_DIRECTION);
+ _rx_frequency_at_last_gain_write = freq;
+ radio_control_impl::set_rx_gain(index, chan);
+ } else {
+ RFNOC_LOG_TRACE(
+ "No change in index or band, skipped writing RX gain index: " << index);
+ }
+
+ return index;
+}
+
+void rhodium_radio_control_impl::_identify_with_leds(double identify_duration)
+{
+ auto duration_ms = static_cast<uint64_t>(identify_duration * 1000);
+ auto end_time =
+ std::chrono::steady_clock::now() + std::chrono::milliseconds(duration_ms);
+ bool led_state = true;
+ {
+ std::lock_guard<std::mutex> lock(_ant_mutex);
+ while (std::chrono::steady_clock::now() < end_time) {
+ auto atr = led_state ? (LED_RX | LED_RX2 | LED_TX) : 0;
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_IDLE, atr, RHODIUM_GPIO_MASK);
+ led_state = !led_state;
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ }
+ }
+ _update_atr(get_tx_antenna(0), TX_DIRECTION);
+ _update_atr(get_rx_antenna(0), RX_DIRECTION);
+}
+
+void rhodium_radio_control_impl::_update_atr(
+ const std::string& ant, const direction_t dir)
+{
+ // This function updates sw10 based on the value of both antennas, so we
+ // use a mutex to prevent other calls in this class instance from running
+ // at the same time.
+ std::lock_guard<std::mutex> lock(_ant_mutex);
+
+ RFNOC_LOG_TRACE(
+ "Updating ATRs for " << ((dir == RX_DIRECTION) ? "RX" : "TX") << " to " << ant);
+
+ const auto rx_ant = (dir == RX_DIRECTION) ? ant : get_rx_antenna(0);
+ const auto tx_ant = (dir == TX_DIRECTION) ? ant : get_tx_antenna(0);
+ const auto sw10_tx = _is_tx_lowband(get_tx_frequency(0)) ?
+ SW10_FROMTXLOWBAND : SW10_FROMTXHIGHBAND;
+
+
+ const uint32_t atr_idle = SW10_ISOLATION;
+
+ const uint32_t atr_rx = [rx_ant]{
+ if (rx_ant == "TX/RX") {
+ return SW10_TORX | LED_RX;
+ } else if (rx_ant == "RX2") {
+ return SW10_ISOLATION | LED_RX2;
+ } else {
+ return SW10_ISOLATION;
+ }
+ }();
+
+ const uint32_t atr_tx = (tx_ant == "TX/RX") ?
+ (sw10_tx | LED_TX) : SW10_ISOLATION;
+
+ const uint32_t atr_dx = [tx_ant, rx_ant, sw10_tx] {
+ uint32_t sw10_return;
+ if (tx_ant == "TX/RX") {
+ // if both channels are set to TX/RX, TX will override
+ sw10_return = sw10_tx | LED_TX;
+ } else if (rx_ant == "TX/RX") {
+ sw10_return = SW10_TORX | LED_RX;
+ } else {
+ sw10_return = SW10_ISOLATION;
+ }
+ sw10_return |= (rx_ant == "RX2") ? LED_RX2 : 0;
+ return sw10_return;
+ }();
+
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_IDLE, atr_idle, RHODIUM_GPIO_MASK);
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, atr_rx, RHODIUM_GPIO_MASK);
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, atr_tx, RHODIUM_GPIO_MASK);
+ _gpio->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, atr_dx, RHODIUM_GPIO_MASK);
+
+ RFNOC_LOG_TRACE(
+ str(boost::format("Wrote ATR registers i:0x%02X, r:0x%02X, t:0x%02X, d:0x%02X")
+ % atr_idle % atr_rx % atr_tx % atr_dx));
+
+ if (dir == RX_DIRECTION) {
+ radio_control_impl::set_rx_antenna(ant, 0);
+ } else {
+ radio_control_impl::set_tx_antenna(ant, 0);
+ }
+}
+
+void rhodium_radio_control_impl::_update_corrections(
+ const double freq, const direction_t dir, const bool enable)
+{
+ const std::string fe_path_part = dir == RX_DIRECTION ? "rx_fe_corrections"
+ : "tx_fe_corrections";
+ const fs_path fe_corr_path = FE_PATH / fe_path_part / 0;
+
+ if (enable) {
+ const std::vector<uint8_t> db_serial_u8 = get_db_eeprom().count("serial")
+ ? std::vector<uint8_t>()
+ : get_db_eeprom().at("serial");
+ const std::string db_serial =
+ db_serial_u8.empty() ? "unknown"
+ : std::string(db_serial_u8.begin(), db_serial_u8.end());
+ RFNOC_LOG_DEBUG("Loading any available frontend corrections for "
+ << ((dir == RX_DIRECTION) ? "RX" : "TX") << " at " << freq);
+ if (dir == RX_DIRECTION) {
+ apply_rx_fe_corrections(get_tree(), db_serial, fe_corr_path, freq);
+ } else {
+ apply_tx_fe_corrections(get_tree(), db_serial, fe_corr_path, freq);
+ }
+ } else {
+ RFNOC_LOG_DEBUG("Disabling frontend corrections for "
+ << ((dir == RX_DIRECTION) ? "RX" : "TX"));
+ if (dir == RX_DIRECTION) {
+ _rx_fe_core->set_iq_balance(rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE);
+ } else {
+ _tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ _tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
+ }
+ }
+}
+
+bool rhodium_radio_control_impl::_get_spur_dodging_enabled(uhd::direction_t dir) const
+{
+ // get the current tune_arg for spur_dodging
+ // if the tune_arg doesn't exist, use the radio block argument instead
+ const std::string spur_dodging_arg = _tune_args.at(dir).cast<std::string>(
+ SPUR_DODGING_PROP_NAME, _spur_dodging_mode.get());
+
+ RFNOC_LOG_TRACE("_get_spur_dodging_enabled returning " << spur_dodging_arg);
+ if (spur_dodging_arg == "enabled") {
+ return true;
+ } else if (spur_dodging_arg == "disabled") {
+ return false;
+ } else {
+ const std::string err_msg = str(
+ boost::format(
+ "Invalid spur_dodging argument: %s Valid options are [enabled, disabled]")
+ % spur_dodging_arg);
+ RFNOC_LOG_ERROR(err_msg);
+ throw uhd::value_error(err_msg);
+ }
+}
+
+double rhodium_radio_control_impl::_get_spur_dodging_threshold(uhd::direction_t dir) const
+{
+ // get the current tune_arg for spur_dodging_threshold
+ // if the tune_arg doesn't exist, use the radio block argument instead
+ const double threshold = _tune_args.at(dir).cast<double>(
+ SPUR_DODGING_THRESHOLD_PROP_NAME, _spur_dodging_threshold.get());
+ RFNOC_LOG_TRACE("_get_spur_dodging_threshold returning " << threshold);
+ return threshold;
+}
+
+bool rhodium_radio_control_impl::_get_highband_spur_reduction_enabled(
+ uhd::direction_t dir) const
+{
+ const std::string highband_spur_reduction_arg = _tune_args.at(dir).cast<std::string>(
+ HIGHBAND_SPUR_REDUCTION_PROP_NAME, _highband_spur_reduction_mode.get());
+
+ RFNOC_LOG_TRACE(__func__ << " returning " << highband_spur_reduction_arg);
+ if (highband_spur_reduction_arg == "enabled") {
+ return true;
+ } else if (highband_spur_reduction_arg == "disabled") {
+ return false;
+ } else {
+ throw uhd::value_error(
+ str(boost::format("Invalid highband_spur_reduction argument: %s Valid "
+ "options are [enabled, disabled]")
+ % highband_spur_reduction_arg));
+ }
+}
+
+bool rhodium_radio_control_impl::_get_timed_command_enabled() const
+{
+ return get_command_time(0) != time_spec_t::ASAP;
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_tx_antennas(const size_t) const
+{
+ return RHODIUM_RX_ANTENNAS;
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_rx_antennas(const size_t) const
+{
+ return RHODIUM_TX_ANTENNAS;
+}
+
+uhd::freq_range_t rhodium_radio_control_impl::get_tx_frequency_range(const size_t) const
+{
+ return meta_range_t(RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ, 1.0);
+}
+
+uhd::freq_range_t rhodium_radio_control_impl::get_rx_frequency_range(const size_t) const
+{
+ return meta_range_t(RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ, 1.0);
+}
+
+uhd::gain_range_t rhodium_radio_control_impl::get_tx_gain_range(const size_t) const
+{
+ return gain_range_t(TX_MIN_GAIN, TX_MAX_GAIN, TX_GAIN_STEP);
+}
+
+uhd::gain_range_t rhodium_radio_control_impl::get_rx_gain_range(const size_t) const
+{
+ return gain_range_t(RX_MIN_GAIN, RX_MAX_GAIN, RX_GAIN_STEP);
+}
+
+uhd::meta_range_t rhodium_radio_control_impl::get_tx_bandwidth_range(size_t) const
+{
+ return meta_range_t(RHODIUM_DEFAULT_BANDWIDTH, RHODIUM_DEFAULT_BANDWIDTH);
+}
+
+uhd::meta_range_t rhodium_radio_control_impl::get_rx_bandwidth_range(size_t) const
+{
+ return meta_range_t(RHODIUM_DEFAULT_BANDWIDTH, RHODIUM_DEFAULT_BANDWIDTH);
+}
+
+
+/**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+size_t rhodium_radio_control_impl::get_chan_from_dboard_fe(
+ const std::string& fe, const direction_t /* dir */
+ ) const
+{
+ UHD_ASSERT_THROW(boost::lexical_cast<size_t>(fe) == 0);
+ return 0;
+}
+
+std::string rhodium_radio_control_impl::get_dboard_fe_from_chan(
+ const size_t chan, const direction_t /* dir */
+ ) const
+{
+ UHD_ASSERT_THROW(chan == 0);
+ return "0";
+}
+
+std::string rhodium_radio_control_impl::get_fe_name(
+ const size_t, const uhd::direction_t) const
+{
+ return RHODIUM_FE_NAME;
+}
+
+/**************************************************************************
+ * GPIO Controls
+ *************************************************************************/
+std::vector<std::string> rhodium_radio_control_impl::get_gpio_banks() const
+{
+ return {RHODIUM_FPGPIO_BANK};
+}
+
+void rhodium_radio_control_impl::set_gpio_attr(
+ const std::string& bank, const std::string& attr, const uint32_t value)
+{
+ if (bank != RHODIUM_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 rhodium_radio_control_impl::get_gpio_attr(
+ const std::string& bank, const std::string& attr)
+{
+ if (bank != RHODIUM_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 rhodium_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);
+ _db_eeprom = this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx);
+}
+
+eeprom_map_t rhodium_radio_control_impl::get_db_eeprom()
+{
+ return _db_eeprom;
+}
+
+/**************************************************************************
+ * Sensor API
+ *************************************************************************/
+std::vector<std::string> rhodium_radio_control_impl::get_rx_sensor_names(size_t) const
+{
+ return _rx_sensor_names;
+}
+
+sensor_value_t rhodium_radio_control_impl::get_rx_sensor(
+ const std::string& name, size_t chan)
+{
+ if (!uhd::has(_rx_sensor_names, name)) {
+ RFNOC_LOG_ERROR("Invalid RX sensor name: " << name);
+ throw uhd::key_error("Invalid RX sensor name!");
+ }
+ 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> rhodium_radio_control_impl::get_tx_sensor_names(size_t) const
+{
+ return _tx_sensor_names;
+}
+
+sensor_value_t rhodium_radio_control_impl::get_tx_sensor(
+ const std::string& name, size_t chan)
+{
+ if (!uhd::has(_rx_sensor_names, name)) {
+ RFNOC_LOG_ERROR("Invalid RX sensor name: " << name);
+ throw uhd::key_error("Invalid RX sensor name!");
+ }
+ 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));
+}
+
+bool rhodium_radio_control_impl::get_lo_lock_status(const direction_t dir) const
+{
+ return (dir == RX_DIRECTION) ? _rx_lo->get_lock_status() : _tx_lo->get_lock_status();
+}
+
+/**************************************************************************
+ * node_t API Calls
+ *************************************************************************/
+void rhodium_radio_control_impl::set_command_time(
+ uhd::time_spec_t time, const size_t chan)
+{
+ UHD_ASSERT_THROW(chan == 0);
+ node_t::set_command_time(time, chan);
+ _wb_iface->set_time(time);
+}
+
+// Register the block
+UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
+ rhodium_radio_control, RADIO_BLOCK, N320, "Radio", true, "radio_clk", "bus_clk");
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.hpp
index fad987b98..a70db79cc 100644
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.hpp
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control.hpp
@@ -1,5 +1,6 @@
//
// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
@@ -7,28 +8,28 @@
#ifndef INCLUDED_LIBUHD_RFNOC_RHODIUM_RADIO_CTRL_IMPL_HPP
#define INCLUDED_LIBUHD_RFNOC_RHODIUM_RADIO_CTRL_IMPL_HPP
+#include "rhodium_constants.hpp"
#include "rhodium_cpld_ctrl.hpp"
#include "rhodium_cpld_regs.hpp"
+#include <uhd/types/serial.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
+#include <uhdlib/rfnoc/radio_control_impl.hpp>
+#include <uhdlib/rfnoc/rpc_block_ctrl.hpp>
#include <uhdlib/usrp/common/lmx2592.hpp>
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
-#include <uhdlib/rfnoc/rpc_block_ctrl.hpp>
-#include <uhdlib/rfnoc/radio_ctrl_impl.hpp>
#include <uhdlib/usrp/cores/rx_frontend_core_3000.hpp>
#include <uhdlib/usrp/cores/tx_frontend_core_200.hpp>
-#include <uhd/types/serial.hpp>
-#include <uhd/usrp/dboard_manager.hpp>
-#include <uhd/usrp/gpio_defs.hpp>
#include <mutex>
-namespace uhd {
- namespace rfnoc {
+namespace uhd { namespace rfnoc {
/*! \brief Provide access to an Rhodium radio.
*/
-class rhodium_radio_ctrl_impl : public radio_ctrl_impl, public rpc_block_ctrl
+class rhodium_radio_control_impl : public radio_control_impl
{
public:
- typedef boost::shared_ptr<rhodium_radio_ctrl_impl> sptr;
+ typedef boost::shared_ptr<rhodium_radio_control_impl> sptr;
//! Frequency bands for RX. Bands are a function of the analog filter banks
enum class rx_band {
@@ -59,54 +60,108 @@ public:
/************************************************************************
* Structors
***********************************************************************/
- UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(rhodium_radio_ctrl)
- virtual ~rhodium_radio_ctrl_impl();
+ rhodium_radio_control_impl(make_args_ptr make_args);
+ virtual ~rhodium_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(const double rate);
+ double set_rate(double rate);
+ // 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 set_tx_bandwidth(const double bandwidth, const size_t chan);
- double set_rx_bandwidth(const double bandwidth, const size_t chan);
-
+ void set_tx_tune_args(const uhd::device_addr_t&, const size_t chan);
+ void set_rx_tune_args(const uhd::device_addr_t&, const size_t chan);
double set_tx_gain(const double gain, const size_t chan);
double set_rx_gain(const double gain, const size_t chan);
- // LO Property Getters
- std::vector<std::string> get_tx_lo_names(const size_t chan);
- std::vector<std::string> get_rx_lo_names(const size_t chan);
- std::vector<std::string> get_tx_lo_sources(const std::string& name, const size_t chan);
- std::vector<std::string> get_rx_lo_sources(const std::string& name, const size_t chan);
- freq_range_t get_tx_lo_freq_range(const std::string& name, const size_t chan);
- freq_range_t get_rx_lo_freq_range(const std::string& name, const size_t chan);
+ // Getters
+ std::vector<std::string> get_tx_antennas(const size_t) const;
+ std::vector<std::string> get_rx_antennas(const size_t) const;
+ uhd::freq_range_t get_tx_frequency_range(const size_t) const;
+ uhd::freq_range_t get_rx_frequency_range(const size_t) const;
+ uhd::gain_range_t get_tx_gain_range(const size_t) const;
+ uhd::gain_range_t get_rx_gain_range(const size_t) const;
+ uhd::meta_range_t get_tx_bandwidth_range(size_t) const;
+ uhd::meta_range_t get_rx_bandwidth_range(size_t) const;
- // LO Frequency Control
- double set_tx_lo_freq(const double freq, const std::string& name, const size_t chan);
- double set_rx_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);
+ /**************************************************************************
+ * 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) 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);
+ 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);
-
- // LO Source Control
- void set_tx_lo_source(const std::string& src, const std::string& name, const size_t chan);
- void set_rx_lo_source(const std::string& src, const std::string& name, 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;
+ 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);
- const std::string get_rx_lo_source(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);
// LO Export Control
- void set_tx_lo_export_enabled(const bool enabled, const std::string& name, const size_t chan);
- void set_rx_lo_export_enabled(const bool enabled, const std::string& name, const size_t chan);
+ void set_tx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan);
+ void set_rx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan);
bool get_tx_lo_export_enabled(const std::string& name, const size_t chan);
bool get_rx_lo_export_enabled(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) const;
+ 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) const;
+ uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan);
+
+ /**************************************************************************
+ * 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;
+
+ /**************************************************************************
+ * node_t API Calls
+ *************************************************************************/
+ void set_command_time(uhd::time_spec_t time, const size_t chan);
+
+ /************************************************************************
+ * ??? calls
+ ***********************************************************************/
// LO Distribution Control
void set_tx_lo_output_enabled(const bool enabled, const std::string& port_name, const size_t chan);
void set_rx_lo_output_enabled(const bool enabled, const std::string& port_name, const size_t chan);
@@ -139,38 +194,49 @@ public:
double get_tx_lo_power(const std::string &name, const size_t chan);
double get_rx_lo_power(const std::string &name, const 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);
-
- void set_rpc_client(
- uhd::rpc_client::sptr rpcc,
- const uhd::device_addr_t &block_args
- );
private:
/**************************************************************************
+ * noc_block_base API
+ *************************************************************************/
+ //! Safely shut down all peripherals
+ //
+ // Reminder: After this is called, no peeks and pokes are allowed!
+ void deinit()
+ {
+ RFNOC_LOG_TRACE("deinit()");
+ // Remove access to all peripherals
+ _wb_iface.reset();
+ _spi.reset();
+ _tx_lo.reset();
+ _rx_lo.reset();
+ _cpld.reset();
+ _gpio.reset();
+ _fp_gpio.reset();
+ _rx_fe_core.reset();
+ _tx_fe_core.reset();
+ }
+
+ /**************************************************************************
* Helpers
*************************************************************************/
//! Initialize all the peripherals connected to this block
void _init_peripherals();
+ //! Sync up with MPM
+ void _init_mpm();
+
//! Set state of this class to sensible defaults
void _init_defaults();
//! Init a subtree for the RF frontends
- void _init_frontend_subtree(
- uhd::property_tree::sptr subtree,
- const size_t chan_idx
- );
+ void _init_frontend_subtree(uhd::property_tree::sptr subtree);
//! Initialize property tree
void _init_prop_tree();
//! Discover and initialize any mpm sensors
- void _init_mpm_sensors(
- const direction_t dir,
- const size_t chan_idx
- );
+ void _init_mpm_sensors(const direction_t dir, const size_t chan_idx);
//! Get the frequency range for an LO
freq_range_t _get_lo_freq_range(const std::string &name) const;
@@ -235,8 +301,6 @@ private:
// NOTE: Returns false if frequency is out of Rh's tx frequency range
static bool _is_tx_lowband(const double freq);
- //! Return the gain range for dir
- static uhd::gain_range_t _get_gain_range(const direction_t dir);
//! Return the gain range of the LMX LO
static uhd::gain_range_t _get_lo_gain_range();
//! Return the power setting range of the LMX LO
@@ -303,12 +367,18 @@ private:
//! Daughterboard info from MPM
std::map<std::string, std::string> _dboard_info;
- //! Additional block args; gets set during set_rpc_client()
- uhd::device_addr_t _block_args;
+ //! Reference to the MB controller
+ mpmd_mb_controller::sptr _n320_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 wb_iface compat adapter. This will call into this->regs()
+ uhd::timed_wb_iface::sptr _wb_iface;
+
//! Reference to the SPI core
uhd::spi_iface::sptr _spi;
@@ -343,7 +413,8 @@ private:
std::string _rx_fe_connection;
std::string _tx_fe_connection;
//! Desired RF frequency
- std::map<direction_t,double> _desired_rf_freq = { {RX_DIRECTION, 2.44e9}, {TX_DIRECTION, 2.44e9} };
+ std::map<direction_t, double> _desired_rf_freq = {
+ {RX_DIRECTION, 2.44e9}, {TX_DIRECTION, 2.44e9}};
//! Frequency at which gain setting was last applied. The CPLD requires a new gain
// control write when switching between lowband and highband frequencies, so save
// the frequency when sending a gain control command.
@@ -356,7 +427,8 @@ private:
double _lo_rx_power = 0.0;
double _lo_tx_power = 0.0;
//! Gain profile
- std::map<direction_t,std::string> _gain_profile = { {RX_DIRECTION, "default"}, {TX_DIRECTION, "default"} };
+ std::map<direction_t, std::string> _gain_profile = {
+ {RX_DIRECTION, "default"}, {TX_DIRECTION, "default"}};
//! LO source
std::string _rx_lo_source = "internal";
@@ -377,7 +449,30 @@ private:
bool _lo_dist_rx_out_enabled[4] = { false, false, false, false };
bool _lo_dist_tx_out_enabled[4] = { false, false, false, false };
-}; /* class radio_ctrl_impl */
+ std::unordered_map<uhd::direction_t, uhd::device_addr_t, std::hash<size_t>>
+ _tune_args{{uhd::RX_DIRECTION, uhd::device_addr_t()},
+ {uhd::TX_DIRECTION, uhd::device_addr_t()}};
+
+ //! Cache the contents of the DB EEPROM
+ uhd::eeprom_map_t _db_eeprom;
+
+ //! Cached list of RX sensor names
+ std::vector<std::string> _rx_sensor_names{"lo_locked"};
+ //! Cached list of TX sensor names
+ std::vector<std::string> _tx_sensor_names{"lo_locked"};
+
+ property_t<std::string> _spur_dodging_mode{SPUR_DODGING_PROP_NAME,
+ RHODIUM_DEFAULT_SPUR_DOGING_MODE,
+ {res_source_info::USER}};
+ property_t<double> _spur_dodging_threshold{SPUR_DODGING_THRESHOLD_PROP_NAME,
+ RHODIUM_DEFAULT_SPUR_DOGING_THRESHOLD,
+ {res_source_info::USER}};
+ property_t<std::string> _highband_spur_reduction_mode{
+ HIGHBAND_SPUR_REDUCTION_PROP_NAME,
+ RHODIUM_DEFAULT_HB_SPUR_REDUCTION_MODE,
+ {res_source_info::USER}};
+
+}; /* class rhodium_radio_control_impl */
}} /* namespace uhd::rfnoc */
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control_cpld.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_cpld.cpp
new file mode 100644
index 000000000..f4f17e8ab
--- /dev/null
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_cpld.cpp
@@ -0,0 +1,252 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "rhodium_constants.hpp"
+#include "rhodium_cpld_ctrl.hpp"
+#include "rhodium_radio_control.hpp"
+#include <uhd/utils/log.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+namespace {
+
+const char* rx_band_to_log(rhodium_radio_control_impl::rx_band rx_band)
+{
+ switch (rx_band) {
+ case rhodium_radio_control_impl::rx_band::RX_BAND_0:
+ return "0";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_1:
+ return "1";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_2:
+ return "2";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_3:
+ return "3";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_4:
+ return "4";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_5:
+ return "5";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_6:
+ return "6";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_7:
+ return "7";
+ case rhodium_radio_control_impl::rx_band::RX_BAND_INVALID:
+ return "INVALID";
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+const char* tx_band_to_log(rhodium_radio_control_impl::tx_band tx_band)
+{
+ switch (tx_band) {
+ case rhodium_radio_control_impl::tx_band::TX_BAND_0:
+ return "0";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_1:
+ return "1";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_2:
+ return "2";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_3:
+ return "3";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_4:
+ return "4";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_5:
+ return "5";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_6:
+ return "6";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_7:
+ return "7";
+ case rhodium_radio_control_impl::tx_band::TX_BAND_INVALID:
+ return "INVALID";
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+} // namespace
+
+void rhodium_radio_control_impl::_update_rx_freq_switches(const double freq)
+{
+ RFNOC_LOG_TRACE("Update all RX freq related switches. f=" << freq << " Hz");
+ const auto band = _map_freq_to_rx_band(freq);
+ RFNOC_LOG_TRACE("Selected band " << rx_band_to_log(band));
+
+ // select values for lowband/highband switches
+ const bool is_lowband = (band == rx_band::RX_BAND_0);
+ auto rx_sw2_sw7 = is_lowband ? rhodium_cpld_ctrl::RX_SW2_SW7_LOWBANDFILTERBANK
+ : rhodium_cpld_ctrl::RX_SW2_SW7_HIGHBANDFILTERBANK;
+ auto rx_hb_lb_sel = is_lowband ? rhodium_cpld_ctrl::RX_HB_LB_SEL_LOWBAND
+ : rhodium_cpld_ctrl::RX_HB_LB_SEL_HIGHBAND;
+
+ // select values for filter bank switches
+ rhodium_cpld_ctrl::rx_sw3_t rx_sw3;
+ rhodium_cpld_ctrl::rx_sw4_sw5_t rx_sw4_sw5;
+ rhodium_cpld_ctrl::rx_sw6_t rx_sw6;
+ switch (band) {
+ case rx_band::RX_BAND_0:
+ // Low band doesn't use the filter banks, use configuration for band 1
+ case rx_band::RX_BAND_1:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
+ break;
+ case rx_band::RX_BAND_2:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0760X1100MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
+ break;
+ case rx_band::RX_BAND_3:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER1100X1410MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
+ break;
+ case rx_band::RX_BAND_4:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER1410X2050MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
+ break;
+ case rx_band::RX_BAND_5:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER2050X3000MHZ;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER2050X3000MHZ;
+ break;
+ case rx_band::RX_BAND_6:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER3000X4500MHZ;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER3000X4500MHZ;
+ break;
+ case rx_band::RX_BAND_7:
+ rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER4500X6000MHZ;
+ rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
+ rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER4500X6000MHZ;
+ break;
+ case rx_band::RX_BAND_INVALID:
+ throw uhd::runtime_error(
+ str(boost::format("Cannot map RX frequency to band: %f") % freq));
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ // commit settings to cpld
+ _cpld->set_rx_switches(rx_sw2_sw7, rx_sw3, rx_sw4_sw5, rx_sw6, rx_hb_lb_sel);
+}
+
+void rhodium_radio_control_impl::_update_tx_freq_switches(const double freq)
+{
+ RFNOC_LOG_TRACE("Update all TX freq related switches. f=" << freq << " Hz");
+ const auto band = _map_freq_to_tx_band(freq);
+ RFNOC_LOG_TRACE("Selected band " << tx_band_to_log(band));
+
+ // select values for lowband/highband switches
+ const bool is_lowband = (band == tx_band::TX_BAND_0);
+ auto tx_hb_lb_sel = is_lowband ? rhodium_cpld_ctrl::TX_HB_LB_SEL_LOWBAND
+ : rhodium_cpld_ctrl::TX_HB_LB_SEL_HIGHBAND;
+
+ // select values for filter bank switches
+ rhodium_cpld_ctrl::tx_sw2_t tx_sw2;
+ rhodium_cpld_ctrl::tx_sw3_sw4_t tx_sw3_sw4;
+ rhodium_cpld_ctrl::tx_sw5_t tx_sw5;
+ switch (band) {
+ case tx_band::TX_BAND_0:
+ // Low band doesn't use the filter banks, use configuration for band 1
+ case tx_band::TX_BAND_1:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
+ break;
+ case tx_band::TX_BAND_2:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1000MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
+ break;
+ case tx_band::TX_BAND_3:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1350MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
+ break;
+ case tx_band::TX_BAND_4:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1900MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
+ break;
+ case tx_band::TX_BAND_5:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP3000MHZ;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP3000MHZ;
+ break;
+ case tx_band::TX_BAND_6:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP4100MHZ;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP4100MHZ;
+ break;
+ case tx_band::TX_BAND_7:
+ tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP6000MHZ;
+ tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
+ tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP6000MHZ;
+ break;
+ case tx_band::TX_BAND_INVALID:
+ throw uhd::runtime_error(
+ str(boost::format("Cannot map TX frequency to band: %f") % freq));
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ // commit settings to cpld
+ _cpld->set_tx_switches(tx_sw2, tx_sw3_sw4, tx_sw5, tx_hb_lb_sel);
+}
+
+void rhodium_radio_control_impl::_update_rx_input_switches(const std::string& input)
+{
+ RFNOC_LOG_TRACE("Update all RX input related switches. input=" << input);
+ const rhodium_cpld_ctrl::cal_iso_sw_t cal_iso =
+ (input == "CAL") ? rhodium_cpld_ctrl::CAL_ISO_CALLOOPBACK
+ : rhodium_cpld_ctrl::CAL_ISO_ISOLATION;
+ const rhodium_cpld_ctrl::rx_sw1_t sw1 = [input] {
+ if (input == "TX/RX") {
+ return rhodium_cpld_ctrl::RX_SW1_FROMTXRXINPUT;
+ } else if (input == "RX2") {
+ return rhodium_cpld_ctrl::RX_SW1_FROMRX2INPUT;
+ } else if (input == "CAL") {
+ return rhodium_cpld_ctrl::RX_SW1_FROMCALLOOPBACK;
+ } else if (input == "TERM") {
+ return rhodium_cpld_ctrl::RX_SW1_ISOLATION;
+ } else {
+ throw uhd::runtime_error(
+ "Invalid antenna in _update_rx_input_switches: " + input);
+ }
+ }();
+
+ RFNOC_LOG_TRACE("Selected switch values:"
+ " sw1="
+ << sw1 << " cal_iso=" << cal_iso);
+ _cpld->set_rx_input_switches(sw1, cal_iso);
+}
+
+void rhodium_radio_control_impl::_update_tx_output_switches(const std::string& output)
+{
+ RFNOC_LOG_TRACE("Update all TX output related switches. output=" << output);
+ rhodium_cpld_ctrl::tx_sw1_t sw1;
+
+ if (output == "TX/RX") {
+ // SW1 needs to select low/high band
+ if (_is_tx_lowband(get_tx_frequency(0))) {
+ sw1 = rhodium_cpld_ctrl::TX_SW1_TOLOWBAND;
+ } else {
+ sw1 = rhodium_cpld_ctrl::TX_SW1_TOSWITCH2;
+ }
+ } else if (output == "CAL") {
+ sw1 = rhodium_cpld_ctrl::TX_SW1_TOCALLOOPBACK;
+ } else if (output == "TERM") {
+ sw1 = rhodium_cpld_ctrl::TX_SW1_ISOLATION;
+ } else {
+ throw uhd::runtime_error(
+ "Invalid antenna in _update_tx_output_switches: " + output);
+ }
+
+ RFNOC_LOG_TRACE("Selected switch values: sw1=" << sw1);
+ _cpld->set_tx_output_switches(sw1);
+}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp
new file mode 100644
index 000000000..d6b7afd09
--- /dev/null
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_init.cpp
@@ -0,0 +1,611 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "rhodium_constants.hpp"
+#include "rhodium_radio_control.hpp"
+#include <uhd/transport/chdr.hpp>
+#include <uhd/types/eeprom.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/rfnoc/reg_iface_adapter.hpp>
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
+#include <uhdlib/usrp/cores/spi_core_3000.hpp>
+#include <string>
+#include <vector>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+namespace {
+enum slave_select_t {
+ SEN_CPLD = 8,
+ SEN_TX_LO = 1,
+ SEN_RX_LO = 2,
+ SEN_LO_DIST = 4 /* Unused */
+};
+
+constexpr double RHODIUM_DEFAULT_FREQ = 2.5e9; // Hz
+// An invalid default index ensures that set gain will apply settings
+// the first time it is called
+constexpr double RHODIUM_DEFAULT_INVALID_GAIN = -1; // gain index
+constexpr double RHODIUM_DEFAULT_GAIN = 0; // gain index
+constexpr double RHODIUM_DEFAULT_LO_GAIN = 30; // gain index
+constexpr char RHODIUM_DEFAULT_RX_ANTENNA[] = "RX2";
+constexpr char RHODIUM_DEFAULT_TX_ANTENNA[] = "TX/RX";
+constexpr auto RHODIUM_DEFAULT_MASH_ORDER = lmx2592_iface::mash_order_t::THIRD;
+
+//! Returns the SPI config used by the CPLD
+spi_config_t _get_cpld_spi_config()
+{
+ spi_config_t spi_config;
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 10;
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+ spi_config.miso_edge = spi_config_t::EDGE_FALL;
+
+ return spi_config;
+}
+
+//! Returns the SPI config used by the TX LO
+spi_config_t _get_tx_lo_spi_config()
+{
+ spi_config_t spi_config;
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 10;
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+ spi_config.miso_edge = spi_config_t::EDGE_FALL;
+
+ return spi_config;
+}
+
+//! Returns the SPI config used by the RX LO
+spi_config_t _get_rx_lo_spi_config()
+{
+ spi_config_t spi_config;
+ spi_config.use_custom_divider = true;
+ spi_config.divider = 10;
+ spi_config.mosi_edge = spi_config_t::EDGE_RISE;
+ spi_config.miso_edge = spi_config_t::EDGE_FALL;
+
+ return spi_config;
+}
+
+std::function<void(uint32_t)> _generate_write_spi(
+ uhd::spi_iface::sptr spi, slave_select_t slave, spi_config_t config)
+{
+ return [spi, slave, config](const uint32_t transaction) {
+ spi->write_spi(slave, config, transaction, 24);
+ };
+}
+
+std::function<uint32_t(uint32_t)> _generate_read_spi(
+ uhd::spi_iface::sptr spi, slave_select_t slave, spi_config_t config)
+{
+ return [spi, slave, config](const uint32_t transaction) {
+ return spi->read_spi(slave, config, transaction, 24);
+ };
+}
+} // namespace
+
+void rhodium_radio_control_impl::_init_defaults()
+{
+ RFNOC_LOG_TRACE("Initializing defaults...");
+ const size_t num_rx_chans = get_num_output_ports();
+ const size_t num_tx_chans = get_num_input_ports();
+ UHD_ASSERT_THROW(num_tx_chans == RHODIUM_NUM_CHANS);
+ UHD_ASSERT_THROW(num_rx_chans == RHODIUM_NUM_CHANS);
+
+ for (size_t chan = 0; chan < num_rx_chans; chan++) {
+ radio_control_impl::set_rx_frequency(RHODIUM_DEFAULT_FREQ, chan);
+ radio_control_impl::set_rx_gain(RHODIUM_DEFAULT_INVALID_GAIN, chan);
+ radio_control_impl::set_rx_antenna(RHODIUM_DEFAULT_RX_ANTENNA, chan);
+ radio_control_impl::set_rx_bandwidth(RHODIUM_DEFAULT_BANDWIDTH, chan);
+ }
+
+ for (size_t chan = 0; chan < num_tx_chans; chan++) {
+ radio_control_impl::set_tx_frequency(RHODIUM_DEFAULT_FREQ, chan);
+ radio_control_impl::set_tx_gain(RHODIUM_DEFAULT_INVALID_GAIN, chan);
+ radio_control_impl::set_tx_antenna(RHODIUM_DEFAULT_TX_ANTENNA, chan);
+ radio_control_impl::set_tx_bandwidth(RHODIUM_DEFAULT_BANDWIDTH, chan);
+ }
+
+ register_property(&_spur_dodging_mode);
+ register_property(&_spur_dodging_threshold);
+ register_property(&_highband_spur_reduction_mode);
+
+ // Update configurable block arguments from the device arguments provided
+ const auto block_args = get_block_args();
+ if (block_args.has_key(SPUR_DODGING_PROP_NAME)) {
+ _spur_dodging_mode.set(block_args.get(SPUR_DODGING_PROP_NAME));
+ }
+ if (block_args.has_key(SPUR_DODGING_THRESHOLD_PROP_NAME)) {
+ _spur_dodging_threshold.set(block_args.cast<double>(
+ SPUR_DODGING_THRESHOLD_PROP_NAME, RHODIUM_DEFAULT_SPUR_DOGING_THRESHOLD));
+ }
+ if (block_args.has_key(HIGHBAND_SPUR_REDUCTION_PROP_NAME)) {
+ _highband_spur_reduction_mode.set(
+ block_args.get(HIGHBAND_SPUR_REDUCTION_PROP_NAME));
+ }
+}
+
+void rhodium_radio_control_impl::_init_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);
+ _wb_iface = RFNOC_MAKE_WB_IFACE(0, 0);
+
+ RFNOC_LOG_TRACE("Initializing CPLD...");
+ _cpld = std::make_shared<rhodium_cpld_ctrl>(
+ _generate_write_spi(this->_spi, SEN_CPLD, _get_cpld_spi_config()),
+ _generate_read_spi(this->_spi, SEN_CPLD, _get_cpld_spi_config()));
+
+ RFNOC_LOG_TRACE("Initializing TX frontend DSP core...")
+ _tx_fe_core = tx_frontend_core_200::make(_wb_iface, n320_regs::SR_TX_FE_BASE);
+ _tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
+ _tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
+ _tx_fe_core->populate_subtree(get_tree()->subtree(FE_PATH / "tx_fe_corrections" / 0));
+
+ RFNOC_LOG_TRACE("Initializing RX frontend DSP core...")
+ _rx_fe_core = rx_frontend_core_3000::make(_wb_iface, n320_regs::SR_TX_FE_BASE);
+ _rx_fe_core->set_adc_rate(_master_clock_rate);
+ _rx_fe_core->set_dc_offset(rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE);
+ _rx_fe_core->set_dc_offset_auto(rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE);
+ _rx_fe_core->set_iq_balance(rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE);
+ _rx_fe_core->populate_subtree(get_tree()->subtree(FE_PATH / "rx_fe_corrections" / 0));
+
+ RFNOC_LOG_TRACE("Writing initial gain values...");
+ set_tx_gain(RHODIUM_DEFAULT_GAIN, 0);
+ set_tx_lo_gain(RHODIUM_DEFAULT_LO_GAIN, RHODIUM_LO1, 0);
+ set_rx_gain(RHODIUM_DEFAULT_GAIN, 0);
+ set_rx_lo_gain(RHODIUM_DEFAULT_LO_GAIN, RHODIUM_LO1, 0);
+
+ RFNOC_LOG_TRACE("Initializing TX LO...");
+ _tx_lo = lmx2592_iface::make(
+ _generate_write_spi(this->_spi, SEN_TX_LO, _get_tx_lo_spi_config()),
+ _generate_read_spi(this->_spi, SEN_TX_LO, _get_tx_lo_spi_config()));
+
+ RFNOC_LOG_TRACE("Writing initial TX LO state...");
+ _tx_lo->set_reference_frequency(RHODIUM_LO1_REF_FREQ);
+ _tx_lo->set_mash_order(RHODIUM_DEFAULT_MASH_ORDER);
+
+ RFNOC_LOG_TRACE("Initializing RX LO...");
+ _rx_lo = lmx2592_iface::make(
+ _generate_write_spi(this->_spi, SEN_RX_LO, _get_rx_lo_spi_config()),
+ _generate_read_spi(this->_spi, SEN_RX_LO, _get_rx_lo_spi_config()));
+
+ RFNOC_LOG_TRACE("Writing initial RX LO state...");
+ _rx_lo->set_reference_frequency(RHODIUM_LO1_REF_FREQ);
+ _rx_lo->set_mash_order(RHODIUM_DEFAULT_MASH_ORDER);
+
+ RFNOC_LOG_TRACE("Initializing GPIOs...");
+ // DB GPIOs
+ _gpio = usrp::gpio_atr::gpio_atr_3000::make(_wb_iface,
+ n320_regs::SR_DB_GPIO,
+ n320_regs::RB_DB_GPIO,
+ n320_regs::PERIPH_REG_OFFSET);
+ _gpio->set_atr_mode(usrp::gpio_atr::MODE_ATR, // Enable ATR mode for Rhodium bits
+ RHODIUM_GPIO_MASK);
+ _gpio->set_atr_mode(usrp::gpio_atr::MODE_GPIO, // Disable ATR mode for unused bits
+ ~RHODIUM_GPIO_MASK);
+ _gpio->set_gpio_ddr(usrp::gpio_atr::DDR_OUTPUT, // Make all GPIOs outputs
+ usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ _fp_gpio = gpio_atr::gpio_atr_3000::make(_wb_iface,
+ n320_regs::SR_FP_GPIO,
+ n320_regs::RB_FP_GPIO,
+ n320_regs::PERIPH_REG_OFFSET);
+
+ RFNOC_LOG_TRACE("Set initial ATR values...");
+ _update_atr(RHODIUM_DEFAULT_TX_ANTENNA, TX_DIRECTION);
+ _update_atr(RHODIUM_DEFAULT_RX_ANTENNA, RX_DIRECTION);
+
+ // Updating the TX frequency path may include an update to SW10, which is
+ // GPIO controlled, so this must follow CPLD and GPIO initialization
+ RFNOC_LOG_TRACE("Writing initial switch values...");
+ _update_tx_freq_switches(RHODIUM_DEFAULT_FREQ);
+ _update_rx_freq_switches(RHODIUM_DEFAULT_FREQ);
+
+ // Antenna setting requires both CPLD and GPIO control
+ RFNOC_LOG_TRACE("Setting initial antenna settings");
+ _update_tx_output_switches(RHODIUM_DEFAULT_TX_ANTENNA);
+ _update_rx_input_switches(RHODIUM_DEFAULT_RX_ANTENNA);
+
+ RFNOC_LOG_TRACE("Checking for existence of LO Distribution board");
+ _lo_dist_present =
+ _rpcc->request_with_token<bool>(_rpc_prefix + "is_lo_dist_present");
+ RFNOC_LOG_DEBUG(
+ "LO distribution board is" << (_lo_dist_present ? "" : " NOT") << " present");
+
+ RFNOC_LOG_TRACE("Reading EEPROM content...");
+ const size_t db_idx = get_block_id().get_block_count();
+ _db_eeprom = this->_rpcc->request_with_token<eeprom_map_t>("get_db_eeprom", db_idx);
+}
+
+// Reminder: The property must not own any properties, it can only interact with
+// the API of this block!
+void rhodium_radio_control_impl::_init_frontend_subtree(uhd::property_tree::sptr subtree)
+{
+ const fs_path tx_fe_path = fs_path("tx_frontends") / 0;
+ const fs_path rx_fe_path = fs_path("rx_frontends") / 0;
+ RFNOC_LOG_TRACE("Adding non-RFNoC block properties for channel 0"
+ << " to prop tree path " << tx_fe_path << " and " << rx_fe_path);
+ // TX Standard attributes
+ subtree->create<std::string>(tx_fe_path / "name").set(RHODIUM_FE_NAME);
+ subtree->create<std::string>(tx_fe_path / "connection")
+ .add_coerced_subscriber(
+ [this](const std::string& conn) { this->_set_tx_fe_connection(conn); })
+ .set_publisher([this]() { return this->_get_tx_fe_connection(); });
+ subtree->create<device_addr_t>(tx_fe_path / "tune_args")
+ .set(device_addr_t())
+ .add_coerced_subscriber(
+ [this](const device_addr_t& args) { set_tx_tune_args(args, 0); })
+ .set_publisher([this]() { return _tune_args.at(uhd::TX_DIRECTION); });
+ // RX Standard attributes
+ subtree->create<std::string>(rx_fe_path / "name").set(RHODIUM_FE_NAME);
+ subtree->create<std::string>(rx_fe_path / "connection")
+ .add_coerced_subscriber(
+ [this](const std::string& conn) { this->_set_rx_fe_connection(conn); })
+ .set_publisher([this]() { return this->_get_rx_fe_connection(); });
+ subtree->create<device_addr_t>(rx_fe_path / "tune_args")
+ .set(device_addr_t())
+ .add_coerced_subscriber(
+ [this](const device_addr_t& args) { set_rx_tune_args(args, 0); })
+ .set_publisher([this]() { return _tune_args.at(uhd::RX_DIRECTION); });
+ ;
+ // TX Antenna
+ subtree->create<std::string>(tx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber(
+ [this](const std::string& ant) { this->set_tx_antenna(ant, 0); })
+ .set_publisher([this]() { return this->get_tx_antenna(0); });
+ subtree->create<std::vector<std::string>>(tx_fe_path / "antenna" / "options")
+ .add_coerced_subscriber([](const std::vector<std::string>&) {
+ throw uhd::runtime_error("Attempting to update antenna options!");
+ })
+ .set_publisher([this]() { return get_tx_antennas(0); });
+ // RX Antenna
+ subtree->create<std::string>(rx_fe_path / "antenna" / "value")
+ .add_coerced_subscriber(
+ [this](const std::string& ant) { this->set_rx_antenna(ant, 0); })
+ .set_publisher([this]() { return this->get_rx_antenna(0); });
+ subtree->create<std::vector<std::string>>(rx_fe_path / "antenna" / "options")
+ .add_coerced_subscriber([](const std::vector<std::string>&) {
+ throw uhd::runtime_error("Attempting to update antenna options!");
+ })
+ .set_publisher([this]() { return get_rx_antennas(0); });
+ // TX frequency
+ subtree->create<double>(tx_fe_path / "freq" / "value")
+ .set_coercer(
+ [this](const double freq) { return this->set_tx_frequency(freq, 0); })
+ .set_publisher([this]() { return this->get_tx_frequency(0); });
+ subtree->create<meta_range_t>(tx_fe_path / "freq" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update freq range!");
+ })
+ .set_publisher([this]() { return get_tx_frequency_range(0); });
+ // RX frequency
+ subtree->create<double>(rx_fe_path / "freq" / "value")
+ .set_coercer(
+ [this](const double freq) { return this->set_rx_frequency(freq, 0); })
+ .set_publisher([this]() { return this->get_rx_frequency(0); });
+ subtree->create<meta_range_t>(rx_fe_path / "freq" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update freq range!");
+ })
+ .set_publisher([this]() { return get_rx_frequency_range(0); });
+ // TX bandwidth
+ subtree->create<double>(tx_fe_path / "bandwidth" / "value")
+ .set_coercer([this](const double bw) { return this->set_tx_bandwidth(bw, 0); })
+ .set_publisher([this]() { return this->get_tx_bandwidth(0); });
+ subtree->create<meta_range_t>(tx_fe_path / "bandwidth" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update bandwidth range!");
+ })
+ .set_publisher([this]() { return get_tx_bandwidth_range(0); });
+ // RX bandwidth
+ subtree->create<double>(rx_fe_path / "bandwidth" / "value")
+ .set_coercer([this](const double bw) { return this->set_rx_bandwidth(bw, 0); })
+ .set_publisher([this]() { return this->get_rx_bandwidth(0); });
+ subtree->create<meta_range_t>(rx_fe_path / "bandwidth" / "range")
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update bandwidth range!");
+ })
+ .set_publisher([this]() { return get_rx_bandwidth_range(0); });
+ // TX gains
+ subtree->create<double>(tx_fe_path / "gains" / "all" / "value")
+ .set_coercer([this](const double gain) { return this->set_tx_gain(gain, 0); })
+ .set_publisher([this]() { return radio_control_impl::get_tx_gain(0); });
+ 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]() { return get_tx_gain_range(0); });
+ // RX gains
+ subtree->create<double>(rx_fe_path / "gains" / "all" / "value")
+ .set_coercer([this](const double gain) { return this->set_rx_gain(gain, 0); })
+ .set_publisher([this]() { return radio_control_impl::get_rx_gain(0); });
+ 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]() { return get_rx_gain_range(0); });
+
+ // LO Specific
+ // RX LO
+ // RX LO1 Frequency
+ subtree->create<double>(rx_fe_path / "los" / RHODIUM_LO1 / "freq/value")
+ .set_publisher([this]() { return this->get_rx_lo_freq(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double freq) {
+ return this->set_rx_lo_freq(freq, RHODIUM_LO1, 0);
+ });
+ subtree->create<meta_range_t>(rx_fe_path / "los" / RHODIUM_LO1 / "freq/range")
+ .set_publisher([this]() { return this->get_rx_lo_freq_range(RHODIUM_LO1, 0); });
+ // RX LO1 Source
+ subtree
+ ->create<std::vector<std::string>>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "source/options")
+ .set_publisher([this]() { return this->get_rx_lo_sources(RHODIUM_LO1, 0); });
+ subtree->create<std::string>(rx_fe_path / "los" / RHODIUM_LO1 / "source/value")
+ .add_coerced_subscriber([this](const std::string& src) {
+ this->set_rx_lo_source(src, RHODIUM_LO1, 0);
+ })
+ .set_publisher([this]() { return this->get_rx_lo_source(RHODIUM_LO1, 0); });
+ // RX LO1 Export
+ subtree->create<bool>(rx_fe_path / "los" / RHODIUM_LO1 / "export")
+ .add_coerced_subscriber([this](bool enabled) {
+ this->set_rx_lo_export_enabled(enabled, RHODIUM_LO1, 0);
+ });
+ // RX LO1 Gain
+ subtree
+ ->create<double>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_GAIN / "value")
+ .set_publisher([this]() { return this->get_rx_lo_gain(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double gain) {
+ return this->set_rx_lo_gain(gain, RHODIUM_LO1, 0);
+ });
+ subtree
+ ->create<meta_range_t>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_GAIN / "range")
+ .set_publisher([]() { return rhodium_radio_control_impl::_get_lo_gain_range(); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update LO gain range!");
+ });
+ // RX LO1 Output Power
+ subtree
+ ->create<double>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_POWER / "value")
+ .set_publisher([this]() { return this->get_rx_lo_power(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double gain) {
+ return this->set_rx_lo_power(gain, RHODIUM_LO1, 0);
+ });
+ subtree
+ ->create<meta_range_t>(
+ rx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_POWER / "range")
+ .set_publisher([]() { return rhodium_radio_control_impl::_get_lo_power_range(); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update LO output power range!");
+ });
+ // RX LO2 Frequency
+ subtree->create<double>(rx_fe_path / "los" / RHODIUM_LO2 / "freq/value")
+ .set_publisher([this]() { return this->get_rx_lo_freq(RHODIUM_LO2, 0); })
+ .set_coercer(
+ [this](double freq) { return this->set_rx_lo_freq(freq, RHODIUM_LO2, 0); });
+ subtree->create<meta_range_t>(rx_fe_path / "los" / RHODIUM_LO2 / "freq/range")
+ .set_publisher([this]() { return this->get_rx_lo_freq_range(RHODIUM_LO2, 0); });
+ // RX LO2 Source
+ subtree
+ ->create<std::vector<std::string>>(
+ rx_fe_path / "los" / RHODIUM_LO2 / "source/options")
+ .set_publisher([this]() { return this->get_rx_lo_sources(RHODIUM_LO2, 0); });
+ subtree->create<std::string>(rx_fe_path / "los" / RHODIUM_LO2 / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_rx_lo_source(src, RHODIUM_LO2, 0); })
+ .set_publisher([this]() { return this->get_rx_lo_source(RHODIUM_LO2, 0); });
+ // RX LO2 Export
+ subtree->create<bool>(rx_fe_path / "los" / RHODIUM_LO2 / "export")
+ .add_coerced_subscriber([this](bool enabled) {
+ this->set_rx_lo_export_enabled(enabled, RHODIUM_LO2, 0);
+ });
+ // RX ALL LOs
+ subtree->create<std::string>(rx_fe_path / "los" / ALL_LOS / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_rx_lo_source(src, ALL_LOS, 0); })
+ .set_publisher([this]() { return this->get_rx_lo_source(ALL_LOS, 0); });
+ subtree
+ ->create<std::vector<std::string>>(
+ rx_fe_path / "los" / ALL_LOS / "source/options")
+ .set_publisher([this]() { return this->get_rx_lo_sources(ALL_LOS, 0); });
+ subtree->create<bool>(rx_fe_path / "los" / ALL_LOS / "export")
+ .add_coerced_subscriber(
+ [this](bool enabled) { this->set_rx_lo_export_enabled(enabled, ALL_LOS, 0); })
+ .set_publisher([this]() { return this->get_rx_lo_export_enabled(ALL_LOS, 0); });
+ // TX LO
+ // TX LO1 Frequency
+ subtree->create<double>(tx_fe_path / "los" / RHODIUM_LO1 / "freq/value ")
+ .set_publisher([this]() { return this->get_tx_lo_freq(RHODIUM_LO1, 0); })
+ .set_coercer(
+ [this](double freq) { return this->set_tx_lo_freq(freq, RHODIUM_LO1, 0); });
+ subtree->create<meta_range_t>(tx_fe_path / "los" / RHODIUM_LO1 / "freq/range")
+ .set_publisher([this]() { return this->get_rx_lo_freq_range(RHODIUM_LO1, 0); });
+ // TX LO1 Source
+ subtree
+ ->create<std::vector<std::string>>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "source/options")
+ .set_publisher([this]() { return this->get_tx_lo_sources(RHODIUM_LO1, 0); });
+ subtree->create<std::string>(tx_fe_path / "los" / RHODIUM_LO1 / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_tx_lo_source(src, RHODIUM_LO1, 0); })
+ .set_publisher([this]() { return this->get_tx_lo_source(RHODIUM_LO1, 0); });
+ // TX LO1 Export
+ subtree->create<bool>(tx_fe_path / "los" / RHODIUM_LO1 / "export")
+ .add_coerced_subscriber([this](bool enabled) {
+ this->set_tx_lo_export_enabled(enabled, RHODIUM_LO1, 0);
+ });
+ // TX LO1 Gain
+ subtree
+ ->create<double>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_GAIN / "value")
+ .set_publisher([this]() { return this->get_tx_lo_gain(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double gain) {
+ return this->set_tx_lo_gain(gain, RHODIUM_LO1, 0);
+ });
+ subtree
+ ->create<meta_range_t>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_GAIN / "range")
+ .set_publisher([]() { return rhodium_radio_control_impl::_get_lo_gain_range(); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update LO gain range!");
+ });
+ // TX LO1 Output Power
+ subtree
+ ->create<double>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_POWER / "value")
+ .set_publisher([this]() { return this->get_tx_lo_power(RHODIUM_LO1, 0); })
+ .set_coercer([this](const double gain) {
+ return this->set_tx_lo_power(gain, RHODIUM_LO1, 0);
+ });
+ subtree
+ ->create<meta_range_t>(
+ tx_fe_path / "los" / RHODIUM_LO1 / "gains" / RHODIUM_LO_POWER / "range")
+ .set_publisher([]() { return rhodium_radio_control_impl::_get_lo_power_range(); })
+ .add_coerced_subscriber([](const meta_range_t&) {
+ throw uhd::runtime_error("Attempting to update LO output power range!");
+ });
+ // TX LO2 Frequency
+ subtree->create<double>(tx_fe_path / "los" / RHODIUM_LO2 / "freq/value")
+ .set_publisher([this]() { return this->get_tx_lo_freq(RHODIUM_LO2, 0); })
+ .set_coercer(
+ [this](double freq) { return this->set_tx_lo_freq(freq, RHODIUM_LO2, 0); });
+ subtree->create<meta_range_t>(tx_fe_path / "los" / RHODIUM_LO2 / "freq/range")
+ .set_publisher([this]() { return this->get_tx_lo_freq_range(RHODIUM_LO2, 0); });
+ // TX LO2 Source
+ subtree
+ ->create<std::vector<std::string>>(
+ tx_fe_path / "los" / RHODIUM_LO2 / "source/options")
+ .set_publisher([this]() { return this->get_tx_lo_sources(RHODIUM_LO2, 0); });
+ subtree->create<std::string>(tx_fe_path / "los" / RHODIUM_LO2 / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_tx_lo_source(src, RHODIUM_LO2, 0); })
+ .set_publisher([this]() { return this->get_tx_lo_source(RHODIUM_LO2, 0); });
+ // TX LO2 Export
+ subtree->create<bool>(tx_fe_path / "los" / RHODIUM_LO2 / "export")
+ .add_coerced_subscriber([this](bool enabled) {
+ this->set_tx_lo_export_enabled(enabled, RHODIUM_LO2, 0);
+ });
+ // TX ALL LOs
+ subtree->create<std::string>(tx_fe_path / "los" / ALL_LOS / "source/value")
+ .add_coerced_subscriber(
+ [this](std::string src) { this->set_tx_lo_source(src, ALL_LOS, 0); })
+ .set_publisher([this]() { return this->get_tx_lo_source(ALL_LOS, 0); });
+ subtree
+ ->create<std::vector<std::string>>(
+ tx_fe_path / "los" / ALL_LOS / "source/options")
+ .set_publisher([this]() { return this->get_tx_lo_sources(ALL_LOS, 0); });
+ subtree->create<bool>(tx_fe_path / "los" / ALL_LOS / "export")
+ .add_coerced_subscriber(
+ [this](bool enabled) { this->set_tx_lo_export_enabled(enabled, ALL_LOS, 0); })
+ .set_publisher([this]() { return this->get_tx_lo_export_enabled(ALL_LOS, 0); });
+
+ // LO Distribution Output Ports
+ if (_lo_dist_present) {
+ for (const auto& port : LO_OUTPUT_PORT_NAMES) {
+ subtree
+ ->create<bool>(tx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port
+ / "export")
+ .add_coerced_subscriber([this, port](bool enabled) {
+ this->set_tx_lo_output_enabled(enabled, port, 0);
+ })
+ .set_publisher(
+ [this, port]() { return this->get_tx_lo_output_enabled(port, 0); });
+ subtree
+ ->create<bool>(rx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port
+ / "export")
+ .add_coerced_subscriber([this, port](bool enabled) {
+ this->set_rx_lo_output_enabled(enabled, port, 0);
+ })
+ .set_publisher(
+ [this, port]() { return this->get_rx_lo_output_enabled(port, 0); });
+ }
+ }
+
+ // Sensors
+ auto rx_sensor_names = get_rx_sensor_names(0);
+ 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]() { return get_rx_sensor(sensor_name, 0); });
+ }
+ auto tx_sensor_names = get_tx_sensor_names(0);
+ 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]() { return get_tx_sensor(sensor_name, 0); });
+ }
+}
+
+void rhodium_radio_control_impl::_init_prop_tree()
+{
+ this->_init_frontend_subtree(get_tree()->subtree(DB_PATH));
+ get_tree()->create<std::string>(fs_path("rx_codecs") / "name").set("ad9695-625");
+ get_tree()->create<std::string>(fs_path("tx_codecs") / "name").set("dac37j82");
+}
+
+void rhodium_radio_control_impl::_init_mpm()
+{
+ auto block_args = get_block_args();
+ 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(
+ std::string("Master clock rate mismatch. Device returns ")
+ + std::to_string(_master_clock_rate)
+ + " MHz, "
+ "but should have been "
+ + std::to_string(
+ block_args.cast<double>("master_clock_rate", _master_clock_rate))
+ + " MHz.");
+ }
+ 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);
+
+ // Unlike N310, N320 does not have any MPM sensors.
+}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_control_lo.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_lo.cpp
new file mode 100644
index 000000000..717a1c94f
--- /dev/null
+++ b/host/lib/usrp/dboard/rhodium/rhodium_radio_control_lo.cpp
@@ -0,0 +1,713 @@
+//
+// Copyright 2018 Ettus Research, a National Instruments Company
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "rhodium_constants.hpp"
+#include "rhodium_radio_control.hpp"
+#include <uhd/exception.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhdlib/utils/narrow.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::rfnoc;
+
+namespace {
+constexpr int NUM_THRESHOLDS = 13;
+constexpr std::array<double, NUM_THRESHOLDS> FREQ_THRESHOLDS = {
+ 0.45e9, 0.5e9, 1e9, 1.5e9, 2e9, 2.5e9, 3e9, 3.55e9, 4e9, 4.5e9, 5e9, 5.5e9, 6e9};
+constexpr std::array<int, NUM_THRESHOLDS> LMX_GAIN_VALUES = {
+ 18, 18, 17, 17, 17, 16, 12, 11, 11, 11, 10, 13, 18};
+const std::array<int, NUM_THRESHOLDS> DSA_RX_GAIN_VALUES = {
+ 19, 19, 21, 21, 20, 20, 22, 21, 20, 22, 22, 24, 26};
+const std::array<int, NUM_THRESHOLDS> DSA_TX_GAIN_VALUES = {
+ 19, 19, 21, 21, 20, 20, 22, 21, 22, 24, 24, 26, 28};
+
+// Describes the lowband LO in terms of the master clock rate
+const std::map<double, double> MCR_TO_LOWBAND_IF = {
+ {200e6, 1200e6},
+ {245.76e6, 1228.8e6},
+ {250e6, 1500e6},
+};
+
+// validation helpers
+
+std::vector<std::string> _get_lo_names()
+{
+ return {RHODIUM_LO1, RHODIUM_LO2};
+}
+
+void _validate_lo_name(const std::string& name, const std::string& function_name)
+{
+ if (!uhd::has(_get_lo_names(), name) and name != radio_control::ALL_LOS) {
+ throw uhd::value_error(
+ str(boost::format("%s was called with an invalid LO name: %s") % function_name
+ % name));
+ }
+}
+
+// object agnostic helpers
+std::vector<std::string> _get_lo_sources(const std::string& name)
+{
+ if (name == RHODIUM_LO1 or name == radio_control::ALL_LOS) {
+ return {"internal", "external"};
+ } else {
+ return {"internal"};
+ }
+}
+} // namespace
+
+/******************************************************************************
+ * Property Getters
+ *****************************************************************************/
+
+std::vector<std::string> rhodium_radio_control_impl::get_tx_lo_names(
+ const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_tx_lo_names(chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ return _get_lo_names();
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_rx_lo_names(
+ const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_rx_lo_names(chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ return _get_lo_names();
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_tx_lo_sources(
+ const std::string& name, const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_tx_lo_sources(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_sources");
+
+ return _get_lo_sources(name);
+}
+
+std::vector<std::string> rhodium_radio_control_impl::get_rx_lo_sources(
+ const std::string& name, const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_rx_lo_sources(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_sources");
+
+ return _get_lo_sources(name);
+}
+
+freq_range_t rhodium_radio_control_impl::_get_lo_freq_range(const std::string& name) const
+{
+ if (name == RHODIUM_LO1) {
+ return freq_range_t{RHODIUM_LO1_MIN_FREQ, RHODIUM_LO1_MAX_FREQ};
+ } else if (name == RHODIUM_LO2) {
+ // The Lowband LO is a fixed frequency
+ return freq_range_t{_get_lowband_lo_freq(), _get_lowband_lo_freq()};
+ } else {
+ throw uhd::runtime_error(
+ "LO frequency range must be retrieved for each stage individually");
+ }
+}
+
+freq_range_t rhodium_radio_control_impl::get_tx_lo_freq_range(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_freq_range(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_freq_range");
+
+ return _get_lo_freq_range(name);
+}
+
+freq_range_t rhodium_radio_control_impl::get_rx_lo_freq_range(
+ const std::string& name, const size_t chan) const
+{
+ RFNOC_LOG_TRACE("get_rx_lo_freq_range(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_freq_range");
+
+ return _get_lo_freq_range(name);
+}
+
+/******************************************************************************
+ * Frequency Control
+ *****************************************************************************/
+
+double rhodium_radio_control_impl::set_tx_lo_freq(
+ const double freq, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_tx_lo_freq(freq=" << freq << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_freq");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO frequency must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO cannot be tuned");
+ return _get_lowband_lo_freq();
+ }
+
+ const auto sd_enabled = _get_spur_dodging_enabled(TX_DIRECTION);
+ const auto sd_threshold = _get_spur_dodging_threshold(TX_DIRECTION);
+
+ _tx_lo_freq = _tx_lo->set_frequency(freq, sd_enabled, sd_threshold);
+ set_tx_lo_gain(_get_lo_dsa_setting(_tx_lo_freq, TX_DIRECTION), RHODIUM_LO1, chan);
+ set_tx_lo_power(_get_lo_power_setting(_tx_lo_freq), RHODIUM_LO1, chan);
+ _cpld->set_tx_lo_path(_tx_lo_freq);
+
+ return _tx_lo_freq;
+}
+
+double rhodium_radio_control_impl::set_rx_lo_freq(
+ const double freq, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_rx_lo_freq(freq=" << freq << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_rx_lo_freq");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO frequency must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO cannot be tuned");
+ return _get_lowband_lo_freq();
+ }
+
+ const auto sd_enabled = _get_spur_dodging_enabled(RX_DIRECTION);
+ const auto sd_threshold = _get_spur_dodging_threshold(RX_DIRECTION);
+
+ _rx_lo_freq = _rx_lo->set_frequency(freq, sd_enabled, sd_threshold);
+ set_rx_lo_gain(_get_lo_dsa_setting(_rx_lo_freq, RX_DIRECTION), RHODIUM_LO1, chan);
+ set_rx_lo_power(_get_lo_power_setting(_rx_lo_freq), RHODIUM_LO1, chan);
+ _cpld->set_rx_lo_path(_rx_lo_freq);
+
+ return _rx_lo_freq;
+}
+
+double rhodium_radio_control_impl::get_tx_lo_freq(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_freq(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_freq");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO frequency must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _tx_lo_freq : _get_lowband_lo_freq();
+}
+
+double rhodium_radio_control_impl::get_rx_lo_freq(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_freq(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_freq");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO frequency must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _rx_lo_freq : _get_lowband_lo_freq();
+}
+
+/******************************************************************************
+ * Source Control
+ *****************************************************************************/
+
+void rhodium_radio_control_impl::set_tx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_tx_lo_source(src=" << src << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_source");
+
+ if (name == RHODIUM_LO2) {
+ if (src != "internal") {
+ throw uhd::value_error("The Lowband LO can only be set to internal");
+ }
+ return;
+ }
+
+ if (src == "internal") {
+ _tx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, true);
+ _cpld->set_tx_lo_source(
+ rhodium_cpld_ctrl::tx_lo_input_sel_t::TX_LO_INPUT_SEL_INTERNAL);
+ } else if (src == "external") {
+ _tx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, false);
+ _cpld->set_tx_lo_source(
+ rhodium_cpld_ctrl::tx_lo_input_sel_t::TX_LO_INPUT_SEL_EXTERNAL);
+ } else {
+ throw uhd::value_error(
+ str(boost::format("set_tx_lo_source was called with an invalid LO source: %s "
+ "Valid sources are [internal, external]")
+ % src));
+ }
+
+ const bool enable_corrections = not _is_tx_lowband(get_tx_frequency(0))
+ and src == "internal";
+ _update_corrections(get_tx_frequency(0), TX_DIRECTION, enable_corrections);
+
+ _tx_lo_source = src;
+}
+
+void rhodium_radio_control_impl::set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_rx_lo_source(src=" << src << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_source");
+
+ if (name == RHODIUM_LO2) {
+ if (src != "internal") {
+ throw uhd::value_error("The Lowband LO can only be set to internal");
+ }
+ return;
+ }
+
+ if (src == "internal") {
+ _rx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, true);
+ _cpld->set_rx_lo_source(
+ rhodium_cpld_ctrl::rx_lo_input_sel_t::RX_LO_INPUT_SEL_INTERNAL);
+ } else if (src == "external") {
+ _rx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, false);
+ _cpld->set_rx_lo_source(
+ rhodium_cpld_ctrl::rx_lo_input_sel_t::RX_LO_INPUT_SEL_EXTERNAL);
+ } else {
+ throw uhd::value_error(
+ str(boost::format("set_rx_lo_source was called with an invalid LO source: %s "
+ "Valid sources for LO1 are [internal, external]")
+ % src));
+ }
+
+ const bool enable_corrections = not _is_rx_lowband(get_rx_frequency(0))
+ and src == "internal";
+ _update_corrections(get_rx_frequency(0), RX_DIRECTION, enable_corrections);
+
+ _rx_lo_source = src;
+}
+
+const std::string rhodium_radio_control_impl::get_tx_lo_source(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_source(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_source");
+ return (name == RHODIUM_LO1 or name == ALL_LOS) ? _tx_lo_source : "internal";
+}
+
+const std::string rhodium_radio_control_impl::get_rx_lo_source(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_source(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_source");
+ return (name == RHODIUM_LO1 or name == ALL_LOS) ? _rx_lo_source : "internal";
+}
+
+/******************************************************************************
+ * Export Control
+ *****************************************************************************/
+
+void rhodium_radio_control_impl::_set_lo1_export_enabled(
+ const bool enabled, const direction_t dir)
+{
+ auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
+ _lo_ctrl->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_B, enabled);
+ if (_lo_dist_present) {
+ const auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
+ _rpcc->notify_with_token(_rpc_prefix + "enable_lo_export", direction, enabled);
+ }
+}
+
+void rhodium_radio_control_impl::set_tx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_lo_export_enabled(enabled=" << enabled << ", name=" << name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_export_enabled");
+
+ if (name == RHODIUM_LO2) {
+ if (enabled) {
+ throw uhd::value_error("The lowband LO cannot be exported");
+ }
+ return;
+ }
+
+ _set_lo1_export_enabled(enabled, TX_DIRECTION);
+ _tx_lo_exported = enabled;
+}
+
+void rhodium_radio_control_impl::set_rx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_lo_export_enabled(enabled=" << enabled << ", name=" << name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_rx_lo_export_enabled");
+
+ if (name == RHODIUM_LO2) {
+ if (enabled) {
+ throw uhd::value_error("The lowband LO cannot be exported");
+ }
+ return;
+ }
+
+ _set_lo1_export_enabled(enabled, RX_DIRECTION);
+ _rx_lo_exported = enabled;
+}
+
+bool rhodium_radio_control_impl::get_tx_lo_export_enabled(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_export_enabled(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_export_enabled");
+
+ return (name == RHODIUM_LO1 or name == ALL_LOS) ? _tx_lo_exported : false;
+}
+
+bool rhodium_radio_control_impl::get_rx_lo_export_enabled(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_export_enabled(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_export_enabled");
+
+ return (name == RHODIUM_LO1 or name == ALL_LOS) ? _rx_lo_exported : false;
+}
+
+/******************************************************************************
+ * LO Distribution Control
+ *****************************************************************************/
+
+void rhodium_radio_control_impl::_validate_output_port(
+ const std::string& port_name, const std::string& function_name)
+{
+ if (!_lo_dist_present) {
+ throw uhd::runtime_error(
+ str(boost::format(
+ "%s can only be called if the LO distribution board was detected")
+ % function_name));
+ }
+
+ if (!uhd::has(LO_OUTPUT_PORT_NAMES, port_name)) {
+ throw uhd::value_error(
+ str(boost::format("%s was called with an invalid LO output port: %s Valid "
+ "ports are [LO_OUT_0, LO_OUT_1, LO_OUT_2, LO_OUT_3]")
+ % function_name % port_name));
+ }
+}
+
+void rhodium_radio_control_impl::_set_lo_output_enabled(
+ const bool enabled, const std::string& port_name, const direction_t dir)
+{
+ auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
+ auto name_iter =
+ std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
+ auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
+
+ _rpcc->notify_with_token(_rpc_prefix + "enable_lo_output", direction, index, enabled);
+ auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled
+ : _lo_dist_tx_out_enabled;
+ out_enabled[index] = enabled;
+}
+
+void rhodium_radio_control_impl::set_tx_lo_output_enabled(
+ const bool enabled, const std::string& port_name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_lo_output_enabled(enabled=" << enabled
+ << ", port_name=" << port_name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "set_tx_lo_output_enabled");
+
+ _set_lo_output_enabled(enabled, port_name, TX_DIRECTION);
+}
+
+void rhodium_radio_control_impl::set_rx_lo_output_enabled(
+ const bool enabled, const std::string& port_name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_lo_output_enabled(enabled=" << enabled
+ << ", port_name=" << port_name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "set_rx_lo_output_enabled");
+
+ _set_lo_output_enabled(enabled, port_name, RX_DIRECTION);
+}
+
+bool rhodium_radio_control_impl::_get_lo_output_enabled(
+ const std::string& port_name, const direction_t dir)
+{
+ auto name_iter =
+ std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
+ auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
+
+ auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled
+ : _lo_dist_tx_out_enabled;
+ return out_enabled[index];
+}
+
+bool rhodium_radio_control_impl::get_tx_lo_output_enabled(
+ const std::string& port_name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "get_tx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "get_tx_lo_output_enabled");
+
+ return _get_lo_output_enabled(port_name, TX_DIRECTION);
+}
+
+bool rhodium_radio_control_impl::get_rx_lo_output_enabled(
+ const std::string& port_name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "get_rx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_output_port(port_name, "get_rx_lo_output_enabled");
+
+ return _get_lo_output_enabled(port_name, RX_DIRECTION);
+}
+
+/******************************************************************************
+ * Gain Control
+ *****************************************************************************/
+
+double rhodium_radio_control_impl::set_tx_lo_gain(
+ double gain, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_tx_lo_gain(gain=" << gain << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_gain");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO gain must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO does not have configurable gain");
+ return 0.0;
+ }
+
+ auto index = _get_lo_gain_range().clip(gain);
+
+ _cpld->set_lo_gain(index, TX_DIRECTION);
+ _lo_tx_gain = index;
+ return _lo_tx_gain;
+}
+
+double rhodium_radio_control_impl::set_rx_lo_gain(
+ double gain, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE(
+ "set_rx_lo_gain(gain=" << gain << ", name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_rx_lo_gain");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO gain must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO does not have configurable gain");
+ return 0.0;
+ }
+
+ auto index = _get_lo_gain_range().clip(gain);
+
+ _cpld->set_lo_gain(index, RX_DIRECTION);
+ _lo_rx_gain = index;
+ return _lo_rx_gain;
+}
+
+double rhodium_radio_control_impl::get_tx_lo_gain(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_gain(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_gain");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO gain must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _lo_rx_gain : 0.0;
+}
+
+double rhodium_radio_control_impl::get_rx_lo_gain(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_gain(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_gain");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error("LO gain must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _lo_tx_gain : 0.0;
+}
+
+/******************************************************************************
+ * Output Power Control
+ *****************************************************************************/
+
+double rhodium_radio_control_impl::_set_lo1_power(
+ const double power, const direction_t dir)
+{
+ auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
+ auto coerced_power = static_cast<uint32_t>(_get_lo_power_range().clip(power, true));
+
+ _lo_ctrl->set_output_power(lmx2592_iface::RF_OUTPUT_A, coerced_power);
+ return coerced_power;
+}
+
+double rhodium_radio_control_impl::set_tx_lo_power(
+ const double power, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_tx_lo_power(power=" << power << ", name=" << name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_tx_lo_power");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO output power must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO does not have configurable output power");
+ return 0.0;
+ }
+
+ _lo_tx_power = _set_lo1_power(power, TX_DIRECTION);
+ return _lo_tx_power;
+}
+
+double rhodium_radio_control_impl::set_rx_lo_power(
+ const double power, const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("set_rx_lo_power(power=" << power << ", name=" << name
+ << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "set_rx_lo_power");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO output power must be set for each stage individually");
+ }
+ if (name == RHODIUM_LO2) {
+ RFNOC_LOG_WARNING("The Lowband LO does not have configurable output power");
+ return 0.0;
+ }
+
+ _lo_rx_power = _set_lo1_power(power, RX_DIRECTION);
+ return _lo_rx_power;
+}
+
+double rhodium_radio_control_impl::get_tx_lo_power(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_tx_lo_power(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_tx_lo_power");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO output power must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _lo_tx_power : 0.0;
+}
+
+double rhodium_radio_control_impl::get_rx_lo_power(
+ const std::string& name, const size_t chan)
+{
+ RFNOC_LOG_TRACE("get_rx_lo_power(name=" << name << ", chan=" << chan << ")");
+ UHD_ASSERT_THROW(chan == 0);
+ _validate_lo_name(name, "get_rx_lo_power");
+
+ if (name == ALL_LOS) {
+ throw uhd::runtime_error(
+ "LO output power must be retrieved for each stage individually");
+ }
+
+ return (name == RHODIUM_LO1) ? _lo_rx_power : 0.0;
+}
+
+/******************************************************************************
+ * Helper Functions
+ *****************************************************************************/
+
+double rhodium_radio_control_impl::_get_lowband_lo_freq() const
+{
+ return MCR_TO_LOWBAND_IF.at(_master_clock_rate);
+}
+
+uhd::gain_range_t rhodium_radio_control_impl::_get_lo_gain_range()
+{
+ return gain_range_t(LO_MIN_GAIN, LO_MAX_GAIN, LO_GAIN_STEP);
+}
+
+uhd::gain_range_t rhodium_radio_control_impl::_get_lo_power_range()
+{
+ return gain_range_t(LO_MIN_POWER, LO_MAX_POWER, LO_POWER_STEP);
+}
+
+int rhodium_radio_control_impl::_get_lo_dsa_setting(
+ const double freq, const direction_t dir)
+{
+ int index = 0;
+ while (freq > FREQ_THRESHOLDS[index + 1]) {
+ index++;
+ }
+
+ const double freq_low = FREQ_THRESHOLDS[index];
+ const double freq_high = FREQ_THRESHOLDS[index + 1];
+
+ const auto& gain_table = (dir == RX_DIRECTION) ? DSA_RX_GAIN_VALUES
+ : DSA_TX_GAIN_VALUES;
+ const double gain_low = gain_table[index];
+ const double gain_high = gain_table[index + 1];
+
+
+ const double slope = (gain_high - gain_low) / (freq_high - freq_low);
+ const double gain_at_freq = gain_low + (freq - freq_low) * slope;
+
+ RFNOC_LOG_TRACE("Interpolated DSA Gain is " << gain_at_freq);
+ return static_cast<int>(std::round(gain_at_freq));
+}
+
+unsigned int rhodium_radio_control_impl::_get_lo_power_setting(double freq)
+{
+ int index = 0;
+ while (freq > FREQ_THRESHOLDS[index + 1]) {
+ index++;
+ }
+
+ const double freq_low = FREQ_THRESHOLDS[index];
+ const double freq_high = FREQ_THRESHOLDS[index + 1];
+ const double power_low = LMX_GAIN_VALUES[index];
+ const double power_high = LMX_GAIN_VALUES[index + 1];
+ const double slope = (power_high - power_low) / (freq_high - freq_low);
+ const double power_at_freq = power_low + (freq - freq_low) * slope;
+
+ RFNOC_LOG_TRACE("Interpolated LMX Power is " << power_at_freq);
+ return uhd::narrow_cast<unsigned int>(std::lround(power_at_freq));
+}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_cpld.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_cpld.cpp
deleted file mode 100644
index 846a4eac6..000000000
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_cpld.cpp
+++ /dev/null
@@ -1,298 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "rhodium_radio_ctrl_impl.hpp"
-#include "rhodium_cpld_ctrl.hpp"
-#include "rhodium_constants.hpp"
-#include <uhd/utils/log.hpp>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-
-namespace {
- const char* rx_band_to_log(rhodium_radio_ctrl_impl::rx_band rx_band)
- {
- switch (rx_band)
- {
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_0:
- return "0";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_1:
- return "1";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_2:
- return "2";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_3:
- return "3";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_4:
- return "4";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_5:
- return "5";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_6:
- return "6";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_7:
- return "7";
- case rhodium_radio_ctrl_impl::rx_band::RX_BAND_INVALID:
- return "INVALID";
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- }
-
- const char* tx_band_to_log(rhodium_radio_ctrl_impl::tx_band tx_band)
- {
- switch (tx_band)
- {
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_0:
- return "0";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_1:
- return "1";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_2:
- return "2";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_3:
- return "3";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_4:
- return "4";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_5:
- return "5";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_6:
- return "6";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_7:
- return "7";
- case rhodium_radio_ctrl_impl::tx_band::TX_BAND_INVALID:
- return "INVALID";
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
- }
-}
-
-void rhodium_radio_ctrl_impl::_update_rx_freq_switches(
- const double freq
-) {
- UHD_LOG_TRACE(unique_id(),
- "Update all RX freq related switches. f=" << freq << " Hz, "
- );
- const auto band = _map_freq_to_rx_band(freq);
- UHD_LOG_TRACE(unique_id(),
- "Selected band " << rx_band_to_log(band));
-
- // select values for lowband/highband switches
- const bool is_lowband = (band == rx_band::RX_BAND_0);
- auto rx_sw2_sw7 = is_lowband ?
- rhodium_cpld_ctrl::RX_SW2_SW7_LOWBANDFILTERBANK :
- rhodium_cpld_ctrl::RX_SW2_SW7_HIGHBANDFILTERBANK;
- auto rx_hb_lb_sel = is_lowband ?
- rhodium_cpld_ctrl::RX_HB_LB_SEL_LOWBAND :
- rhodium_cpld_ctrl::RX_HB_LB_SEL_HIGHBAND;
-
- // select values for filter bank switches
- rhodium_cpld_ctrl::rx_sw3_t rx_sw3;
- rhodium_cpld_ctrl::rx_sw4_sw5_t rx_sw4_sw5;
- rhodium_cpld_ctrl::rx_sw6_t rx_sw6;
- switch (band)
- {
- case rx_band::RX_BAND_0:
- // Low band doesn't use the filter banks, use configuration for band 1
- case rx_band::RX_BAND_1:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
- break;
- case rx_band::RX_BAND_2:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0760X1100MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
- break;
- case rx_band::RX_BAND_3:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER1100X1410MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
- break;
- case rx_band::RX_BAND_4:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOSWITCH4;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER1410X2050MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMSWITCH5;
- break;
- case rx_band::RX_BAND_5:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER2050X3000MHZ;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER2050X3000MHZ;
- break;
- case rx_band::RX_BAND_6:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER3000X4500MHZ;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER3000X4500MHZ;
- break;
- case rx_band::RX_BAND_7:
- rx_sw3 = rhodium_cpld_ctrl::RX_SW3_TOFILTER4500X6000MHZ;
- rx_sw4_sw5 = rhodium_cpld_ctrl::RX_SW4_SW5_FILTER0450X0760MHZ;
- rx_sw6 = rhodium_cpld_ctrl::RX_SW6_FROMFILTER4500X6000MHZ;
- break;
- case rx_band::RX_BAND_INVALID:
- throw uhd::runtime_error(str(boost::format(
- "Cannot map RX frequency to band: %f") % freq));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
-
- // commit settings to cpld
- _cpld->set_rx_switches(
- rx_sw2_sw7,
- rx_sw3,
- rx_sw4_sw5,
- rx_sw6,
- rx_hb_lb_sel
- );
-}
-
-void rhodium_radio_ctrl_impl::_update_tx_freq_switches(
- const double freq
-){
- UHD_LOG_TRACE(unique_id(),
- "Update all TX freq related switches. f=" << freq << " Hz, "
- );
-
- const auto band = _map_freq_to_tx_band(freq);
-
- UHD_LOG_TRACE(unique_id(),
- "Selected band " << tx_band_to_log(band));
-
- // select values for lowband/highband switches
- const bool is_lowband = (band == tx_band::TX_BAND_0);
- auto tx_hb_lb_sel = is_lowband ?
- rhodium_cpld_ctrl::TX_HB_LB_SEL_LOWBAND :
- rhodium_cpld_ctrl::TX_HB_LB_SEL_HIGHBAND;
-
- // select values for filter bank switches
- rhodium_cpld_ctrl::tx_sw2_t tx_sw2;
- rhodium_cpld_ctrl::tx_sw3_sw4_t tx_sw3_sw4;
- rhodium_cpld_ctrl::tx_sw5_t tx_sw5;
- switch (band)
- {
- case tx_band::TX_BAND_0:
- // Low band doesn't use the filter banks, use configuration for band 1
- case tx_band::TX_BAND_1:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
- break;
- case tx_band::TX_BAND_2:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1000MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
- break;
- case tx_band::TX_BAND_3:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1350MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
- break;
- case tx_band::TX_BAND_4:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMSWITCH3;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP1900MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOSWITCH4;
- break;
- case tx_band::TX_BAND_5:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP3000MHZ;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP3000MHZ;
- break;
- case tx_band::TX_BAND_6:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP4100MHZ;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP4100MHZ;
- break;
- case tx_band::TX_BAND_7:
- tx_sw2 = rhodium_cpld_ctrl::TX_SW2_FROMTXFILTERLP6000MHZ;
- tx_sw3_sw4 = rhodium_cpld_ctrl::TX_SW3_SW4_FROMTXFILTERLP0650MHZ;
- tx_sw5 = rhodium_cpld_ctrl::TX_SW5_TOTXFILTERLP6000MHZ;
- break;
- case tx_band::TX_BAND_INVALID:
- throw uhd::runtime_error(str(boost::format(
- "Cannot map TX frequency to band: %f") % freq));
- default:
- UHD_THROW_INVALID_CODE_PATH();
- }
-
- // commit settings to cpld
- _cpld->set_tx_switches(
- tx_sw2,
- tx_sw3_sw4,
- tx_sw5,
- tx_hb_lb_sel
- );
-}
-
-void rhodium_radio_ctrl_impl::_update_rx_input_switches(
- const std::string &input
-) {
- UHD_LOG_TRACE(unique_id(),
- "Update all RX input related switches. input=" << input
- );
- const rhodium_cpld_ctrl::cal_iso_sw_t cal_iso = (input == "CAL") ?
- rhodium_cpld_ctrl::CAL_ISO_CALLOOPBACK :
- rhodium_cpld_ctrl::CAL_ISO_ISOLATION;
- const rhodium_cpld_ctrl::rx_sw1_t sw1 = [input]{
- if (input == "TX/RX")
- {
- return rhodium_cpld_ctrl::RX_SW1_FROMTXRXINPUT;
- }
- else if (input == "RX2") {
- return rhodium_cpld_ctrl::RX_SW1_FROMRX2INPUT;
- }
- else if (input == "CAL") {
- return rhodium_cpld_ctrl::RX_SW1_FROMCALLOOPBACK;
- }
- else if (input == "TERM") {
- return rhodium_cpld_ctrl::RX_SW1_ISOLATION;
- }
- else {
- throw uhd::runtime_error("Invalid antenna in _update_rx_input_switches: " + input);
- }
- }();
-
- UHD_LOG_TRACE(unique_id(),
- "Selected switch values:"
- " sw1=" << sw1 <<
- " cal_iso=" << cal_iso
- );
- _cpld->set_rx_input_switches(sw1, cal_iso);
-}
-
-void rhodium_radio_ctrl_impl::_update_tx_output_switches(
- const std::string &output
-) {
- UHD_LOG_TRACE(unique_id(),
- "Update all TX output related switches. output=" << output
- );
- rhodium_cpld_ctrl::tx_sw1_t sw1;
-
- if (output == "TX/RX")
- {
- //SW1 needs to select low/high band
- if (_is_tx_lowband(get_tx_frequency(0)))
- {
- sw1 = rhodium_cpld_ctrl::TX_SW1_TOLOWBAND;
- }
- else {
- sw1 = rhodium_cpld_ctrl::TX_SW1_TOSWITCH2;
- }
- }
- else if (output == "CAL") {
- sw1 = rhodium_cpld_ctrl::TX_SW1_TOCALLOOPBACK;
- }
- else if (output == "TERM") {
- sw1 = rhodium_cpld_ctrl::TX_SW1_ISOLATION;
- }
- else {
- throw uhd::runtime_error("Invalid antenna in _update_tx_output_switches: " + output);
- }
-
- UHD_LOG_TRACE(unique_id(),
- "Selected switch values: sw1=" << sw1
- );
-
- _cpld->set_tx_output_switches(sw1);
-}
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.cpp
deleted file mode 100644
index d6dbbc594..000000000
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_impl.cpp
+++ /dev/null
@@ -1,677 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "rhodium_radio_ctrl_impl.hpp"
-#include "rhodium_constants.hpp"
-#include <uhdlib/utils/narrow.hpp>
-#include <uhdlib/usrp/common/apply_corrections.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhd/rfnoc/node_ctrl_base.hpp>
-#include <uhd/transport/chdr.hpp>
-#include <uhd/utils/algorithm.hpp>
-#include <uhd/utils/math.hpp>
-#include <uhd/types/direction.hpp>
-#include <uhd/types/eeprom.hpp>
-#include <uhd/exception.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/make_shared.hpp>
-#include <boost/format.hpp>
-#include <sstream>
-#include <cmath>
-#include <cstdlib>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-using namespace uhd::math::fp_compare;
-
-namespace {
- constexpr char RX_FE_CONNECTION_LOWBAND[] = "QI";
- constexpr char RX_FE_CONNECTION_HIGHBAND[] = "IQ";
- constexpr char TX_FE_CONNECTION_LOWBAND[] = "QI";
- constexpr char TX_FE_CONNECTION_HIGHBAND[] = "IQ";
-
- constexpr double DEFAULT_IDENTIFY_DURATION = 5.0; // seconds
-
- constexpr uint64_t SET_RATE_RPC_TIMEOUT_MS = 10000;
-
- const fs_path TX_FE_PATH = fs_path("tx_frontends") / 0 / "tune_args";
- const fs_path RX_FE_PATH = fs_path("rx_frontends") / 0 / "tune_args";
-
- device_addr_t _get_tune_args(uhd::property_tree::sptr tree, std::string _radio_slot, uhd::direction_t dir)
- {
- const auto subtree = tree->subtree(fs_path("dboards") / _radio_slot);
- const auto tune_arg_path = (dir == RX_DIRECTION) ? RX_FE_PATH : TX_FE_PATH;
- return subtree->access<device_addr_t>(tune_arg_path).get();
- }
-}
-
-
-/******************************************************************************
- * Structors
- *****************************************************************************/
-UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(rhodium_radio_ctrl)
-{
- UHD_LOG_TRACE(unique_id(), "Entering rhodium_radio_ctrl_impl ctor...");
- const char radio_slot_name[] = {'A', 'B'};
- _radio_slot = radio_slot_name[get_block_id().get_block_count()];
- _rpc_prefix =
- (_radio_slot == "A") ? "db_0_" : "db_1_";
- UHD_LOG_TRACE(unique_id(), "Radio slot: " << _radio_slot);
-}
-
-rhodium_radio_ctrl_impl::~rhodium_radio_ctrl_impl()
-{
- UHD_LOG_TRACE(unique_id(), "rhodium_radio_ctrl_impl::dtor() ");
-}
-
-
-/******************************************************************************
- * API Calls
- *****************************************************************************/
-double rhodium_radio_ctrl_impl::set_rate(double requested_rate)
-{
- meta_range_t rates;
- for (const double rate : RHODIUM_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) << " MHz to " <<
- (rate / 1e6) << " MHz, the closest possible rate.");
- }
-
- const double current_rate = get_rate();
- if (math::frequencies_are_equal(current_rate, rate)) {
- UHD_LOG_DEBUG(
- unique_id(), "Rate is already at " << (rate / 1e6) << " MHz. Skipping set_rate()");
- return current_rate;
- }
-
- // The master clock rate is always set by requesting db0's clock rate.
- UHD_LOG_TRACE(unique_id(), "Updating master clock rate to " << rate);
- auto new_rate = _rpcc->request_with_token<double>(
- SET_RATE_RPC_TIMEOUT_MS, "db_0_set_master_clock_rate", rate);
- // The lowband LO frequency will change with the master clock rate, so
- // update the tuning of the device.
- set_tx_frequency(get_tx_frequency(0), 0);
- set_rx_frequency(get_rx_frequency(0), 0);
-
- radio_ctrl_impl::set_rate(new_rate);
- return new_rate;
-}
-
-void rhodium_radio_ctrl_impl::set_tx_antenna(
- const std::string &ant,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_antenna(ant=" << ant << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- if (!uhd::has(RHODIUM_TX_ANTENNAS, ant)) {
- throw uhd::value_error(str(
- boost::format("[%s] Requesting invalid TX antenna value: %s")
- % unique_id()
- % ant
- ));
- }
-
- _update_tx_output_switches(ant);
- // _update_atr will set the cached antenna value, so no need to do
- // it here. See comments in _update_antenna for more info.
- _update_atr(ant, TX_DIRECTION);
-}
-
-void rhodium_radio_ctrl_impl::set_rx_antenna(
- const std::string &ant,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "Setting RX antenna to " << ant);
- UHD_ASSERT_THROW(chan == 0);
-
- if (!uhd::has(RHODIUM_RX_ANTENNAS, ant)) {
- throw uhd::value_error(str(
- boost::format("[%s] Requesting invalid RX antenna value: %s")
- % unique_id()
- % ant
- ));
- }
-
- _update_rx_input_switches(ant);
- // _update_atr will set the cached antenna value, so no need to do
- // it here. See comments in _update_antenna for more info.
- _update_atr(ant, RX_DIRECTION);
-}
-
-void rhodium_radio_ctrl_impl::_set_tx_fe_connection(const std::string &conn)
-{
- UHD_LOG_TRACE(unique_id(), "set_tx_fe_connection(conn=" << conn << ")");
-
- if (conn != _tx_fe_connection)
- {
- _tx_fe_core->set_mux(conn);
- _tx_fe_connection = conn;
- }
-}
-
-void rhodium_radio_ctrl_impl::_set_rx_fe_connection(const std::string &conn)
-{
- UHD_LOG_TRACE(unique_id(), "set_rx_fe_connection(conn=" << conn << ")");
-
- if (conn != _rx_fe_connection)
- {
- _rx_fe_core->set_fe_connection(conn);
- _rx_fe_connection = conn;
- }
-}
-
-std::string rhodium_radio_ctrl_impl::_get_tx_fe_connection() const
-{
- UHD_LOG_TRACE(unique_id(), "get_tx_fe_connection()");
-
- return _tx_fe_connection;
-}
-
-std::string rhodium_radio_ctrl_impl::_get_rx_fe_connection() const
-{
- UHD_LOG_TRACE(unique_id(), "get_rx_fe_connection()");
-
- return _rx_fe_connection;
-}
-
-double rhodium_radio_ctrl_impl::set_tx_frequency(
- const double freq,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_frequency(f=" << freq << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- const auto old_freq = get_tx_frequency(0);
- double coerced_target_freq = uhd::clip(freq, RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ);
-
- if (freq != coerced_target_freq) {
- UHD_LOG_DEBUG(unique_id(), "Requested frequency is outside supported range. Coercing to " << coerced_target_freq);
- }
-
- const bool is_highband = !_is_tx_lowband(coerced_target_freq);
-
- const double target_lo_freq = is_highband ?
- coerced_target_freq : _get_lowband_lo_freq() - coerced_target_freq;
- const double actual_lo_freq =
- set_tx_lo_freq(target_lo_freq, RHODIUM_LO1, chan);
- const double coerced_freq = is_highband ?
- actual_lo_freq : _get_lowband_lo_freq() - actual_lo_freq;
- const auto conn = is_highband ?
- TX_FE_CONNECTION_HIGHBAND : TX_FE_CONNECTION_LOWBAND;
-
- // update the cached frequency value now so calls to set gain and update
- // switches will read the new frequency
- radio_ctrl_impl::set_tx_frequency(coerced_freq, chan);
-
- _set_tx_fe_connection(conn);
- set_tx_gain(get_tx_gain(chan), 0);
-
- if (_get_highband_spur_reduction_enabled(TX_DIRECTION)) {
- if (_get_timed_command_enabled() and _is_tx_lowband(old_freq) != not is_highband) {
- UHD_LOG_WARNING(unique_id(),
- "Timed tuning commands that transition between lowband and highband, 450 "
- "MHz, do not function correctly when highband_spur_reduction is enabled! "
- "Disable highband_spur_reduction or avoid using timed tuning commands.");
- }
- UHD_LOG_TRACE(
- unique_id(), "TX Lowband LO is " << (is_highband ? "disabled" : "enabled"));
- _rpcc->notify_with_token(_rpc_prefix + "enable_tx_lowband_lo", (!is_highband));
- }
- _update_tx_freq_switches(coerced_freq);
- const bool enable_corrections = is_highband
- and (get_tx_lo_source(RHODIUM_LO1, 0) == "internal");
- _update_corrections(actual_lo_freq, TX_DIRECTION, enable_corrections);
- // if TX lowband/highband changed and antenna is TX/RX,
- // the ATR and SW1 need to be updated
- _update_tx_output_switches(get_tx_antenna(0));
- _update_atr(get_tx_antenna(0), TX_DIRECTION);
-
- return coerced_freq;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_frequency(
- const double freq,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_frequency(f=" << freq << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- const auto old_freq = get_rx_frequency(0);
- double coerced_target_freq = uhd::clip(freq, RHODIUM_MIN_FREQ, RHODIUM_MAX_FREQ);
-
- if (freq != coerced_target_freq) {
- UHD_LOG_DEBUG(unique_id(), "Requested frequency is outside supported range. Coercing to " << coerced_target_freq);
- }
-
- const bool is_highband = !_is_rx_lowband(coerced_target_freq);
-
- const double target_lo_freq = is_highband ?
- coerced_target_freq : _get_lowband_lo_freq() - coerced_target_freq;
- const double actual_lo_freq =
- set_rx_lo_freq(target_lo_freq, RHODIUM_LO1, chan);
- const double coerced_freq = is_highband ?
- actual_lo_freq : _get_lowband_lo_freq() - actual_lo_freq;
- const auto conn = is_highband ?
- RX_FE_CONNECTION_HIGHBAND : RX_FE_CONNECTION_LOWBAND;
-
- // update the cached frequency value now so calls to set gain and update
- // switches will read the new frequency
- radio_ctrl_impl::set_rx_frequency(coerced_freq, chan);
-
- _set_rx_fe_connection(conn);
- set_rx_gain(get_rx_gain(chan), 0);
-
- if (_get_highband_spur_reduction_enabled(RX_DIRECTION)) {
- if (_get_timed_command_enabled() and _is_rx_lowband(old_freq) != not is_highband) {
- UHD_LOG_WARNING(unique_id(),
- "Timed tuning commands that transition between lowband and highband, 450 "
- "MHz, do not function correctly when highband_spur_reduction is enabled! "
- "Disable highband_spur_reduction or avoid using timed tuning commands.");
- }
- UHD_LOG_TRACE(
- unique_id(), "RX Lowband LO is " << (is_highband ? "disabled" : "enabled"));
- _rpcc->notify_with_token(_rpc_prefix + "enable_rx_lowband_lo", (!is_highband));
- }
- _update_rx_freq_switches(coerced_freq);
- const bool enable_corrections = is_highband
- and (get_rx_lo_source(RHODIUM_LO1, 0) == "internal");
- _update_corrections(actual_lo_freq, RX_DIRECTION, enable_corrections);
-
- return coerced_freq;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_bandwidth(
- const double bandwidth,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_bandwidth(bandwidth=" << bandwidth << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- return get_rx_bandwidth(chan);
-}
-
-double rhodium_radio_ctrl_impl::set_tx_bandwidth(
- const double bandwidth,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_bandwidth(bandwidth=" << bandwidth << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- return get_tx_bandwidth(chan);
-}
-
-double rhodium_radio_ctrl_impl::set_tx_gain(
- const double gain,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_gain(gain=" << gain << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- auto freq = this->get_tx_frequency(chan);
- auto index = _get_gain_range(TX_DIRECTION).clip(gain);
-
- auto old_band = _is_tx_lowband(_tx_frequency_at_last_gain_write) ?
- rhodium_cpld_ctrl::gain_band_t::LOW :
- rhodium_cpld_ctrl::gain_band_t::HIGH;
- auto new_band = _is_tx_lowband(freq) ?
- rhodium_cpld_ctrl::gain_band_t::LOW :
- rhodium_cpld_ctrl::gain_band_t::HIGH;
-
- // The CPLD requires a rewrite of the gain control command on a change of lowband or highband
- if (get_tx_gain(chan) != index or old_band != new_band) {
- UHD_LOG_TRACE(unique_id(), "Writing new TX gain index: " << index);
- _cpld->set_gain_index(index, new_band, TX_DIRECTION);
- _tx_frequency_at_last_gain_write = freq;
- radio_ctrl_impl::set_tx_gain(index, chan);
- } else {
- UHD_LOG_TRACE(unique_id(), "No change in index or band, skipped writing TX gain index: " << index);
- }
-
- return index;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_gain(
- const double gain,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_gain(gain=" << gain << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- auto freq = this->get_rx_frequency(chan);
- auto index = _get_gain_range(RX_DIRECTION).clip(gain);
-
- auto old_band = _is_rx_lowband(_rx_frequency_at_last_gain_write) ?
- rhodium_cpld_ctrl::gain_band_t::LOW :
- rhodium_cpld_ctrl::gain_band_t::HIGH;
- auto new_band = _is_rx_lowband(freq) ?
- rhodium_cpld_ctrl::gain_band_t::LOW :
- rhodium_cpld_ctrl::gain_band_t::HIGH;
-
- // The CPLD requires a rewrite of the gain control command on a change of lowband or highband
- if (get_rx_gain(chan) != index or old_band != new_band) {
- UHD_LOG_TRACE(unique_id(), "Writing new RX gain index: " << index);
- _cpld->set_gain_index(index, new_band, RX_DIRECTION);
- _rx_frequency_at_last_gain_write = freq;
- radio_ctrl_impl::set_rx_gain(index, chan);
- } else {
- UHD_LOG_TRACE(unique_id(), "No change in index or band, skipped writing RX gain index: " << index);
- }
-
- return index;
-}
-
-void rhodium_radio_ctrl_impl::_identify_with_leds(
- double identify_duration
-) {
- auto duration_ms = static_cast<uint64_t>(identify_duration * 1000);
- auto end_time =
- std::chrono::steady_clock::now() + std::chrono::milliseconds(duration_ms);
- bool led_state = true;
- {
- std::lock_guard<std::mutex> lock(_ant_mutex);
- while (std::chrono::steady_clock::now() < end_time) {
- auto atr = led_state ? (LED_RX | LED_RX2 | LED_TX) : 0;
- _gpio->set_atr_reg(gpio_atr::ATR_REG_IDLE, atr, RHODIUM_GPIO_MASK);
- led_state = !led_state;
- std::this_thread::sleep_for(std::chrono::milliseconds(500));
- }
- }
- _update_atr(get_tx_antenna(0), TX_DIRECTION);
- _update_atr(get_rx_antenna(0), RX_DIRECTION);
-}
-
-void rhodium_radio_ctrl_impl::_update_atr(
- const std::string& ant,
- const direction_t dir
-) {
- // This function updates sw10 based on the value of both antennas, so we
- // use a mutex to prevent other calls in this class instance from running
- // at the same time.
- std::lock_guard<std::mutex> lock(_ant_mutex);
-
- UHD_LOG_TRACE(unique_id(),
- "Updating ATRs for " << ((dir == RX_DIRECTION) ? "RX" : "TX") << " to " << ant);
-
- const auto rx_ant = (dir == RX_DIRECTION) ? ant : get_rx_antenna(0);
- const auto tx_ant = (dir == TX_DIRECTION) ? ant : get_tx_antenna(0);
- const auto sw10_tx = _is_tx_lowband(get_tx_frequency(0)) ?
- SW10_FROMTXLOWBAND : SW10_FROMTXHIGHBAND;
-
-
- const uint32_t atr_idle = SW10_ISOLATION;
-
- const uint32_t atr_rx = [rx_ant]{
- if (rx_ant == "TX/RX") {
- return SW10_TORX | LED_RX;
- } else if (rx_ant == "RX2") {
- return SW10_ISOLATION | LED_RX2;
- } else {
- return SW10_ISOLATION;
- }
- }();
-
- const uint32_t atr_tx = (tx_ant == "TX/RX") ?
- (sw10_tx | LED_TX) : SW10_ISOLATION;
-
- const uint32_t atr_dx = [tx_ant, rx_ant, sw10_tx] {
- uint32_t sw10_return;
- if (tx_ant == "TX/RX") {
- // if both channels are set to TX/RX, TX will override
- sw10_return = sw10_tx | LED_TX;
- } else if (rx_ant == "TX/RX") {
- sw10_return = SW10_TORX | LED_RX;
- } else {
- sw10_return = SW10_ISOLATION;
- }
- sw10_return |= (rx_ant == "RX2") ? LED_RX2 : 0;
- return sw10_return;
- }();
-
- _gpio->set_atr_reg(gpio_atr::ATR_REG_IDLE, atr_idle, RHODIUM_GPIO_MASK);
- _gpio->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, atr_rx, RHODIUM_GPIO_MASK);
- _gpio->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, atr_tx, RHODIUM_GPIO_MASK);
- _gpio->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, atr_dx, RHODIUM_GPIO_MASK);
-
- UHD_LOG_TRACE(unique_id(),
- str(boost::format("Wrote ATR registers i:0x%02X, r:0x%02X, t:0x%02X, d:0x%02X")
- % atr_idle % atr_rx % atr_tx % atr_dx));
-
- if (dir == RX_DIRECTION) {
- radio_ctrl_impl::set_rx_antenna(ant, 0);
- } else {
- radio_ctrl_impl::set_tx_antenna(ant, 0);
- }
-}
-
-void rhodium_radio_ctrl_impl::_update_corrections(
- const double freq,
- const direction_t dir,
- const bool enable)
-{
- const std::string fe_path_part = dir == RX_DIRECTION ? "rx_fe_corrections"
- : "tx_fe_corrections";
- const fs_path fe_corr_path = _root_path / fe_path_part / 0;
- const fs_path dboard_path = fs_path("dboards") / _radio_slot;
-
- if (enable)
- {
- UHD_LOG_DEBUG(unique_id(),
- "Loading any available frontend corrections for "
- << ((dir == RX_DIRECTION) ? "RX" : "TX") << " at " << freq);
- if (dir == RX_DIRECTION) {
- apply_rx_fe_corrections(_tree, dboard_path, fe_corr_path, freq);
- } else {
- apply_tx_fe_corrections(_tree, dboard_path, fe_corr_path, freq);
- }
- } else {
- UHD_LOG_DEBUG(unique_id(),
- "Disabling frontend corrections for "
- << ((dir == RX_DIRECTION) ? "RX" : "TX"));
- if (dir == RX_DIRECTION) {
- _rx_fe_core->set_iq_balance(rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE);
- } else {
- _tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
- _tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
- }
- }
-
-}
-
-uhd::gain_range_t rhodium_radio_ctrl_impl::_get_gain_range(direction_t dir)
-{
- if (dir == RX_DIRECTION) {
- return gain_range_t(RX_MIN_GAIN, RX_MAX_GAIN, RX_GAIN_STEP);
- } else if (dir == TX_DIRECTION) {
- return gain_range_t(TX_MIN_GAIN, TX_MAX_GAIN, TX_GAIN_STEP);
- } else {
- UHD_THROW_INVALID_CODE_PATH();
- }
-}
-
-bool rhodium_radio_ctrl_impl::_get_spur_dodging_enabled(uhd::direction_t dir) const
-{
- UHD_ASSERT_THROW(_tree->exists(get_arg_path(SPUR_DODGING_ARG_NAME) / "value"));
- auto block_value = _tree->access<std::string>(get_arg_path(SPUR_DODGING_ARG_NAME) / "value").get();
- auto dict = _get_tune_args(_tree, _radio_slot, dir);
-
- // get the current tune_arg for spur_dodging
- // if the tune_arg doesn't exist, use the radio block argument instead
- std::string spur_dodging_arg = dict.cast<std::string>(
- SPUR_DODGING_ARG_NAME,
- block_value);
-
- if (spur_dodging_arg == "enabled")
- {
- UHD_LOG_TRACE(unique_id(), "_get_spur_dodging_enabled returning enabled");
- return true;
- }
- else if (spur_dodging_arg == "disabled") {
- UHD_LOG_TRACE(unique_id(), "_get_spur_dodging_enabled returning disabled");
- return false;
- }
- else {
- throw uhd::value_error(
- str(boost::format("Invalid spur_dodging argument: %s Valid options are [enabled, disabled]")
- % spur_dodging_arg));
- }
-}
-
-double rhodium_radio_ctrl_impl::_get_spur_dodging_threshold(uhd::direction_t dir) const
-{
- UHD_ASSERT_THROW(_tree->exists(get_arg_path(SPUR_DODGING_THRESHOLD_ARG_NAME) / "value"));
- auto block_value = _tree->access<double>(get_arg_path(SPUR_DODGING_THRESHOLD_ARG_NAME) / "value").get();
- auto dict = _get_tune_args(_tree, _radio_slot, dir);
-
- // get the current tune_arg for spur_dodging_threshold
- // if the tune_arg doesn't exist, use the radio block argument instead
- auto threshold = dict.cast<double>(SPUR_DODGING_THRESHOLD_ARG_NAME, block_value);
-
- UHD_LOG_TRACE(unique_id(), "_get_spur_dodging_threshold returning " << threshold);
-
- return threshold;
-}
-
-bool rhodium_radio_ctrl_impl::_get_highband_spur_reduction_enabled(uhd::direction_t dir) const
-{
- UHD_ASSERT_THROW(
- _tree->exists(get_arg_path(HIGHBAND_SPUR_REDUCTION_ARG_NAME) / "value"));
- auto block_value = _tree
- ->access<std::string>(
- get_arg_path(HIGHBAND_SPUR_REDUCTION_ARG_NAME) / "value")
- .get();
- auto dict = _get_tune_args(_tree, _radio_slot, dir);
-
- // get the current tune_arg for highband_spur_reduction
- // if the tune_arg doesn't exist, use the radio block argument instead
- std::string highband_spur_reduction_arg =
- dict.cast<std::string>(HIGHBAND_SPUR_REDUCTION_ARG_NAME, block_value);
-
- if (highband_spur_reduction_arg == "enabled") {
- UHD_LOG_TRACE(unique_id(), __func__ << " returning enabled");
- return true;
- } else if (highband_spur_reduction_arg == "disabled") {
- UHD_LOG_TRACE(unique_id(), __func__ << " returning disabled");
- return false;
- } else {
- throw uhd::value_error(
- str(boost::format("Invalid highband_spur_reduction argument: %s Valid "
- "options are [enabled, disabled]")
- % highband_spur_reduction_arg));
- }
-}
-
-bool rhodium_radio_ctrl_impl::_get_timed_command_enabled() const
-{
- auto& prop = _tree->access<time_spec_t>(fs_path("time") / "cmd");
- // if timed commands are never set, the property will be empty
- // if timed commands were set but cleared, time_spec will be set to 0.0
- return !prop.empty() and prop.get() != time_spec_t(0.0);
-}
-
-size_t rhodium_radio_ctrl_impl::get_chan_from_dboard_fe(
- const std::string &fe, const direction_t /* dir */
-) {
- UHD_ASSERT_THROW(boost::lexical_cast<size_t>(fe) == 0);
- return 0;
-}
-
-std::string rhodium_radio_ctrl_impl::get_dboard_fe_from_chan(
- const size_t chan,
- const direction_t /* dir */
-) {
- UHD_ASSERT_THROW(chan == 0);
- return "0";
-}
-
-void rhodium_radio_ctrl_impl::set_rpc_client(
- uhd::rpc_client::sptr rpcc,
- const uhd::device_addr_t &block_args
-) {
- _rpcc = rpcc;
- _block_args = block_args;
-
- // Get and verify the MCR before _init_peripherals, which will use this value
- // 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);
-
- UHD_LOG_TRACE(unique_id(), "Checking for existence of Rhodium DB in slot " << _radio_slot);
- const auto all_dboard_info = _rpcc->request<std::vector<std::map<std::string, std::string>>>("get_dboard_info");
-
- // There is a bug that if only one DB is plugged into slot B the vector
- // will only have 1 element but not be correlated to slot B at all.
- // For now, we assume a 1 element array means the DB is in slot A.
- if (all_dboard_info.size() <= get_block_id().get_block_count())
- {
- UHD_LOG_DEBUG(unique_id(), "No DB detected in slot " << _radio_slot);
- // Name and master clock rate are needed for RFNoC init, so set the
- // name now and let this function continue to set the MCR
- _tree->subtree(fs_path("dboards") / _radio_slot / "tx_frontends" / "0")
- ->create<std::string>("name").set("Unknown");
- _tree->subtree(fs_path("dboards") / _radio_slot / "rx_frontends" / "0")
- ->create<std::string>("name").set("Unknown");
- }
- else {
- _dboard_info = all_dboard_info.at(get_block_id().get_block_count());
- UHD_LOG_DEBUG(unique_id(),
- "Rhodium DB detected in slot " << _radio_slot <<
- ". Serial: " << _dboard_info.at("serial"));
- _init_defaults();
- _init_peripherals();
- _init_prop_tree();
-
- if (block_args.has_key("identify")) {
- const std::string identify_val = block_args.get("identify");
- double identify_duration = 0.0;
- try {
- identify_duration = std::stod(identify_val);
- if (!std::isnormal(identify_duration)) {
- identify_duration = DEFAULT_IDENTIFY_DURATION;
- }
- } catch (std::invalid_argument) {
- identify_duration = DEFAULT_IDENTIFY_DURATION;
- }
-
- UHD_LOG_INFO(unique_id(),
- "Running LED identification process for " << identify_duration
- << " seconds.");
- _identify_with_leds(identify_duration);
- }
- }
-}
-
-bool rhodium_radio_ctrl_impl::get_lo_lock_status(
- const direction_t dir
-) const
-{
- return
- ((dir == RX_DIRECTION) or _tx_lo->get_lock_status()) and
- ((dir == TX_DIRECTION) or _rx_lo->get_lock_status());
-}
-
-UHD_RFNOC_BLOCK_REGISTER(rhodium_radio_ctrl, "RhodiumRadio");
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp
deleted file mode 100644
index 356932bc2..000000000
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_init.cpp
+++ /dev/null
@@ -1,843 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "rhodium_radio_ctrl_impl.hpp"
-#include "rhodium_constants.hpp"
-#include <uhdlib/usrp/cores/spi_core_3000.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhd/utils/algorithm.hpp>
-#include <uhd/types/eeprom.hpp>
-#include <uhd/types/sensors.hpp>
-#include <uhd/transport/chdr.hpp>
-#include <vector>
-#include <string>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-
-namespace {
- enum slave_select_t {
- SEN_CPLD = 8,
- SEN_TX_LO = 1,
- SEN_RX_LO = 2,
- SEN_LO_DIST = 4 /* Unused */
- };
-
- constexpr uint32_t TX_FE_BASE = 224;
- constexpr uint32_t RX_FE_BASE = 232;
-
- constexpr double RHODIUM_DEFAULT_FREQ = 2.5e9; // Hz
- // An invalid default index ensures that set gain will apply settings
- // the first time it is called
- constexpr double RHODIUM_DEFAULT_INVALID_GAIN = -1; // gain index
- constexpr double RHODIUM_DEFAULT_GAIN = 0; // gain index
- constexpr double RHODIUM_DEFAULT_LO_GAIN = 30; // gain index
- constexpr char RHODIUM_DEFAULT_RX_ANTENNA[] = "RX2";
- constexpr char RHODIUM_DEFAULT_TX_ANTENNA[] = "TX/RX";
- constexpr double RHODIUM_DEFAULT_BANDWIDTH = 250e6; // Hz
- constexpr auto RHODIUM_DEFAULT_MASH_ORDER = lmx2592_iface::mash_order_t::THIRD;
-
- //! Rhodium gain profile options
- const std::vector<std::string> RHODIUM_GP_OPTIONS = {
- "default"
- };
-
- //! Returns the SPI config used by the CPLD
- spi_config_t _get_cpld_spi_config() {
- spi_config_t spi_config;
- spi_config.use_custom_divider = true;
- spi_config.divider = 10;
- spi_config.mosi_edge = spi_config_t::EDGE_RISE;
- spi_config.miso_edge = spi_config_t::EDGE_FALL;
-
- return spi_config;
- }
-
- //! Returns the SPI config used by the TX LO
- spi_config_t _get_tx_lo_spi_config() {
- spi_config_t spi_config;
- spi_config.use_custom_divider = true;
- spi_config.divider = 10;
- spi_config.mosi_edge = spi_config_t::EDGE_RISE;
- spi_config.miso_edge = spi_config_t::EDGE_FALL;
-
- return spi_config;
- }
-
- //! Returns the SPI config used by the RX LO
- spi_config_t _get_rx_lo_spi_config() {
- spi_config_t spi_config;
- spi_config.use_custom_divider = true;
- spi_config.divider = 10;
- spi_config.mosi_edge = spi_config_t::EDGE_RISE;
- spi_config.miso_edge = spi_config_t::EDGE_FALL;
-
- return spi_config;
- }
-
- std::function<void(uint32_t)> _generate_write_spi(
- uhd::spi_iface::sptr spi,
- slave_select_t slave,
- spi_config_t config
- ) {
- return [spi, slave, config](const uint32_t transaction) {
- spi->write_spi(slave, config, transaction, 24);
- };
- }
-
- std::function<uint32_t(uint32_t)> _generate_read_spi(
- uhd::spi_iface::sptr spi,
- slave_select_t slave,
- spi_config_t config
- ) {
- return [spi, slave, config](const uint32_t transaction) {
- return spi->read_spi(slave, config, transaction, 24);
- };
- }
-}
-
-void rhodium_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(RHODIUM_DEFAULT_FREQ, chan);
- radio_ctrl_impl::set_rx_gain(RHODIUM_DEFAULT_INVALID_GAIN, chan);
- radio_ctrl_impl::set_rx_antenna(RHODIUM_DEFAULT_RX_ANTENNA, chan);
- radio_ctrl_impl::set_rx_bandwidth(RHODIUM_DEFAULT_BANDWIDTH, chan);
- }
-
- for (size_t chan = 0; chan < num_tx_chans; chan++) {
- radio_ctrl_impl::set_tx_frequency(RHODIUM_DEFAULT_FREQ, chan);
- radio_ctrl_impl::set_tx_gain(RHODIUM_DEFAULT_INVALID_GAIN, chan);
- radio_ctrl_impl::set_tx_antenna(RHODIUM_DEFAULT_TX_ANTENNA, chan);
- radio_ctrl_impl::set_tx_bandwidth(RHODIUM_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);
-
- // Update configurable block arguments from the device arguments provided
- if (_block_args.has_key(SPUR_DODGING_ARG_NAME)) {
- _tree->access<std::string>(get_arg_path(SPUR_DODGING_ARG_NAME) / "value")
- .set(_block_args.get(SPUR_DODGING_ARG_NAME));
- }
-
- if (_block_args.has_key(SPUR_DODGING_THRESHOLD_ARG_NAME)) {
- _tree->access<double>(get_arg_path(SPUR_DODGING_THRESHOLD_ARG_NAME) / "value")
- .set(boost::lexical_cast<double>(_block_args.get(SPUR_DODGING_THRESHOLD_ARG_NAME)));
- }
-
- if (_block_args.has_key(HIGHBAND_SPUR_REDUCTION_ARG_NAME)) {
- _tree
- ->access<std::string>(
- get_arg_path(HIGHBAND_SPUR_REDUCTION_ARG_NAME) / "value")
- .set(_block_args.get(HIGHBAND_SPUR_REDUCTION_ARG_NAME));
- }
-}
-
-void rhodium_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...");
- _cpld = std::make_shared<rhodium_cpld_ctrl>(
- _generate_write_spi(this->_spi, SEN_CPLD, _get_cpld_spi_config()),
- _generate_read_spi(this->_spi, SEN_CPLD, _get_cpld_spi_config()));
-
- UHD_LOG_TRACE(unique_id(), "Initializing TX frontend DSP core...")
- _tx_fe_core = tx_frontend_core_200::make(_get_ctrl(0), regs::sr_addr(TX_FE_BASE));
- _tx_fe_core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
- _tx_fe_core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
- _tx_fe_core->populate_subtree(_tree->subtree(_root_path / "tx_fe_corrections" / 0));
-
- UHD_LOG_TRACE(unique_id(), "Initializing RX frontend DSP core...")
- _rx_fe_core = rx_frontend_core_3000::make(_get_ctrl(0), regs::sr_addr(RX_FE_BASE));
- _rx_fe_core->set_adc_rate(_master_clock_rate);
- _rx_fe_core->set_dc_offset(rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE);
- _rx_fe_core->set_dc_offset_auto(rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE);
- _rx_fe_core->set_iq_balance(rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE);
- _rx_fe_core->populate_subtree(_tree->subtree(_root_path / "rx_fe_corrections" / 0));
-
- UHD_LOG_TRACE(unique_id(), "Writing initial gain values...");
- set_tx_gain(RHODIUM_DEFAULT_GAIN, 0);
- set_tx_lo_gain(RHODIUM_DEFAULT_LO_GAIN, RHODIUM_LO1, 0);
- set_rx_gain(RHODIUM_DEFAULT_GAIN, 0);
- set_rx_lo_gain(RHODIUM_DEFAULT_LO_GAIN, RHODIUM_LO1, 0);
-
- UHD_LOG_TRACE(unique_id(), "Initializing TX LO...");
- _tx_lo = lmx2592_iface::make(
- _generate_write_spi(this->_spi, SEN_TX_LO, _get_tx_lo_spi_config()),
- _generate_read_spi(this->_spi, SEN_TX_LO, _get_tx_lo_spi_config()));
-
- UHD_LOG_TRACE(unique_id(), "Writing initial TX LO state...");
- _tx_lo->set_reference_frequency(RHODIUM_LO1_REF_FREQ);
- _tx_lo->set_mash_order(RHODIUM_DEFAULT_MASH_ORDER);
-
- UHD_LOG_TRACE(unique_id(), "Initializing RX LO...");
- _rx_lo = lmx2592_iface::make(
- _generate_write_spi(this->_spi, SEN_RX_LO, _get_rx_lo_spi_config()),
- _generate_read_spi(this->_spi, SEN_RX_LO, _get_rx_lo_spi_config()));
-
- UHD_LOG_TRACE(unique_id(), "Writing initial RX LO state...");
- _rx_lo->set_reference_frequency(RHODIUM_LO1_REF_FREQ);
- _rx_lo->set_mash_order(RHODIUM_DEFAULT_MASH_ORDER);
-
- UHD_LOG_TRACE(unique_id(), "Initializing GPIOs...");
- _gpio =
- usrp::gpio_atr::gpio_atr_3000::make(
- _get_ctrl(0),
- regs::sr_addr(regs::GPIO),
- regs::rb_addr(regs::RB_DB_GPIO)
- );
- _gpio->set_atr_mode(
- usrp::gpio_atr::MODE_ATR, // Enable ATR mode for Rhodium bits
- RHODIUM_GPIO_MASK
- );
- _gpio->set_atr_mode(
- usrp::gpio_atr::MODE_GPIO, // Disable ATR mode for unused bits
- ~RHODIUM_GPIO_MASK
- );
- _gpio->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(), "Set initial ATR values...");
- _update_atr(RHODIUM_DEFAULT_TX_ANTENNA, TX_DIRECTION);
- _update_atr(RHODIUM_DEFAULT_RX_ANTENNA, RX_DIRECTION);
-
- // Updating the TX frequency path may include an update to SW10, which is
- // GPIO controlled, so this must follow CPLD and GPIO initialization
- UHD_LOG_TRACE(unique_id(), "Writing initial switch values...");
- _update_tx_freq_switches(RHODIUM_DEFAULT_FREQ);
- _update_rx_freq_switches(RHODIUM_DEFAULT_FREQ);
-
- // Antenna setting requires both CPLD and GPIO control
- UHD_LOG_TRACE(unique_id(), "Setting initial antenna settings");
- _update_tx_output_switches(RHODIUM_DEFAULT_TX_ANTENNA);
- _update_rx_input_switches(RHODIUM_DEFAULT_RX_ANTENNA);
-
- UHD_LOG_TRACE(unique_id(), "Checking for existence of LO Distribution board");
- _lo_dist_present = _rpcc->request_with_token<bool>(_rpc_prefix + "is_lo_dist_present");
- UHD_LOG_DEBUG(unique_id(), str(boost::format("LO distribution board is%s present") % (_lo_dist_present ? "" : " NOT")));
-}
-
-void rhodium_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("Rhodium")))
- ;
- subtree->create<std::string>(tx_fe_path / "connection")
- .add_coerced_subscriber([this](const std::string& conn){
- this->_set_tx_fe_connection(conn);
- })
- .set_publisher([this](){
- return this->_get_tx_fe_connection();
- })
- ;
- subtree->create<device_addr_t>(tx_fe_path / "tune_args")
- .set(device_addr_t())
- ;
- // RX Standard attributes
- subtree->create<std::string>(rx_fe_path / "name")
- .set(str(boost::format("Rhodium")))
- ;
- subtree->create<std::string>(rx_fe_path / "connection")
- .add_coerced_subscriber([this](const std::string& conn){
- this->_set_rx_fe_connection(conn);
- })
- .set_publisher([this](){
- return this->_get_rx_fe_connection();
- })
- ;
- subtree->create<device_addr_t>(rx_fe_path / "tune_args")
- .set(device_addr_t())
- ;
- // 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(RHODIUM_TX_ANTENNAS)
- .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(RHODIUM_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(RHODIUM_MIN_FREQ, RHODIUM_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(RHODIUM_MIN_FREQ, RHODIUM_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_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(RHODIUM_DEFAULT_BANDWIDTH, RHODIUM_DEFAULT_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_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(meta_range_t(RHODIUM_DEFAULT_BANDWIDTH, RHODIUM_DEFAULT_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([](){
- return rhodium_radio_ctrl_impl::_get_gain_range(TX_DIRECTION);
- })
- ;
-
- subtree->create<std::vector<std::string>>(tx_fe_path / "gains/all/profile/options")
- .set(RHODIUM_GP_OPTIONS);
-
- subtree->create<std::string>(tx_fe_path / "gains/all/profile/value")
- .set_coercer([this](const std::string& profile){
- std::string return_profile = profile;
- if (!uhd::has(RHODIUM_GP_OPTIONS, profile))
- {
- 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([](){
- return rhodium_radio_ctrl_impl::_get_gain_range(RX_DIRECTION);
- })
- ;
-
- subtree->create<std::vector<std::string> >(rx_fe_path / "gains/all/profile/options")
- .set(RHODIUM_GP_OPTIONS);
-
- subtree->create<std::string>(rx_fe_path / "gains/all/profile/value")
- .set_coercer([this](const std::string& profile){
- std::string return_profile = profile;
- if (!uhd::has(RHODIUM_GP_OPTIONS, profile))
- {
- return_profile = "default";
- }
- _gain_profile[RX_DIRECTION] = return_profile;
- return return_profile;
- })
- .set_publisher([this](){
- return _gain_profile[RX_DIRECTION];
- })
- ;
-
- // TX LO lock sensor
- 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
- 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
- //RX LO1 Frequency
- subtree->create<double>(rx_fe_path / "los"/RHODIUM_LO1/"freq/value")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double freq){
- return this->set_rx_lo_freq(freq, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "los"/RHODIUM_LO1/"freq/range")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq_range(RHODIUM_LO1, chan_idx);
- })
- ;
- //RX LO1 Source
- subtree->create<std::vector<std::string>>(rx_fe_path / "los"/RHODIUM_LO1/"source/options")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_sources(RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<std::string>(rx_fe_path / "los"/RHODIUM_LO1/"source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src){
- this->set_rx_lo_source(src, RHODIUM_LO1,chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_source(RHODIUM_LO1, chan_idx);
- })
- ;
- //RX LO1 Export
- subtree->create<bool>(rx_fe_path / "los"/RHODIUM_LO1/"export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_rx_lo_export_enabled(enabled, RHODIUM_LO1, chan_idx);
- })
- ;
- //RX LO1 Gain
- subtree->create<double>(rx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_GAIN / "value")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_gain(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double gain){
- return this->set_rx_lo_gain(gain, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_GAIN / "range")
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_lo_gain_range();
- })
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error("Attempting to update LO gain range!");
- })
- ;
- //RX LO1 Output Power
- subtree->create<double>(rx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_POWER / "value")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_power(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double gain){
- return this->set_rx_lo_power(gain, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_POWER / "range")
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_lo_power_range();
- })
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error("Attempting to update LO output power range!");
- })
- ;
- //RX LO2 Frequency
- subtree->create<double>(rx_fe_path / "los"/RHODIUM_LO2/"freq/value")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq(RHODIUM_LO2, chan_idx);
- })
- .set_coercer([this,chan_idx](double freq){
- return this->set_rx_lo_freq(freq, RHODIUM_LO2, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(rx_fe_path / "los"/RHODIUM_LO2/"freq/range")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq_range(RHODIUM_LO2, chan_idx);
- })
- ;
- //RX LO2 Source
- subtree->create<std::vector<std::string>>(rx_fe_path / "los"/RHODIUM_LO2/"source/options")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_sources(RHODIUM_LO2, chan_idx);
- })
- ;
- subtree->create<std::string>(rx_fe_path / "los"/RHODIUM_LO2/"source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src){
- this->set_rx_lo_source(src, RHODIUM_LO2, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_source(RHODIUM_LO2, chan_idx);
- })
- ;
- //RX LO2 Export
- subtree->create<bool>(rx_fe_path / "los"/RHODIUM_LO2/"export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_rx_lo_export_enabled(enabled, RHODIUM_LO2, chan_idx);
- });
- //RX ALL LOs
- subtree->create<std::string>(rx_fe_path / "los" / ALL_LOS / "source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src) {
- this->set_rx_lo_source(src, ALL_LOS, chan_idx);
- })
- .set_publisher([this,chan_idx]() {
- return this->get_rx_lo_source(ALL_LOS, chan_idx);
- })
- ;
- subtree->create<std::vector<std::string>>(rx_fe_path / "los" / ALL_LOS / "source/options")
- .set_publisher([this, chan_idx]() {
- return this->get_rx_lo_sources(ALL_LOS, chan_idx);
- })
- ;
- subtree->create<bool>(rx_fe_path / "los" / ALL_LOS / "export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_rx_lo_export_enabled(enabled, ALL_LOS, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_export_enabled(ALL_LOS, chan_idx);
- })
- ;
- //TX LO
- //TX LO1 Frequency
- subtree->create<double>(tx_fe_path / "los"/RHODIUM_LO1/"freq/value ")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_freq(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](double freq){
- return this->set_tx_lo_freq(freq, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "los"/RHODIUM_LO1/"freq/range")
- .set_publisher([this,chan_idx](){
- return this->get_rx_lo_freq_range(RHODIUM_LO1, chan_idx);
- })
- ;
- //TX LO1 Source
- subtree->create<std::vector<std::string>>(tx_fe_path / "los"/RHODIUM_LO1/"source/options")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_sources(RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<std::string>(tx_fe_path / "los"/RHODIUM_LO1/"source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src){
- this->set_tx_lo_source(src, RHODIUM_LO1, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_source(RHODIUM_LO1, chan_idx);
- })
- ;
- //TX LO1 Export
- subtree->create<bool>(tx_fe_path / "los"/RHODIUM_LO1/"export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_tx_lo_export_enabled(enabled, RHODIUM_LO1, chan_idx);
- })
- ;
- //TX LO1 Gain
- subtree->create<double>(tx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_GAIN / "value")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_gain(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double gain){
- return this->set_tx_lo_gain(gain, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_GAIN / "range")
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_lo_gain_range();
- })
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error("Attempting to update LO gain range!");
- })
- ;
- //TX LO1 Output Power
- subtree->create<double>(tx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_POWER / "value")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_power(RHODIUM_LO1, chan_idx);
- })
- .set_coercer([this,chan_idx](const double gain){
- return this->set_tx_lo_power(gain, RHODIUM_LO1, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "los" /RHODIUM_LO1/ "gains" / RHODIUM_LO_POWER / "range")
- .set_publisher([](){
- return rhodium_radio_ctrl_impl::_get_lo_power_range();
- })
- .add_coerced_subscriber([](const meta_range_t &){
- throw uhd::runtime_error("Attempting to update LO output power range!");
- })
- ;
- //TX LO2 Frequency
- subtree->create<double>(tx_fe_path / "los"/RHODIUM_LO2/"freq/value")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_freq(RHODIUM_LO2, chan_idx);
- })
- .set_coercer([this,chan_idx](double freq){
- return this->set_tx_lo_freq(freq, RHODIUM_LO2, chan_idx);
- })
- ;
- subtree->create<meta_range_t>(tx_fe_path / "los"/RHODIUM_LO2/"freq/range")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_freq_range(RHODIUM_LO2,chan_idx);
- })
- ;
- //TX LO2 Source
- subtree->create<std::vector<std::string>>(tx_fe_path / "los"/RHODIUM_LO2/"source/options")
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_sources(RHODIUM_LO2, chan_idx);
- })
- ;
- subtree->create<std::string>(tx_fe_path / "los"/RHODIUM_LO2/"source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src){
- this->set_tx_lo_source(src, RHODIUM_LO2, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_source(RHODIUM_LO2, chan_idx);
- })
- ;
- //TX LO2 Export
- subtree->create<bool>(tx_fe_path / "los"/RHODIUM_LO2/"export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_tx_lo_export_enabled(enabled, RHODIUM_LO2, chan_idx);
- })
- ;
- //TX ALL LOs
- subtree->create<std::string>(tx_fe_path / "los" / ALL_LOS / "source/value")
- .add_coerced_subscriber([this,chan_idx](std::string src) {
- this->set_tx_lo_source(src, ALL_LOS, chan_idx);
- })
- .set_publisher([this,chan_idx]() {
- return this->get_tx_lo_source(ALL_LOS, chan_idx);
- })
- ;
- subtree->create<std::vector<std::string>>(tx_fe_path / "los" / ALL_LOS / "source/options")
- .set_publisher([this, chan_idx]() {
- return this->get_tx_lo_sources(ALL_LOS, chan_idx);
- })
- ;
- subtree->create<bool>(tx_fe_path / "los" / ALL_LOS / "export")
- .add_coerced_subscriber([this,chan_idx](bool enabled){
- this->set_tx_lo_export_enabled(enabled, ALL_LOS, chan_idx);
- })
- .set_publisher([this,chan_idx](){
- return this->get_tx_lo_export_enabled(ALL_LOS, chan_idx);
- })
- ;
-
- //LO Distribution Output Ports
- if (_lo_dist_present) {
- for (const auto& port : LO_OUTPUT_PORT_NAMES) {
- subtree->create<bool>(tx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port / "export")
- .add_coerced_subscriber([this, chan_idx, port](bool enabled) {
- this->set_tx_lo_output_enabled(enabled, port, chan_idx);
- })
- .set_publisher([this, chan_idx, port]() {
- return this->get_tx_lo_output_enabled(port, chan_idx);
- })
- ;
- subtree->create<bool>(rx_fe_path / "los" / RHODIUM_LO1 / "lo_distribution" / port / "export")
- .add_coerced_subscriber([this, chan_idx, port](bool enabled) {
- this->set_rx_lo_output_enabled(enabled, port, chan_idx);
- })
- .set_publisher([this, chan_idx, port]() {
- return this->get_rx_lo_output_enabled(port, chan_idx);
- })
- ;
- }
- }
-}
-
-void rhodium_radio_ctrl_impl::_init_prop_tree()
-{
- const fs_path fe_base = fs_path("dboards") / _radio_slot;
- this->_init_frontend_subtree(_tree->subtree(fe_base), 0);
-
- // legacy EEPROM paths
- auto eeprom_get = [this]() {
- auto eeprom = dboard_eeprom_t();
- eeprom.id = boost::lexical_cast<uint16_t>(_dboard_info.at("pid"));
- eeprom.revision = _dboard_info.at("rev");
- eeprom.serial = _dboard_info.at("serial");
- return eeprom;
- };
-
- auto eeprom_set = [](dboard_eeprom_t) {
- throw uhd::not_implemented_error("Setting DB EEPROM from this interface not implemented");
- };
-
- _tree->create<dboard_eeprom_t>(fe_base / "rx_eeprom")
- .set_publisher(eeprom_get)
- .add_coerced_subscriber(eeprom_set);
-
- _tree->create<dboard_eeprom_t>(fe_base / "tx_eeprom")
- .set_publisher(eeprom_get)
- .add_coerced_subscriber(eeprom_set);
-
- // EEPROM paths subject to change FIXME
- _tree->create<eeprom_map_t>(_root_path / "eeprom")
- .set(eeprom_map_t());
-
- _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("ad9695-625");
- _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("dac37j82");
-
- // The tick_rate is equivalent to the master clock rate of the DB in slot A
- if (_radio_slot == "A")
- {
- UHD_ASSERT_THROW(!_tree->exists("tick_rate"));
- // set_rate sets the clock rate of the entire device, not just this DB,
- // so only add DB A's set and get functions to the tree.
- _tree->create<double>("tick_rate")
- .set_publisher([this](){ return this->get_rate(); })
- .add_coerced_subscriber([this](double rate) { return this->set_rate(rate); })
- ;
- }
-}
-
-void rhodium_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)
- );
- })
- ;
- }
-}
-
diff --git a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp b/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp
deleted file mode 100644
index 405862485..000000000
--- a/host/lib/usrp/dboard/rhodium/rhodium_radio_ctrl_lo.cpp
+++ /dev/null
@@ -1,726 +0,0 @@
-//
-// Copyright 2018 Ettus Research, a National Instruments Company
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-//
-
-#include "rhodium_radio_ctrl_impl.hpp"
-#include "rhodium_constants.hpp"
-#include <uhdlib/utils/narrow.hpp>
-#include <uhd/utils/log.hpp>
-#include <uhd/utils/algorithm.hpp>
-#include <uhd/types/direction.hpp>
-#include <uhd/exception.hpp>
-#include <boost/format.hpp>
-
-using namespace uhd;
-using namespace uhd::usrp;
-using namespace uhd::rfnoc;
-
-namespace {
- constexpr int NUM_THRESHOLDS = 13;
- constexpr std::array<double, NUM_THRESHOLDS> FREQ_THRESHOLDS =
- {0.45e9, 0.5e9, 1e9, 1.5e9, 2e9, 2.5e9, 3e9, 3.55e9, 4e9, 4.5e9, 5e9, 5.5e9, 6e9};
- constexpr std::array<int, NUM_THRESHOLDS> LMX_GAIN_VALUES =
- {18, 18, 17, 17, 17, 16, 12, 11, 11, 11, 10, 13, 18};
- const std::array<int, NUM_THRESHOLDS> DSA_RX_GAIN_VALUES =
- {19, 19, 21, 21, 20, 20, 22, 21, 20, 22, 22, 24, 26};
- const std::array<int, NUM_THRESHOLDS> DSA_TX_GAIN_VALUES =
- {19, 19, 21, 21, 20, 20, 22, 21, 22, 24, 24, 26, 28};
-
- // Describes the lowband LO in terms of the master clock rate
- const std::map<double, double> MCR_TO_LOWBAND_IF = {
- {200e6, 1200e6},
- {245.76e6, 1228.8e6},
- {250e6, 1500e6},
- };
-
- // validation helpers
-
- std::vector<std::string> _get_lo_names()
- {
- return { RHODIUM_LO1, RHODIUM_LO2 };
- }
-
- void _validate_lo_name(const std::string& name, const std::string& function_name)
- {
- if (!uhd::has(_get_lo_names(), name) and name != radio_ctrl::ALL_LOS) {
- throw uhd::value_error(str(boost::format(
- "%s was called with an invalid LO name: %s")
- % function_name
- % name));
- }
- }
-
- // object agnostic helpers
- std::vector<std::string> _get_lo_sources(const std::string& name)
- {
- if (name == RHODIUM_LO1 or name == radio_ctrl::ALL_LOS) {
- return { "internal", "external" };
- } else {
- return { "internal" };
- }
- }
-}
-
-/******************************************************************************
- * Property Getters
- *****************************************************************************/
-
-std::vector<std::string> rhodium_radio_ctrl_impl::get_tx_lo_names(
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_names(chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- return _get_lo_names();
-}
-
-std::vector<std::string> rhodium_radio_ctrl_impl::get_rx_lo_names(
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_names(chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
-
- return _get_lo_names();
-}
-
-std::vector<std::string> rhodium_radio_ctrl_impl::get_tx_lo_sources(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_sources(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_sources");
-
- return _get_lo_sources(name);
-}
-
-std::vector<std::string> rhodium_radio_ctrl_impl::get_rx_lo_sources(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_sources(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_sources");
-
- return _get_lo_sources(name);
-}
-
-freq_range_t rhodium_radio_ctrl_impl::_get_lo_freq_range(const std::string &name) const
-{
- if (name == RHODIUM_LO1) {
- return freq_range_t{ RHODIUM_LO1_MIN_FREQ, RHODIUM_LO1_MAX_FREQ };
- } else if (name == RHODIUM_LO2) {
- // The Lowband LO is a fixed frequency
- return freq_range_t{ _get_lowband_lo_freq(), _get_lowband_lo_freq() };
- } else {
- throw uhd::runtime_error(
- "LO frequency range must be retrieved for each stage individually");
- }
-}
-
-freq_range_t rhodium_radio_ctrl_impl::get_tx_lo_freq_range(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_freq_range(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_freq_range");
-
- return _get_lo_freq_range(name);
-}
-
-freq_range_t rhodium_radio_ctrl_impl::get_rx_lo_freq_range(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_freq_range(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_freq_range");
-
- return _get_lo_freq_range(name);
-}
-
-/******************************************************************************
- * Frequency Control
- *****************************************************************************/
-
-double rhodium_radio_ctrl_impl::set_tx_lo_freq(
- const double freq,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_freq(freq=" << freq << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_freq");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO frequency must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO cannot be tuned");
- return _get_lowband_lo_freq();
- }
-
- const auto sd_enabled = _get_spur_dodging_enabled(TX_DIRECTION);
- const auto sd_threshold = _get_spur_dodging_threshold(TX_DIRECTION);
-
- _tx_lo_freq = _tx_lo->set_frequency(freq, sd_enabled, sd_threshold);
- set_tx_lo_gain(_get_lo_dsa_setting(_tx_lo_freq, TX_DIRECTION), RHODIUM_LO1, chan);
- set_tx_lo_power(_get_lo_power_setting(_tx_lo_freq), RHODIUM_LO1, chan);
- _cpld->set_tx_lo_path(_tx_lo_freq);
-
- return _tx_lo_freq;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_lo_freq(
- const double freq,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_freq(freq=" << freq << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_rx_lo_freq");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO frequency must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO cannot be tuned");
- return _get_lowband_lo_freq();
- }
-
- const auto sd_enabled = _get_spur_dodging_enabled(RX_DIRECTION);
- const auto sd_threshold = _get_spur_dodging_threshold(RX_DIRECTION);
-
- _rx_lo_freq = _rx_lo->set_frequency(freq, sd_enabled, sd_threshold);
- set_rx_lo_gain(_get_lo_dsa_setting(_rx_lo_freq, RX_DIRECTION), RHODIUM_LO1, chan);
- set_rx_lo_power(_get_lo_power_setting(_rx_lo_freq), RHODIUM_LO1, chan);
- _cpld->set_rx_lo_path(_rx_lo_freq);
-
- return _rx_lo_freq;
-}
-
-double rhodium_radio_ctrl_impl::get_tx_lo_freq(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_freq(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_freq");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO frequency must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _tx_lo_freq : _get_lowband_lo_freq();
-}
-
-double rhodium_radio_ctrl_impl::get_rx_lo_freq(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_freq(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_freq");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO frequency must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _rx_lo_freq : _get_lowband_lo_freq();
-}
-
-/******************************************************************************
- * Source Control
- *****************************************************************************/
-
-void rhodium_radio_ctrl_impl::set_tx_lo_source(
- const std::string& src,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_source(src=" << src << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_source");
-
- if (name == RHODIUM_LO2) {
- if (src != "internal") {
- throw uhd::value_error("The Lowband LO can only be set to internal");
- }
- return;
- }
-
- if (src == "internal") {
- _tx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, true);
- _cpld->set_tx_lo_source(rhodium_cpld_ctrl::tx_lo_input_sel_t::TX_LO_INPUT_SEL_INTERNAL);
- } else if (src == "external") {
- _tx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, false);
- _cpld->set_tx_lo_source(rhodium_cpld_ctrl::tx_lo_input_sel_t::TX_LO_INPUT_SEL_EXTERNAL);
- } else {
- throw uhd::value_error(str(boost::format("set_tx_lo_source was called with an invalid LO source: %s Valid sources are [internal, external]") % src));
- }
-
- const bool enable_corrections = not _is_tx_lowband(get_tx_frequency(0))
- and src == "internal";
- _update_corrections(get_tx_frequency(0), TX_DIRECTION, enable_corrections);
-
- _tx_lo_source = src;
-}
-
-void rhodium_radio_ctrl_impl::set_rx_lo_source(
- const std::string& src,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_source(src=" << src << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_source");
-
- if (name == RHODIUM_LO2) {
- if (src != "internal") {
- throw uhd::value_error("The Lowband LO can only be set to internal");
- }
- return;
- }
-
- if (src == "internal") {
- _rx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, true);
- _cpld->set_rx_lo_source(rhodium_cpld_ctrl::rx_lo_input_sel_t::RX_LO_INPUT_SEL_INTERNAL);
- } else if (src == "external") {
- _rx_lo->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_A, false);
- _cpld->set_rx_lo_source(rhodium_cpld_ctrl::rx_lo_input_sel_t::RX_LO_INPUT_SEL_EXTERNAL);
- } else {
- throw uhd::value_error(str(boost::format("set_rx_lo_source was called with an invalid LO source: %s Valid sources for LO1 are [internal, external]") % src));
- }
-
- const bool enable_corrections = not _is_rx_lowband(get_rx_frequency(0))
- and src == "internal";
- _update_corrections(get_rx_frequency(0), RX_DIRECTION, enable_corrections);
-
- _rx_lo_source = src;
-}
-
-const std::string rhodium_radio_ctrl_impl::get_tx_lo_source(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_source(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_source");
-
- return (name == RHODIUM_LO1 or name == ALL_LOS) ? _tx_lo_source : "internal";
-}
-
-const std::string rhodium_radio_ctrl_impl::get_rx_lo_source(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_source(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_source");
-
- return (name == RHODIUM_LO1 or name == ALL_LOS) ? _rx_lo_source : "internal";
-}
-
-/******************************************************************************
- * Export Control
- *****************************************************************************/
-
-void rhodium_radio_ctrl_impl::_set_lo1_export_enabled(
- const bool enabled,
- const direction_t dir
-) {
- auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
- _lo_ctrl->set_output_enable(lmx2592_iface::output_t::RF_OUTPUT_B, enabled);
- if (_lo_dist_present) {
- const auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
- _rpcc->notify_with_token(_rpc_prefix + "enable_lo_export", direction, enabled);
- }
-}
-
-void rhodium_radio_ctrl_impl::set_tx_lo_export_enabled(
- const bool enabled,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_export_enabled(enabled=" << enabled << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_export_enabled");
-
- if (name == RHODIUM_LO2) {
- if (enabled) {
- throw uhd::value_error("The lowband LO cannot be exported");
- }
- return;
- }
-
- _set_lo1_export_enabled(enabled, TX_DIRECTION);
- _tx_lo_exported = enabled;
-}
-
-void rhodium_radio_ctrl_impl::set_rx_lo_export_enabled(
- const bool enabled,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_export_enabled(enabled=" << enabled << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_rx_lo_export_enabled");
-
- if (name == RHODIUM_LO2) {
- if (enabled) {
- throw uhd::value_error("The lowband LO cannot be exported");
- }
- return;
- }
-
- _set_lo1_export_enabled(enabled, RX_DIRECTION);
- _rx_lo_exported = enabled;
-}
-
-bool rhodium_radio_ctrl_impl::get_tx_lo_export_enabled(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_export_enabled(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_export_enabled");
-
- return (name == RHODIUM_LO1 or name == ALL_LOS) ? _tx_lo_exported : false;
-}
-
-bool rhodium_radio_ctrl_impl::get_rx_lo_export_enabled(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_export_enabled(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_export_enabled");
-
- return (name == RHODIUM_LO1 or name == ALL_LOS) ? _rx_lo_exported : false;
-}
-
-/******************************************************************************
- * LO Distribution Control
- *****************************************************************************/
-
-void rhodium_radio_ctrl_impl::_validate_output_port(const std::string& port_name, const std::string& function_name)
-{
- if (!_lo_dist_present) {
- throw uhd::runtime_error(str(boost::format(
- "%s can only be called if the LO distribution board was detected") % function_name));
- }
-
- if (!uhd::has(LO_OUTPUT_PORT_NAMES, port_name)) {
- throw uhd::value_error(str(boost::format(
- "%s was called with an invalid LO output port: %s Valid ports are [LO_OUT_0, LO_OUT_1, LO_OUT_2, LO_OUT_3]")
- % function_name % port_name));
- }
-}
-
-void rhodium_radio_ctrl_impl::_set_lo_output_enabled(
- const bool enabled,
- const std::string& port_name,
- const direction_t dir
-) {
- auto direction = (dir == RX_DIRECTION) ? "RX" : "TX";
- auto name_iter = std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
- auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
-
- _rpcc->notify_with_token(_rpc_prefix + "enable_lo_output", direction, index, enabled);
- auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled : _lo_dist_tx_out_enabled;
- out_enabled[index] = enabled;
-}
-
-void rhodium_radio_ctrl_impl::set_tx_lo_output_enabled(
- const bool enabled,
- const std::string& port_name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_output_enabled(enabled=" << enabled << ", port_name=" << port_name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_output_port(port_name, "set_tx_lo_output_enabled");
-
- _set_lo_output_enabled(enabled, port_name, TX_DIRECTION);
-}
-
-void rhodium_radio_ctrl_impl::set_rx_lo_output_enabled(
- const bool enabled,
- const std::string& port_name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_output_enabled(enabled=" << enabled << ", port_name=" << port_name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_output_port(port_name, "set_rx_lo_output_enabled");
-
- _set_lo_output_enabled(enabled, port_name, RX_DIRECTION);
-}
-
-bool rhodium_radio_ctrl_impl::_get_lo_output_enabled(
- const std::string& port_name,
- const direction_t dir
-) {
- auto name_iter = std::find(LO_OUTPUT_PORT_NAMES.begin(), LO_OUTPUT_PORT_NAMES.end(), port_name);
- auto index = std::distance(LO_OUTPUT_PORT_NAMES.begin(), name_iter);
-
- auto out_enabled = (dir == RX_DIRECTION) ? _lo_dist_rx_out_enabled : _lo_dist_tx_out_enabled;
- return out_enabled[index];
-}
-
-bool rhodium_radio_ctrl_impl::get_tx_lo_output_enabled(
- const std::string& port_name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_output_port(port_name, "get_tx_lo_output_enabled");
-
- return _get_lo_output_enabled(port_name, TX_DIRECTION);
-}
-
-bool rhodium_radio_ctrl_impl::get_rx_lo_output_enabled(
- const std::string& port_name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_output_enabled(port_name=" << port_name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_output_port(port_name, "get_rx_lo_output_enabled");
-
- return _get_lo_output_enabled(port_name, RX_DIRECTION);
-}
-
-/******************************************************************************
- * Gain Control
- *****************************************************************************/
-
-double rhodium_radio_ctrl_impl::set_tx_lo_gain(
- double gain,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_gain(gain=" << gain << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_gain");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO gain must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO does not have configurable gain");
- return 0.0;
- }
-
- auto index = _get_lo_gain_range().clip(gain);
-
- _cpld->set_lo_gain(index, TX_DIRECTION);
- _lo_tx_gain = index;
- return _lo_tx_gain;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_lo_gain(
- double gain,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_gain(gain=" << gain << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_rx_lo_gain");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO gain must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO does not have configurable gain");
- return 0.0;
- }
-
- auto index = _get_lo_gain_range().clip(gain);
-
- _cpld->set_lo_gain(index, RX_DIRECTION);
- _lo_rx_gain = index;
- return _lo_rx_gain;
-}
-
-double rhodium_radio_ctrl_impl::get_tx_lo_gain(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_gain(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_gain");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO gain must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _lo_rx_gain : 0.0;
-}
-
-double rhodium_radio_ctrl_impl::get_rx_lo_gain(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_gain(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_gain");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error("LO gain must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _lo_tx_gain : 0.0;
-}
-
-/******************************************************************************
- * Output Power Control
- *****************************************************************************/
-
-double rhodium_radio_ctrl_impl::_set_lo1_power(
- const double power,
- const direction_t dir
-) {
- auto& _lo_ctrl = (dir == RX_DIRECTION) ? _rx_lo : _tx_lo;
- auto coerced_power = static_cast<uint32_t>(_get_lo_power_range().clip(power, true));
-
- _lo_ctrl->set_output_power(lmx2592_iface::RF_OUTPUT_A, coerced_power);
- return coerced_power;
-}
-
-double rhodium_radio_ctrl_impl::set_tx_lo_power(
- const double power,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_tx_lo_power(power=" << power << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_tx_lo_power");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO output power must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO does not have configurable output power");
- return 0.0;
- }
-
- _lo_tx_power = _set_lo1_power(power, TX_DIRECTION);
- return _lo_tx_power;
-}
-
-double rhodium_radio_ctrl_impl::set_rx_lo_power(
- const double power,
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "set_rx_lo_power(power=" << power << ", name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "set_rx_lo_power");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO output power must be set for each stage individually");
- }
- if (name == RHODIUM_LO2) {
- UHD_LOG_WARNING(unique_id(), "The Lowband LO does not have configurable output power");
- return 0.0;
- }
-
- _lo_rx_power = _set_lo1_power(power, RX_DIRECTION);
- return _lo_rx_power;
-}
-
-double rhodium_radio_ctrl_impl::get_tx_lo_power(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_tx_lo_power(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_tx_lo_power");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO output power must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _lo_tx_power : 0.0;
-}
-
-double rhodium_radio_ctrl_impl::get_rx_lo_power(
- const std::string& name,
- const size_t chan
-) {
- UHD_LOG_TRACE(unique_id(), "get_rx_lo_power(name=" << name << ", chan=" << chan << ")");
- UHD_ASSERT_THROW(chan == 0);
- _validate_lo_name(name, "get_rx_lo_power");
-
- if (name == ALL_LOS) {
- throw uhd::runtime_error(
- "LO output power must be retrieved for each stage individually");
- }
-
- return (name == RHODIUM_LO1) ? _lo_rx_power : 0.0;
-}
-
-/******************************************************************************
- * Helper Functions
- *****************************************************************************/
-
-double rhodium_radio_ctrl_impl::_get_lowband_lo_freq() const
-{
- return MCR_TO_LOWBAND_IF.at(_master_clock_rate);
-}
-
-uhd::gain_range_t rhodium_radio_ctrl_impl::_get_lo_gain_range()
-{
- return gain_range_t(LO_MIN_GAIN, LO_MAX_GAIN, LO_GAIN_STEP);
-}
-
-uhd::gain_range_t rhodium_radio_ctrl_impl::_get_lo_power_range()
-{
- return gain_range_t(LO_MIN_POWER, LO_MAX_POWER, LO_POWER_STEP);
-}
-
-int rhodium_radio_ctrl_impl::_get_lo_dsa_setting(const double freq, const direction_t dir) {
- int index = 0;
- while (freq > FREQ_THRESHOLDS[index+1]) {
- index++;
- }
-
- const double freq_low = FREQ_THRESHOLDS[index];
- const double freq_high = FREQ_THRESHOLDS[index+1];
-
- const auto& gain_table = (dir == RX_DIRECTION) ? DSA_RX_GAIN_VALUES : DSA_TX_GAIN_VALUES;
- const double gain_low = gain_table[index];
- const double gain_high = gain_table[index+1];
-
-
- const double slope = (gain_high - gain_low) / (freq_high - freq_low);
- const double gain_at_freq = gain_low + (freq - freq_low) * slope;
-
- UHD_LOG_TRACE(unique_id(), "Interpolated DSA Gain is " << gain_at_freq);
- return static_cast<int>(std::round(gain_at_freq));
-}
-
-unsigned int rhodium_radio_ctrl_impl::_get_lo_power_setting(double freq) {
- int index = 0;
- while (freq > FREQ_THRESHOLDS[index+1]) {
- index++;
- }
-
- const double freq_low = FREQ_THRESHOLDS[index];
- const double freq_high = FREQ_THRESHOLDS[index+1];
- const double power_low = LMX_GAIN_VALUES[index];
- const double power_high = LMX_GAIN_VALUES[index+1];
-
-
- const double slope = (power_high - power_low) / (freq_high - freq_low);
- const double power_at_freq = power_low + (freq - freq_low) * slope;
-
- UHD_LOG_TRACE(unique_id(), "Interpolated LMX Power is " << power_at_freq);
- return uhd::narrow_cast<unsigned int>(std::lround(power_at_freq));
-}