diff options
Diffstat (limited to 'host/lib/usrp/x400')
-rw-r--r-- | host/lib/usrp/x400/CMakeLists.txt | 20 | ||||
-rw-r--r-- | host/lib/usrp/x400/adc_self_calibration.cpp | 196 | ||||
-rw-r--r-- | host/lib/usrp/x400/adc_self_calibration.hpp | 46 | ||||
-rw-r--r-- | host/lib/usrp/x400/x400_radio_control.cpp | 752 | ||||
-rw-r--r-- | host/lib/usrp/x400/x400_radio_control.hpp | 198 | ||||
-rw-r--r-- | host/lib/usrp/x400/x400_rfdc_control.cpp | 73 |
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; +} |