aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/x400
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/x400')
-rw-r--r--host/lib/usrp/x400/CMakeLists.txt20
-rw-r--r--host/lib/usrp/x400/adc_self_calibration.cpp196
-rw-r--r--host/lib/usrp/x400/adc_self_calibration.hpp46
-rw-r--r--host/lib/usrp/x400/x400_radio_control.cpp752
-rw-r--r--host/lib/usrp/x400/x400_radio_control.hpp198
-rw-r--r--host/lib/usrp/x400/x400_rfdc_control.cpp73
6 files changed, 1285 insertions, 0 deletions
diff --git a/host/lib/usrp/x400/CMakeLists.txt b/host/lib/usrp/x400/CMakeLists.txt
new file mode 100644
index 000000000..b3efe3b60
--- /dev/null
+++ b/host/lib/usrp/x400/CMakeLists.txt
@@ -0,0 +1,20 @@
+#
+# Copyright 2020 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/adc_self_calibration.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x400_radio_control.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/x400_rfdc_control.cpp
+)
+
+if(ENABLE_DPDK)
+ include_directories(${DPDK_INCLUDE_DIRS})
+ add_definitions(-DHAVE_DPDK)
+endif(ENABLE_DPDK)
diff --git a/host/lib/usrp/x400/adc_self_calibration.cpp b/host/lib/usrp/x400/adc_self_calibration.cpp
new file mode 100644
index 000000000..7deba7725
--- /dev/null
+++ b/host/lib/usrp/x400/adc_self_calibration.cpp
@@ -0,0 +1,196 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "adc_self_calibration.hpp"
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/scope_exit.hpp>
+#include <chrono>
+#include <thread>
+
+using namespace std::chrono_literals;
+
+namespace uhd { namespace features {
+
+adc_self_calibration::adc_self_calibration(uhd::usrp::x400_rpc_iface::sptr rpcc,
+ const std::string rpc_prefix,
+ const std::string unique_id,
+ size_t db_number,
+ uhd::usrp::x400::x400_dboard_iface::sptr daughterboard)
+ : _rpcc(rpcc)
+ , _rpc_prefix(rpc_prefix)
+ , _db_number(db_number)
+ , _daughterboard(daughterboard)
+ , _unique_id(unique_id)
+{
+}
+
+void adc_self_calibration::run(size_t chan)
+{
+ const auto tx_gain_profile =
+ _daughterboard->get_tx_gain_profile_api()->get_gain_profile(chan);
+ const auto rx_gain_profile =
+ _daughterboard->get_rx_gain_profile_api()->get_gain_profile(chan);
+ if (tx_gain_profile != "default" || rx_gain_profile != "default") {
+ throw uhd::runtime_error("Cannot run ADC self calibration when gain "
+ "profile for RX or TX is not 'default'.");
+ }
+
+ // The frequency that we need to feed into the ADC is, by decree,
+ // 13109 / 32768 times the ADC sample rate. (approx. 1.2GHz for a 3Gsps rate)
+ const double spll_freq = _rpcc->get_spll_freq();
+ const double cal_tone_freq = spll_freq * 13109.0 / 32768.0;
+
+ const auto cal_params = _daughterboard->get_adc_self_cal_params(cal_tone_freq);
+
+ // Switch to CAL_LOOPBACK and save the current antenna
+ const auto rx_antenna = _daughterboard->get_rx_antenna(chan);
+ const auto tx_antenna = _daughterboard->get_tx_antenna(chan);
+
+ auto reset_antennas = uhd::utils::scope_exit::make([&]() {
+ _daughterboard->set_rx_antenna(rx_antenna, chan);
+ _daughterboard->set_tx_antenna(tx_antenna, chan);
+
+ // Waiting here allows some CPLD registers to be set. It's not clear
+ // to me why we require this wait. See azdo #1473824
+ constexpr auto fudge_time = 100ms;
+ std::this_thread::sleep_for(fudge_time);
+ });
+
+ _daughterboard->set_rx_antenna("CAL_LOOPBACK", chan);
+ _daughterboard->set_tx_antenna("CAL_LOOPBACK", chan);
+
+ // Configure the output DAC mux to output 1/2 full scale
+ // set_dac_mux_data uses 16-bit values.
+ _rpcc->set_dac_mux_data(32768 / 2, 0);
+
+ const size_t motherboard_channel_number = _db_number * 2 + chan;
+ _rpcc->set_dac_mux_enable(motherboard_channel_number, 1);
+ auto disable_dac_mux = uhd::utils::scope_exit::make(
+ [&]() { _rpcc->set_dac_mux_enable(motherboard_channel_number, 0); });
+
+ // Save all of the LO frequencies & sources
+ const double original_rx_freq = _daughterboard->get_rx_frequency(chan);
+ std::map<std::string, std::tuple<std::string, double>> rx_lo_state;
+ for (auto rx_lo : _daughterboard->get_rx_lo_names(chan)) {
+ const std::string source(_daughterboard->get_rx_lo_source(rx_lo, chan));
+ const double freq = _daughterboard->get_rx_lo_freq(rx_lo, chan);
+ rx_lo_state.emplace(std::piecewise_construct,
+ std::forward_as_tuple(rx_lo),
+ std::forward_as_tuple(source, freq));
+ }
+ auto restore_rx_los = uhd::utils::scope_exit::make([&]() {
+ _daughterboard->set_rx_frequency(original_rx_freq, chan);
+ for (auto entry : rx_lo_state) {
+ auto& lo = std::get<0>(entry);
+ auto& state = std::get<1>(entry);
+
+ auto& source = std::get<0>(state);
+ const double freq = std::get<1>(state);
+ _daughterboard->set_rx_lo_source(source, lo, chan);
+ _daughterboard->set_rx_lo_freq(freq, lo, chan);
+ }
+ });
+
+ const double original_tx_freq = _daughterboard->get_tx_frequency(chan);
+ std::map<std::string, std::tuple<std::string, double>> tx_lo_state;
+ for (auto tx_lo : _daughterboard->get_tx_lo_names(chan)) {
+ const std::string source(_daughterboard->get_tx_lo_source(tx_lo, chan));
+ const double freq = _daughterboard->get_tx_lo_freq(tx_lo, chan);
+ tx_lo_state.emplace(std::piecewise_construct,
+ std::forward_as_tuple(tx_lo),
+ std::forward_as_tuple(source, freq));
+ }
+ auto restore_tx_los = uhd::utils::scope_exit::make([&]() {
+ _daughterboard->set_tx_frequency(original_tx_freq, chan);
+ for (auto entry : tx_lo_state) {
+ auto& lo = std::get<0>(entry);
+ auto& state = std::get<1>(entry);
+
+ auto& source = std::get<0>(state);
+ const double freq = std::get<1>(state);
+ _daughterboard->set_tx_lo_source(source, lo, chan);
+ _daughterboard->set_tx_lo_freq(freq, lo, chan);
+ }
+ });
+
+ _daughterboard->set_tx_frequency(cal_params.tx_freq, chan);
+ _daughterboard->set_rx_frequency(cal_params.rx_freq, chan);
+
+ // Set & restore the gain
+ const double tx_gain = _daughterboard->get_tx_gain(chan);
+ const double rx_gain = _daughterboard->get_rx_gain(chan);
+ auto restore_gains = uhd::utils::scope_exit::make([&]() {
+ _daughterboard->get_rx_gain_profile_api()->set_gain_profile("default", chan);
+ _daughterboard->get_tx_gain_profile_api()->set_gain_profile("default", chan);
+
+ _daughterboard->set_tx_gain(tx_gain, chan);
+ _daughterboard->set_rx_gain(rx_gain, chan);
+ });
+
+ // Set the threshold to detect half-scale
+ // The setup_threshold call uses 14-bit ADC values
+ constexpr int hysteresis_reset_time = 100;
+ constexpr int hysteresis_reset_threshold = 8000;
+ constexpr int hysteresis_set_threshold = 8192;
+ _rpcc->setup_threshold(_db_number,
+ chan,
+ 0,
+ "hysteresis",
+ hysteresis_reset_time,
+ hysteresis_reset_threshold,
+ hysteresis_set_threshold);
+ bool found_gain = false;
+ for (double i = cal_params.min_gain; i <= cal_params.max_gain; i += 1.0) {
+ _daughterboard->get_rx_gain_profile_api()->set_gain_profile("default", chan);
+ _daughterboard->get_tx_gain_profile_api()->set_gain_profile("default", chan);
+
+ _daughterboard->set_tx_gain(i, chan);
+ _daughterboard->set_rx_gain(i, chan);
+
+ // Set the daughterboard to use the duplex entry in the DSA table which was
+ // configured in the set_?x_gain call. (note that with a LabVIEW FPGA, we don't
+ // control the ATR lines, hence why we override them here)
+ _daughterboard->get_rx_gain_profile_api()->set_gain_profile("table_noatr", chan);
+ _daughterboard->get_tx_gain_profile_api()->set_gain_profile("table_noatr", chan);
+
+ _daughterboard->set_rx_gain(0b11, chan);
+ _daughterboard->set_tx_gain(0b11, chan);
+
+ // Wait for it to settle
+ constexpr auto settle_time = 10ms;
+ std::this_thread::sleep_for(settle_time);
+
+ try {
+ const bool threshold_status =
+ _rpcc->get_threshold_status(_db_number, chan, 0);
+ if (threshold_status) {
+ found_gain = true;
+ break;
+ }
+ } catch (uhd::runtime_error&) {
+ // Catch & eat this error, the user has a 5.0 FPGA and so can't auto-gain
+ return;
+ }
+ }
+
+ if (!found_gain) {
+ throw uhd::runtime_error(
+ "Could not find appropriate gain for performing ADC self cal");
+ }
+
+ // (if required) unfreeze calibration
+ const std::vector<int> current_frozen_state = _rpcc->get_cal_frozen(_db_number, chan);
+ _rpcc->set_cal_frozen(0, _db_number, chan);
+ auto refreeze_adcs = uhd::utils::scope_exit::make(
+ [&]() { _rpcc->set_cal_frozen(current_frozen_state[0], _db_number, chan); });
+
+ // Let the ADC calibrate
+ // 2000ms was found experimentally to be sufficient
+ constexpr auto calibration_time = 2000ms;
+ std::this_thread::sleep_for(calibration_time);
+}
+
+}} // namespace uhd::features
diff --git a/host/lib/usrp/x400/adc_self_calibration.hpp b/host/lib/usrp/x400/adc_self_calibration.hpp
new file mode 100644
index 000000000..474c14e07
--- /dev/null
+++ b/host/lib/usrp/x400/adc_self_calibration.hpp
@@ -0,0 +1,46 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include <uhd/features/adc_self_calibration_iface.hpp>
+#include <uhdlib/usrp/common/rpc.hpp>
+#include <uhdlib/usrp/dboard/x400_dboard_iface.hpp>
+#include <string>
+
+namespace uhd { namespace features {
+
+using namespace uhd::rfnoc;
+
+class adc_self_calibration : public adc_self_calibration_iface
+{
+public:
+ adc_self_calibration(uhd::usrp::x400_rpc_iface::sptr rpcc,
+ const std::string rpc_prefix,
+ const std::string unique_id,
+ size_t db_number,
+ uhd::usrp::x400::x400_dboard_iface::sptr daughterboard);
+
+ void run(const size_t channel) override;
+
+private:
+ //! Reference to the RPC client
+ uhd::usrp::x400_rpc_iface::sptr _rpcc;
+
+ const std::string _rpc_prefix;
+
+ const size_t _db_number;
+
+ uhd::usrp::x400::x400_dboard_iface::sptr _daughterboard;
+
+ const std::string _unique_id;
+ std::string get_unique_id() const
+ {
+ return _unique_id;
+ }
+};
+
+}} // namespace uhd::features
diff --git a/host/lib/usrp/x400/x400_radio_control.cpp b/host/lib/usrp/x400/x400_radio_control.cpp
new file mode 100644
index 000000000..281ae7916
--- /dev/null
+++ b/host/lib/usrp/x400/x400_radio_control.cpp
@@ -0,0 +1,752 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include "x400_radio_control.hpp"
+#include <uhd/rfnoc/registry.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/math.hpp>
+#include <uhdlib/usrp/common/x400_rfdc_control.hpp>
+#include <uhdlib/usrp/dboard/debug_dboard.hpp>
+#include <uhdlib/usrp/dboard/null_dboard.hpp>
+#include <uhdlib/usrp/dboard/zbx/zbx_dboard.hpp>
+
+namespace uhd { namespace rfnoc {
+
+x400_radio_control_impl::fpga_onload::fpga_onload(size_t num_channels,
+ uhd::features::adc_self_calibration_iface::sptr adc_self_cal,
+ std::string unique_id)
+ : _num_channels(num_channels), _adc_self_cal(adc_self_cal), _unique_id(unique_id)
+{
+}
+
+void x400_radio_control_impl::fpga_onload::onload()
+{
+ for (size_t channel = 0; channel < _num_channels; channel++) {
+ if (_adc_self_cal) {
+ try {
+ _adc_self_cal->run(channel);
+ } catch (uhd::runtime_error& e) {
+ RFNOC_LOG_WARNING("Failure while running self cal on channel "
+ << channel << ": " << e.what());
+ }
+ }
+ }
+}
+
+x400_radio_control_impl::x400_radio_control_impl(make_args_ptr make_args)
+ : radio_control_impl(std::move(make_args))
+{
+ RFNOC_LOG_TRACE("Initializing x400_radio_control");
+
+ UHD_ASSERT_THROW(get_block_id().get_block_count() < 2);
+ constexpr char radio_slot_name[2] = {'A', 'B'};
+ _radio_slot = radio_slot_name[get_block_id().get_block_count()];
+ _rpc_prefix = get_block_id().get_block_count() == 1 ? "db_1_" : "db_0_";
+
+ UHD_ASSERT_THROW(get_mb_controller());
+ _mb_control = std::dynamic_pointer_cast<mpmd_mb_controller>(get_mb_controller());
+
+ _x4xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>(
+ _mb_control->get_timekeeper(0));
+ UHD_ASSERT_THROW(_x4xx_timekeeper);
+
+ _rpcc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::x400_rpc_iface>();
+ if (!_rpcc) {
+ _rpcc = std::make_shared<uhd::usrp::x400_rpc>(_mb_control->get_rpc_client());
+ }
+
+ _db_rpcc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::dboard_base_rpc_iface>();
+ if (!_db_rpcc) {
+ _db_rpcc = std::make_shared<uhd::usrp::dboard_base_rpc>(
+ _mb_control->get_rpc_client(), _rpc_prefix);
+ }
+
+ const auto all_dboard_info = _rpcc->get_dboard_info();
+ RFNOC_LOG_TRACE("Hardware detected " << all_dboard_info.size() << " daughterboards.");
+
+ // If we have 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()) {
+ RFNOC_LOG_WARNING("The number of discovered daughterboards did not match the "
+ "number of radio blocks. Skipping front end initialization.");
+ _daughterboard = std::make_shared<null_dboard_impl>();
+ return;
+ }
+
+ const double master_clock_rate = _rpcc->get_master_clock_rate();
+ set_tick_rate(master_clock_rate);
+ _x4xx_timekeeper->update_tick_rate(master_clock_rate);
+ radio_control_impl::set_rate(master_clock_rate);
+
+ for (auto& samp_rate_prop : _samp_rate_in) {
+ set_property(samp_rate_prop.get_id(), get_rate(), samp_rate_prop.get_src_info());
+ }
+ for (auto& samp_rate_prop : _samp_rate_out) {
+ set_property(samp_rate_prop.get_id(), get_rate(), samp_rate_prop.get_src_info());
+ }
+
+ _validate_master_clock_rate_args();
+ _init_mpm();
+
+ RFNOC_LOG_TRACE("Initializing RFDC controls...");
+ _rfdcc = std::make_shared<x400::rfdc_control>(
+ // clang-format off
+ uhd::memmap32_iface_timed{
+ [this](const uint32_t addr, const uint32_t data, const uhd::time_spec_t& time_spec) {
+ regs().poke32(addr + x400_regs::RFDC_CTRL_BASE, data, time_spec);
+ },
+ [this](const uint32_t addr) {
+ return regs().peek32(addr + x400_regs::RFDC_CTRL_BASE);
+ }
+ },
+ // clang-format on
+ get_unique_id() + "::RFDC");
+
+ const auto& dboard = all_dboard_info[get_block_id().get_block_count()];
+ const std::string pid(dboard.at("pid").begin(), dboard.at("pid").end());
+ RFNOC_LOG_TRACE("Initializing daughterboard driver for PID " << pid);
+
+ // We may have physical daughterboards in the system, but no GPIO interface to the
+ // daughterboard in the FPGA. In this case, just instantiate the null daughterboard.
+ if (!_rpcc->is_db_gpio_ifc_present(get_block_id().get_block_count())) {
+ RFNOC_LOG_WARNING(
+ "Skipping daughterboard initialization, no GPIO interface in FPGA");
+ _daughterboard = std::make_shared<null_dboard_impl>();
+ return;
+ }
+
+ if (std::stol(pid) == uhd::usrp::zbx::ZBX_PID) {
+ auto zbx_rpc_sptr = _mb_control->dynamic_cast_rpc_as<uhd::usrp::zbx_rpc_iface>();
+ if (!zbx_rpc_sptr) {
+ zbx_rpc_sptr = std::make_shared<uhd::usrp::zbx_rpc>(
+ _mb_control->get_rpc_client(), _rpc_prefix);
+ }
+ _daughterboard = std::make_shared<uhd::usrp::zbx::zbx_dboard_impl>(
+ regs(),
+ regmap::PERIPH_BASE,
+ [this](const size_t instance) { return get_command_time(instance); },
+ get_block_id().get_block_count(),
+ _radio_slot,
+ _rpc_prefix,
+ get_unique_id(),
+ _rpcc,
+ zbx_rpc_sptr,
+ _rfdcc,
+ get_tree());
+ } else if (std::stol(pid) == uhd::rfnoc::DEBUG_DB_PID) {
+ _daughterboard = std::make_shared<debug_dboard_impl>();
+ } else if (std::stol(pid) == uhd::rfnoc::IF_TEST_DBOARD_PID) {
+ _daughterboard =
+ std::make_shared<if_test_dboard_impl>(get_block_id().get_block_count(),
+ _rpc_prefix,
+ get_unique_id(),
+ _mb_control,
+ get_tree());
+ } else if (std::stol(pid) == uhd::rfnoc::EMPTY_DB_PID) {
+ _daughterboard = std::make_shared<empty_slot_dboard_impl>();
+ set_num_output_ports(0);
+ set_num_input_ports(0);
+ } else {
+ RFNOC_LOG_WARNING("Skipping Daughterboard initialization for unsupported PID "
+ << "0x" << std::hex << std::stol(pid));
+ _daughterboard = std::make_shared<null_dboard_impl>();
+ return;
+ }
+
+ _init_prop_tree();
+
+ _rx_pwr_mgr = _daughterboard->get_pwr_mgr(uhd::RX_DIRECTION);
+ _tx_pwr_mgr = _daughterboard->get_pwr_mgr(uhd::TX_DIRECTION);
+
+ _tx_gain_profile_api = _daughterboard->get_tx_gain_profile_api();
+ _rx_gain_profile_api = _daughterboard->get_rx_gain_profile_api();
+
+ if (_daughterboard->is_adc_self_cal_supported()) {
+ _adc_self_calibration =
+ std::make_shared<uhd::features::adc_self_calibration>(_rpcc,
+ _rpc_prefix,
+ get_unique_id(),
+ get_block_id().get_block_count(),
+ _daughterboard);
+ register_feature(_adc_self_calibration);
+ }
+
+ _fpga_onload = std::make_shared<fpga_onload>(
+ get_num_output_ports(), _adc_self_calibration, get_unique_id());
+ register_feature(_fpga_onload);
+ _mb_control->_fpga_onload->request_cb(_fpga_onload);
+}
+
+void x400_radio_control_impl::_init_prop_tree()
+{
+ auto subtree = get_tree()->subtree(fs_path("mboard"));
+
+ for (size_t chan_idx = 0; chan_idx < get_num_output_ports(); chan_idx++) {
+ const fs_path rx_codec_path =
+ fs_path("rx_codec") / get_dboard_fe_from_chan(chan_idx, uhd::RX_DIRECTION);
+ const fs_path tx_codec_path =
+ fs_path("tx_codec") / get_dboard_fe_from_chan(chan_idx, uhd::TX_DIRECTION);
+ RFNOC_LOG_TRACE("Adding non-RFNoC block properties for channel "
+ << chan_idx << " to prop tree paths " << rx_codec_path << " and "
+ << tx_codec_path);
+
+ // ADC calibration state attributes
+ subtree->create<bool>(rx_codec_path / "calibration_frozen")
+ .add_coerced_subscriber([this, chan_idx](bool state) {
+ _rpcc->set_cal_frozen(state, get_block_id().get_block_count(), chan_idx);
+ })
+ .set_publisher([this, chan_idx]() {
+ const auto freeze_states =
+ _rpcc->get_cal_frozen(get_block_id().get_block_count(), chan_idx);
+ return freeze_states.at(0) == 1;
+ });
+
+ // RFDC NCO
+ // RX
+ subtree->create<double>(rx_codec_path / "rfdc" / "freq/value")
+ .add_desired_subscriber([this, chan_idx](double freq) {
+ _rpcc->rfdc_set_nco_freq(_get_trx_string(RX_DIRECTION),
+ get_block_id().get_block_count(),
+ chan_idx,
+ freq);
+ })
+ .set_publisher([this, chan_idx]() {
+ const auto nco_freq =
+ _rpcc->rfdc_get_nco_freq(_get_trx_string(RX_DIRECTION),
+ get_block_id().get_block_count(),
+ chan_idx);
+ return nco_freq;
+ });
+
+ // TX
+ subtree->create<double>(tx_codec_path / "rfdc" / "freq/value")
+ .add_desired_subscriber([this, chan_idx](double freq) {
+ _rpcc->rfdc_set_nco_freq(_get_trx_string(TX_DIRECTION),
+ get_block_id().get_block_count(),
+ chan_idx,
+ freq);
+ })
+ .set_publisher([this, chan_idx]() {
+ const auto nco_freq =
+ _rpcc->rfdc_get_nco_freq(_get_trx_string(TX_DIRECTION),
+ get_block_id().get_block_count(),
+ chan_idx);
+ return nco_freq;
+ });
+ }
+}
+
+void x400_radio_control_impl::_validate_master_clock_rate_args()
+{
+ auto block_args = get_block_args();
+
+ // 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.
+ const double master_clock_rate = _rpcc->get_master_clock_rate();
+ if (!uhd::math::frequencies_are_equal(get_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) % (get_rate() / 1e6)));
+ }
+ RFNOC_LOG_DEBUG("Master Clock Rate is: " << (master_clock_rate / 1e6) << " MHz.");
+}
+
+void x400_radio_control_impl::_init_mpm()
+{
+ // Init sensors
+ for (const auto& dir : std::vector<direction_t>{RX_DIRECTION, TX_DIRECTION}) {
+ // TODO: We should pull the number of channels from _daughterboard
+ for (size_t chan_idx = 0; chan_idx < uhd::usrp::zbx::ZBX_NUM_CHANS; chan_idx++) {
+ _init_mpm_sensors(dir, chan_idx);
+ }
+ }
+}
+
+// @TODO: This should be a method on direction_t
+// (or otherwise not duplicated from the implementation in zbx)
+std::string x400_radio_control_impl::_get_trx_string(const direction_t dir) const
+{
+ if (dir == RX_DIRECTION) {
+ return "rx";
+ } else if (dir == TX_DIRECTION) {
+ return "tx";
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+
+void x400_radio_control_impl::_init_mpm_sensors(
+ const direction_t dir, const size_t chan_idx)
+{
+ // TODO: We should pull the number of channels from _daughterboard
+ UHD_ASSERT_THROW(chan_idx < uhd::usrp::zbx::ZBX_NUM_CHANS);
+ const std::string trx = _get_trx_string(dir);
+ const fs_path fe_path = fs_path("dboard")
+ / (dir == RX_DIRECTION ? "rx_frontends" : "tx_frontends")
+ / chan_idx;
+ auto sensor_list = _db_rpcc->get_sensors(trx);
+ RFNOC_LOG_TRACE("Chan " << chan_idx << ": Found " << sensor_list.size() << " " << trx
+ << " sensors.");
+ for (const auto& sensor_name : sensor_list) {
+ RFNOC_LOG_TRACE("Adding " << trx << " sensor " << sensor_name);
+ get_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->_db_rpcc->get_sensor(trx, sensor_name, chan_idx));
+ });
+ }
+}
+
+fs_path x400_radio_control_impl::_get_db_fe_path(
+ const size_t chan, const direction_t dir) const
+{
+ const std::string trx = _get_trx_string(dir);
+ return DB_PATH / (trx + "_frontends") / get_dboard_fe_from_chan(chan, dir);
+}
+
+
+double x400_radio_control_impl::set_rate(const double rate)
+{
+ // X400 does not support runtime rate changes
+ if (!uhd::math::frequencies_are_equal(rate, get_rate())) {
+ RFNOC_LOG_WARNING("Requesting invalid sampling rate from device: "
+ << (rate / 1e6) << " MHz. Actual rate is: "
+ << (get_rate() / 1e6) << " MHz.");
+ }
+ return get_rate();
+}
+
+eeprom_map_t x400_radio_control_impl::get_db_eeprom()
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_db_eeprom();
+}
+
+std::vector<uhd::usrp::pwr_cal_mgr::sptr> x400_radio_control_impl::get_pwr_mgr(
+ const uhd::direction_t trx)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_pwr_mgr(trx);
+}
+
+std::string x400_radio_control_impl::get_tx_antenna(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_antenna(chan);
+}
+
+std::vector<std::string> x400_radio_control_impl::get_tx_antennas(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_antennas(chan);
+}
+
+void x400_radio_control_impl::set_tx_antenna(const std::string& ant, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_tx_antenna(ant, chan);
+}
+
+std::string x400_radio_control_impl::get_rx_antenna(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_antenna(chan);
+}
+
+std::vector<std::string> x400_radio_control_impl::get_rx_antennas(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_antennas(chan);
+}
+
+void x400_radio_control_impl::set_rx_antenna(const std::string& ant, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_rx_antenna(ant, chan);
+}
+
+double x400_radio_control_impl::get_tx_frequency(const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_frequency(chan);
+}
+
+double x400_radio_control_impl::set_tx_frequency(const double freq, size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_tx_frequency(freq, chan);
+}
+
+void x400_radio_control_impl::set_tx_tune_args(
+ const uhd::device_addr_t& args, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_tx_tune_args(args, chan);
+}
+
+uhd::freq_range_t x400_radio_control_impl::get_tx_frequency_range(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_frequency_range(chan);
+}
+
+double x400_radio_control_impl::get_rx_frequency(const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_frequency(chan);
+}
+
+double x400_radio_control_impl::set_rx_frequency(const double freq, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_rx_frequency(freq, chan);
+}
+
+void x400_radio_control_impl::set_rx_tune_args(
+ const uhd::device_addr_t& args, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_rx_tune_args(args, chan);
+}
+
+uhd::freq_range_t x400_radio_control_impl::get_rx_frequency_range(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_frequency_range(chan);
+}
+
+std::vector<std::string> x400_radio_control_impl::get_tx_gain_names(
+ const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_gain_names(chan);
+}
+
+uhd::gain_range_t x400_radio_control_impl::get_tx_gain_range(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_gain_range(chan);
+}
+
+uhd::gain_range_t x400_radio_control_impl::get_tx_gain_range(
+ const std::string& name, const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_gain_range(name, chan);
+}
+
+double x400_radio_control_impl::get_tx_gain(const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_gain(chan);
+}
+
+double x400_radio_control_impl::get_tx_gain(const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_gain(name, chan);
+}
+
+double x400_radio_control_impl::set_tx_gain(const double gain, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_tx_gain(gain, chan);
+}
+
+double x400_radio_control_impl::set_tx_gain(
+ const double gain, const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_tx_gain(gain, name, chan);
+}
+
+std::vector<std::string> x400_radio_control_impl::get_rx_gain_names(
+ const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_gain_names(chan);
+}
+
+uhd::gain_range_t x400_radio_control_impl::get_rx_gain_range(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_gain_range(chan);
+}
+
+uhd::gain_range_t x400_radio_control_impl::get_rx_gain_range(
+ const std::string& name, const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_gain_range(name, chan);
+}
+
+double x400_radio_control_impl::get_rx_gain(const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_gain(chan);
+}
+
+double x400_radio_control_impl::get_rx_gain(const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_gain(name, chan);
+}
+
+double x400_radio_control_impl::set_rx_gain(const double gain, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_rx_gain(gain, chan);
+}
+
+double x400_radio_control_impl::set_rx_gain(
+ const double gain, const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_rx_gain(gain, name, chan);
+}
+
+void x400_radio_control_impl::set_rx_agc(const bool enable, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_rx_agc(enable, chan);
+}
+
+meta_range_t x400_radio_control_impl::get_tx_bandwidth_range(size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_bandwidth_range(chan);
+}
+
+double x400_radio_control_impl::get_tx_bandwidth(const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_bandwidth(chan);
+}
+
+double x400_radio_control_impl::set_tx_bandwidth(
+ const double bandwidth, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_tx_bandwidth(bandwidth, chan);
+}
+
+meta_range_t x400_radio_control_impl::get_rx_bandwidth_range(size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_bandwidth_range(chan);
+}
+
+double x400_radio_control_impl::get_rx_bandwidth(const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_bandwidth(chan);
+}
+
+double x400_radio_control_impl::set_rx_bandwidth(
+ const double bandwidth, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_rx_bandwidth(bandwidth, chan);
+}
+
+std::vector<std::string> x400_radio_control_impl::get_rx_lo_names(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_lo_names(chan);
+}
+
+std::vector<std::string> x400_radio_control_impl::get_rx_lo_sources(
+ const std::string& name, const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_lo_sources(name, chan);
+}
+
+freq_range_t x400_radio_control_impl::get_rx_lo_freq_range(
+ const std::string& name, const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_lo_freq_range(name, chan);
+}
+
+void x400_radio_control_impl::set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_rx_lo_source(src, name, chan);
+}
+
+const std::string x400_radio_control_impl::get_rx_lo_source(
+ const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_lo_source(name, chan);
+}
+
+void x400_radio_control_impl::set_rx_lo_export_enabled(
+ bool enabled, const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_rx_lo_export_enabled(enabled, name, chan);
+}
+
+bool x400_radio_control_impl::get_rx_lo_export_enabled(
+ const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_lo_export_enabled(name, chan);
+}
+
+double x400_radio_control_impl::set_rx_lo_freq(
+ double freq, const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_rx_lo_freq(freq, name, chan);
+}
+
+double x400_radio_control_impl::get_rx_lo_freq(const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_rx_lo_freq(name, chan);
+}
+
+std::vector<std::string> x400_radio_control_impl::get_tx_lo_names(const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_lo_names(chan);
+}
+
+std::vector<std::string> x400_radio_control_impl::get_tx_lo_sources(
+ const std::string& name, const size_t chan) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_lo_sources(name, chan);
+}
+
+freq_range_t x400_radio_control_impl::get_tx_lo_freq_range(
+ const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_lo_freq_range(name, chan);
+}
+
+void x400_radio_control_impl::set_tx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_tx_lo_source(src, name, chan);
+}
+
+const std::string x400_radio_control_impl::get_tx_lo_source(
+ const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_lo_source(name, chan);
+}
+
+void x400_radio_control_impl::set_tx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ _daughterboard->set_tx_lo_export_enabled(enabled, name, chan);
+}
+
+bool x400_radio_control_impl::get_tx_lo_export_enabled(
+ const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_lo_export_enabled(name, chan);
+}
+
+double x400_radio_control_impl::set_tx_lo_freq(
+ const double freq, const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->set_tx_lo_freq(freq, name, chan);
+}
+
+double x400_radio_control_impl::get_tx_lo_freq(const std::string& name, const size_t chan)
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_tx_lo_freq(name, chan);
+}
+
+void x400_radio_control_impl::set_command_time(uhd::time_spec_t time, const size_t chan)
+{
+ node_t::set_command_time(time, chan);
+ _daughterboard->set_command_time(time, chan);
+}
+
+/**************************************************************************
+ * Sensor API
+ *************************************************************************/
+std::vector<std::string> x400_radio_control_impl::get_rx_sensor_names(
+ const size_t chan) const
+{
+ const fs_path sensor_path = _get_db_fe_path(chan, RX_DIRECTION) / "sensors";
+ if (get_tree()->exists(sensor_path)) {
+ return get_tree()->list(sensor_path);
+ }
+ return {};
+}
+
+uhd::sensor_value_t x400_radio_control_impl::get_rx_sensor(
+ const std::string& name, const size_t chan)
+{
+ return get_tree()
+ ->access<uhd::sensor_value_t>(
+ _get_db_fe_path(chan, RX_DIRECTION) / "sensors" / name)
+ .get();
+}
+
+std::vector<std::string> x400_radio_control_impl::get_tx_sensor_names(
+ const size_t chan) const
+{
+ const fs_path sensor_path = _get_db_fe_path(chan, TX_DIRECTION) / "sensors";
+ if (get_tree()->exists(sensor_path)) {
+ return get_tree()->list(sensor_path);
+ }
+ return {};
+}
+
+uhd::sensor_value_t x400_radio_control_impl::get_tx_sensor(
+ const std::string& name, const size_t chan)
+{
+ return get_tree()
+ ->access<uhd::sensor_value_t>(
+ _get_db_fe_path(chan, TX_DIRECTION) / "sensors" / name)
+ .get();
+}
+
+/**************************************************************************
+ * Radio Identification API Calls
+ *************************************************************************/
+size_t x400_radio_control_impl::get_chan_from_dboard_fe(
+ const std::string& fe, const uhd::direction_t direction) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_chan_from_dboard_fe(fe, direction);
+}
+
+std::string x400_radio_control_impl::get_dboard_fe_from_chan(
+ const size_t chan, const uhd::direction_t direction) const
+{
+ std::lock_guard<std::recursive_mutex> l(_lock);
+ return _daughterboard->get_dboard_fe_from_chan(chan, direction);
+}
+
+UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT(
+ x400_radio_control, RADIO_BLOCK, X400, "Radio", true, "radio_clk", "ctrl_clk")
+
+}} // namespace uhd::rfnoc
diff --git a/host/lib/usrp/x400/x400_radio_control.hpp b/host/lib/usrp/x400/x400_radio_control.hpp
new file mode 100644
index 000000000..65b37cc2b
--- /dev/null
+++ b/host/lib/usrp/x400/x400_radio_control.hpp
@@ -0,0 +1,198 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include "adc_self_calibration.hpp"
+#include <uhd/rfnoc/noc_block_base.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhdlib/features/fpga_load_notification_iface.hpp>
+#include <uhdlib/rfnoc/radio_control_impl.hpp>
+#include <uhdlib/rfnoc/rf_control/dboard_iface.hpp>
+#include <uhdlib/usrp/common/mpmd_mb_controller.hpp>
+#include <uhdlib/usrp/common/rpc.hpp>
+#include <uhdlib/usrp/common/x400_rfdc_control.hpp>
+#include <uhdlib/utils/rpc.hpp>
+#include <stddef.h>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace uhd { namespace rfnoc {
+
+namespace x400_regs {
+
+//! Base address for the rf_timing_control module. This controls the NCOs and
+// other things in the RFDC.
+constexpr uint32_t RFDC_CTRL_BASE = radio_control_impl::regmap::PERIPH_BASE + 0x8000;
+
+} // namespace x400_regs
+
+class x400_radio_control_impl : public radio_control_impl
+{
+public:
+ using sptr = std::shared_ptr<x400_radio_control_impl>;
+
+ /************************************************************************
+ * Structors
+ ***********************************************************************/
+ x400_radio_control_impl(make_args_ptr make_args);
+ virtual ~x400_radio_control_impl() = default;
+
+ std::string get_slot_name() const override
+ {
+ return _radio_slot;
+ }
+
+ size_t get_chan_from_dboard_fe(const std::string&, uhd::direction_t) const override;
+ std::string get_dboard_fe_from_chan(size_t chan, uhd::direction_t) const override;
+
+ uhd::eeprom_map_t get_db_eeprom() override;
+
+ // Shim calls for every method in rf_control_core
+ double set_rate(const double rate) override;
+ std::string get_tx_antenna(const size_t chan) const override;
+ std::vector<std::string> get_tx_antennas(const size_t chan) const override;
+ void set_tx_antenna(const std::string& ant, const size_t chan) override;
+ std::string get_rx_antenna(const size_t chan) const override;
+ std::vector<std::string> get_rx_antennas(const size_t chan) const override;
+ void set_rx_antenna(const std::string& ant, const size_t chan) override;
+ double get_tx_frequency(const size_t chan) override;
+ double set_tx_frequency(const double freq, size_t chan) override;
+ void set_tx_tune_args(const uhd::device_addr_t& args, const size_t chan) override;
+ uhd::freq_range_t get_tx_frequency_range(const size_t chan) const override;
+ double get_rx_frequency(const size_t chan) override;
+ double set_rx_frequency(const double freq, const size_t chan) override;
+ void set_rx_tune_args(const uhd::device_addr_t& args, const size_t chan) override;
+ uhd::freq_range_t get_rx_frequency_range(const size_t chan) const override;
+ std::vector<std::string> get_tx_gain_names(const size_t chan) const override;
+ uhd::gain_range_t get_tx_gain_range(const size_t chan) const override;
+ uhd::gain_range_t get_tx_gain_range(
+ const std::string& name, const size_t chan) const override;
+ double get_tx_gain(const size_t chan) override;
+ double get_tx_gain(const std::string& name, const size_t chan) override;
+ double set_tx_gain(const double gain, const size_t chan) override;
+ double set_tx_gain(
+ const double gain, const std::string& name, const size_t chan) override;
+ std::vector<std::string> get_rx_gain_names(const size_t chan) const override;
+ uhd::gain_range_t get_rx_gain_range(const size_t chan) const override;
+ uhd::gain_range_t get_rx_gain_range(
+ const std::string& name, const size_t chan) const override;
+ double get_rx_gain(const size_t chan) override;
+ double get_rx_gain(const std::string& name, const size_t chan) override;
+ double set_rx_gain(const double gain, const size_t chan) override;
+ double set_rx_gain(
+ const double gain, const std::string& name, const size_t chan) override;
+ void set_rx_agc(const bool enable, const size_t chan) override;
+ meta_range_t get_tx_bandwidth_range(size_t chan) const override;
+ double get_tx_bandwidth(const size_t chan) override;
+ double set_tx_bandwidth(const double bandwidth, const size_t chan) override;
+ meta_range_t get_rx_bandwidth_range(size_t chan) const override;
+ double get_rx_bandwidth(const size_t chan) override;
+ double set_rx_bandwidth(const double bandwidth, const size_t chan) override;
+
+ std::vector<std::string> get_rx_lo_names(const size_t chan) const override;
+ std::vector<std::string> get_rx_lo_sources(
+ const std::string& name, const size_t chan) const override;
+ freq_range_t get_rx_lo_freq_range(
+ const std::string& name, const size_t chan) const override;
+ void set_rx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan) override;
+ const std::string get_rx_lo_source(
+ const std::string& name, const size_t chan) override;
+ void set_rx_lo_export_enabled(
+ bool enabled, const std::string& name, const size_t chan) override;
+ bool get_rx_lo_export_enabled(
+ const std::string& name, const size_t chan) override;
+ double set_rx_lo_freq(
+ double freq, const std::string& name, const size_t chan) override;
+ double get_rx_lo_freq(const std::string& name, const size_t chan) override;
+ std::vector<std::string> get_tx_lo_names(const size_t chan) const override;
+ std::vector<std::string> get_tx_lo_sources(
+ const std::string& name, const size_t chan) const override;
+ freq_range_t get_tx_lo_freq_range(
+ const std::string& name, const size_t chan) override;
+ void set_tx_lo_source(
+ const std::string& src, const std::string& name, const size_t chan) override;
+ const std::string get_tx_lo_source(
+ const std::string& name, const size_t chan) override;
+ void set_tx_lo_export_enabled(
+ const bool enabled, const std::string& name, const size_t chan) override;
+ bool get_tx_lo_export_enabled(const std::string& name, const size_t chan) override;
+ double set_tx_lo_freq(
+ const double freq, const std::string& name, const size_t chan) override;
+ double get_tx_lo_freq(const std::string& name, const size_t chan) override;
+ std::vector<std::string> get_rx_sensor_names(size_t chan) const override;
+ uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan) override;
+ std::vector<std::string> get_tx_sensor_names(size_t chan) const override;
+ uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan) override;
+
+ void set_command_time(uhd::time_spec_t time, const size_t chan) override;
+
+ // Non-API methods
+ // This is used for x4xx radio block unit tests
+ std::vector<uhd::usrp::pwr_cal_mgr::sptr> get_pwr_mgr(const uhd::direction_t trx);
+
+private:
+ //! Locks access to the API
+ mutable std::recursive_mutex _lock;
+
+ std::string _get_trx_string(const direction_t dir) const;
+
+ void _validate_master_clock_rate_args();
+ void _init_mpm();
+ void _init_mpm_sensors(const direction_t dir, const size_t chan_idx);
+ void _init_prop_tree();
+ fs_path _get_db_fe_path(const size_t chan, const uhd::direction_t dir) const;
+
+ //! Reference to the MB controller
+ uhd::rfnoc::mpmd_mb_controller::sptr _mb_control;
+
+ //! Reference to the RPC client
+ uhd::usrp::x400_rpc_iface::sptr _rpcc;
+ uhd::usrp::dboard_base_rpc_iface::sptr _db_rpcc;
+
+ //! Reference to the MB timekeeper
+ uhd::rfnoc::mpmd_mb_controller::mpmd_timekeeper::sptr _x4xx_timekeeper;
+
+ std::string _radio_slot;
+ std::string _rpc_prefix;
+
+ //! Reference to this radio block's RFDC control
+ x400::rfdc_control::sptr _rfdcc;
+
+ uhd::usrp::x400::x400_dboard_iface::sptr _daughterboard;
+
+ uhd::features::adc_self_calibration_iface::sptr _adc_self_calibration;
+
+ class fpga_onload : public uhd::features::fpga_load_notification_iface
+ {
+ public:
+ using sptr = std::shared_ptr<fpga_onload>;
+
+ fpga_onload(size_t num_channels,
+ uhd::features::adc_self_calibration_iface::sptr adc_self_cal,
+ std::string unique_id);
+
+ void onload() override;
+
+ private:
+ const size_t _num_channels;
+ uhd::features::adc_self_calibration_iface::sptr _adc_self_cal;
+ const std::string _unique_id;
+ std::string get_unique_id() const
+ {
+ return _unique_id;
+ }
+ };
+
+ fpga_onload::sptr _fpga_onload;
+};
+
+}} // namespace uhd::rfnoc
diff --git a/host/lib/usrp/x400/x400_rfdc_control.cpp b/host/lib/usrp/x400/x400_rfdc_control.cpp
new file mode 100644
index 000000000..b963c0a46
--- /dev/null
+++ b/host/lib/usrp/x400/x400_rfdc_control.cpp
@@ -0,0 +1,73 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#include <uhd/utils/log.hpp>
+#include <uhdlib/usrp/common/x400_rfdc_control.hpp>
+#include <unordered_map>
+
+using namespace uhd::rfnoc::x400;
+
+rfdc_control::rfdc_control(uhd::memmap32_iface_timed&& iface, const std::string& log_id)
+ : _iface(std::move(iface)), _log_id(log_id)
+{
+ // nop
+}
+
+void rfdc_control::reset_ncos(
+ const std::vector<rfdc_type>& ncos, const uhd::time_spec_t& time)
+{
+ if (ncos.empty()) {
+ UHD_LOG_WARNING(_log_id,
+ "reset_ncos() called with empty NCO list! "
+ "Not resetting NCOs.");
+ return;
+ }
+ UHD_LOG_TRACE(_log_id, "Resetting " << ncos.size() << " NCOs...");
+
+ const uint32_t reset_word = 1;
+ // TODO: When the FPGA supports it, map the list of ncos into bits onto
+ // reset_word to set the bit for the specific NCOs that will be reset.
+
+ _iface.poke32(regmap::NCO_RESET, reset_word, time);
+}
+
+void rfdc_control::reset_gearboxes(
+ const std::vector<rfdc_type>& gearboxes, const uhd::time_spec_t& time)
+{
+ if (gearboxes.empty()) {
+ UHD_LOG_WARNING(_log_id,
+ "reset_gearboxes() called with empty gearbox list! "
+ "Not resetting gearboxes.");
+ return;
+ }
+ // This is intentionally at INFO: It should typically happen once per session.
+ UHD_LOG_INFO(_log_id, "Resetting " << gearboxes.size() << " gearbox(es)...");
+ // TODO: Either the FPGA supports resetting gearboxes individually, or we
+ // remove this TODO
+ static const std::unordered_map<rfdc_type, const uint32_t> gb_map{
+ {rfdc_type::TX0, regmap::DAC_RESET_MSB},
+ {rfdc_type::TX1, regmap::DAC_RESET_MSB},
+ {rfdc_type::RX0, regmap::ADC_RESET_MSB},
+ {rfdc_type::RX1, regmap::ADC_RESET_MSB}};
+
+ uint32_t reset_word = 0;
+ for (const auto gb : gearboxes) {
+ reset_word = reset_word | (1 << gb_map.at(gb));
+ }
+
+ _iface.poke32(regmap::GEARBOX_RESET, reset_word, time);
+}
+
+bool rfdc_control::get_nco_reset_done()
+{
+ return bool(_iface.peek32(regmap::NCO_RESET) & (1 << regmap::NCO_RESET_DONE_MSB));
+}
+
+double rfdc_control::set_nco_freq(const rfdc_type, const double freq)
+{
+ UHD_LOG_WARNING(_log_id, "set_nco_freq() called but not yet implemented!");
+ return freq;
+}