mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
This adds support for the new USRP X420. It is the latest addition to our X4xx series and comes with two HBX daughterboards. The HBX is a one-channel direct conversion transceiver that covers 5G NR FR3 and X-/KU-Band needs for Research and Prototyping in a frequency range of 10 MHz up to 20 GHz. It supports analog bandwidths of up to 1 GHz. This commit is the UHD/host portion of the X420 support. Co-authored-by: Martin Anderseck <martin.anderseck@emerson.com> Co-authored-by: Ahmed Salman <ahmed.salman@emerson.com> Co-authored-by: Max Köhler <max.koehler@emerson.com> Co-authored-by: Markus Schill <markus.schill@emerson.com>
965 lines
36 KiB
C++
965 lines
36 KiB
C++
//
|
|
// Copyright 2020 Ettus Research, a National Instruments Brand
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
#include "x400_radio_control.hpp"
|
|
#include "x400_gpio_control.hpp"
|
|
#include "x400_internal_sync.hpp"
|
|
#include <uhd/rfnoc/registry.hpp>
|
|
#include <uhd/types/serial.hpp>
|
|
#include <uhd/utils/log.hpp>
|
|
#include <uhd/utils/math.hpp>
|
|
#include <uhdlib/rfnoc/reg_iface_adapter.hpp>
|
|
#include <uhdlib/usrp/common/x400_rfdc_control.hpp>
|
|
#include <uhdlib/usrp/common/x4xx_ch_modes.hpp>
|
|
#include <uhdlib/usrp/cores/spi_core_4000.hpp>
|
|
#include <uhdlib/usrp/dboard/debug_dboard.hpp>
|
|
#include <uhdlib/usrp/dboard/fbx/fbx_dboard.hpp>
|
|
#include <uhdlib/usrp/dboard/hbx/hbx_dboard.hpp>
|
|
#include <uhdlib/usrp/dboard/null_dboard.hpp>
|
|
#include <uhdlib/usrp/dboard/zbx/zbx_dboard.hpp>
|
|
#include <uhdlib/utils/prefs.hpp>
|
|
#include <future>
|
|
|
|
using uhd::usrp::x400::ch_mode;
|
|
namespace uhd { namespace rfnoc {
|
|
|
|
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());
|
|
UHD_ASSERT_THROW(_mb_control)
|
|
|
|
RFNOC_LOG_TRACE("num timekeepers: " << _mb_control->get_num_timekeepers()
|
|
<< "radio slot: " << _radio_slot);
|
|
// select time keeper based on block id but restrict to number of time keepers
|
|
// available
|
|
const size_t tk_idx =
|
|
_mb_control->get_num_timekeepers() >= 2 ? get_block_id().get_block_count() : 0;
|
|
_x4xx_timekeeper = std::dynamic_pointer_cast<mpmd_mb_controller::mpmd_timekeeper>(
|
|
_mb_control->get_timekeeper(tk_idx));
|
|
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;
|
|
}
|
|
|
|
// Note: mpmd passed the device args to MPM earlier, so if the user provided
|
|
// a master_clock_rate key, then MPM will have used that to initialize the
|
|
// master clock rate.
|
|
const double master_clock_rate = _db_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::usrp::fbx::FBX_PID) {
|
|
auto fbx_rpc_sptr = _mb_control->dynamic_cast_rpc_as<uhd::usrp::fbx_rpc_iface>();
|
|
if (!fbx_rpc_sptr) {
|
|
fbx_rpc_sptr = std::make_shared<uhd::usrp::fbx_rpc>(
|
|
_mb_control->get_rpc_client(), _rpc_prefix);
|
|
}
|
|
_daughterboard = std::make_shared<uhd::usrp::fbx::fbx_dboard_impl>(
|
|
regs(),
|
|
regmap::PERIPH_BASE,
|
|
[this](const size_t instance) { return get_command_time(instance); },
|
|
get_block_id().get_block_count(),
|
|
_radio_slot,
|
|
get_num_input_ports(),
|
|
get_num_output_ports(),
|
|
_rpc_prefix,
|
|
get_unique_id(),
|
|
_rpcc,
|
|
fbx_rpc_sptr,
|
|
_rfdcc,
|
|
get_tree());
|
|
|
|
auto fbx_dboard =
|
|
std::dynamic_pointer_cast<uhd::usrp::fbx::fbx_dboard_impl>(_daughterboard);
|
|
if (fbx_dboard != NULL) {
|
|
RFNOC_LOG_DEBUG("Registering internal sync feature")
|
|
auto int_sync = std::make_shared<uhd::features::internal_sync>(
|
|
uhd::features::internal_sync(fbx_dboard->get_fbx_ctrl()));
|
|
register_feature(int_sync);
|
|
}
|
|
} else if (std::stol(pid) == uhd::usrp::hbx::HBX_PID) {
|
|
const auto args = get_block_args();
|
|
bool ignore_cal_file = false;
|
|
if (args.has_key("ignore-cal-file")) {
|
|
ignore_cal_file = args.get("ignore-cal-file") == "1";
|
|
}
|
|
if (ignore_cal_file) {
|
|
RFNOC_LOG_WARNING("Ignoring IQ correction and Power calibration files.");
|
|
}
|
|
|
|
auto hbx_rpc_sptr = _mb_control->dynamic_cast_rpc_as<uhd::usrp::hbx_rpc_iface>();
|
|
if (!hbx_rpc_sptr) {
|
|
hbx_rpc_sptr = std::make_shared<uhd::usrp::hbx_rpc>(
|
|
_mb_control->get_rpc_client(), _rpc_prefix);
|
|
}
|
|
_daughterboard = std::make_shared<uhd::usrp::hbx::hbx_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,
|
|
hbx_rpc_sptr,
|
|
_rfdcc,
|
|
get_tree(),
|
|
ignore_cal_file,
|
|
master_clock_rate);
|
|
|
|
auto hbx_dboard =
|
|
std::dynamic_pointer_cast<uhd::usrp::hbx::hbx_dboard_impl>(_daughterboard);
|
|
if (hbx_dboard != NULL) {
|
|
RFNOC_LOG_DEBUG("Registering internal sync feature")
|
|
auto int_sync = std::make_shared<uhd::features::internal_sync>(
|
|
uhd::features::internal_sync(hbx_dboard->get_hbx_cpld_ctrl()));
|
|
register_feature(int_sync);
|
|
}
|
|
} 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);
|
|
}
|
|
|
|
auto mpm_rpc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::mpmd_rpc_iface>();
|
|
if (mpm_rpc->get_gpio_banks().size() > 0) {
|
|
_gpios = std::make_shared<x400::gpio_control>(
|
|
_rpcc, _mb_control, RFNOC_MAKE_WB_IFACE(regmap::PERIPH_BASE + 0xC000, 0));
|
|
|
|
// Check if SPI is available as GPIO source, otherwise don't register
|
|
// SPI_GETTER_IFace
|
|
auto gpio_srcs = _mb_control->get_gpio_srcs("GPIO0");
|
|
if (std::count(gpio_srcs.begin(), gpio_srcs.end(), "DB0_SPI") > 0) {
|
|
auto spicore = uhd::cores::spi_core_4000::make(
|
|
[this](const uint32_t addr, const uint32_t data) {
|
|
regs().poke32(addr, data, get_command_time(0));
|
|
},
|
|
[this](const uint32_t addr) {
|
|
return regs().peek32(addr, get_command_time(0));
|
|
},
|
|
x400_regs::SPI_PERIPH_CFG,
|
|
x400_regs::SPI_TRANSACTION_CFG_REG,
|
|
x400_regs::SPI_TRANSACTION_GO_REG,
|
|
x400_regs::SPI_STATUS_REG,
|
|
x400_regs::SPI_CONTROLLER_INFO_REG);
|
|
|
|
_spi_getter_iface = std::make_shared<x400_spi_getter>(spicore);
|
|
register_feature(_spi_getter_iface);
|
|
} else {
|
|
UHD_LOG_INFO("x400_radio_control",
|
|
"SPI functionality not available in this FPGA image. Please update to at "
|
|
"least version 7.7 to use SPI.");
|
|
}
|
|
}
|
|
|
|
uhd::rfnoc::mb_controller::sync_source_updater_t self_cal_runner =
|
|
[&, pid](const uhd::rfnoc::mb_controller::sync_source_t&) {
|
|
auto mpm_rpc = _mb_control->dynamic_cast_rpc_as<uhd::usrp::mpmd_rpc_iface>();
|
|
auto self_cal_req = mpm_rpc->pop_host_tasks(
|
|
"db" + std::to_string(get_block_id().get_block_count()) + "_ADCSelfCal");
|
|
if (self_cal_req.size() > 0) {
|
|
if (has_feature<uhd::features::adc_self_calibration_iface>()) {
|
|
RFNOC_LOG_INFO("Clocking reconfigured, running ADC Self Cal on DB"
|
|
<< get_block_id().get_block_count() << "...");
|
|
const auto args = get_block_args();
|
|
const std::string ch_list = args.get("cal_ch_list", "");
|
|
size_t num_calibrations = 0;
|
|
auto& self_cal =
|
|
get_feature<uhd::features::adc_self_calibration_iface>();
|
|
const size_t num_channels = get_num_output_ports();
|
|
// Get current setting for guided mode and disable it for the duration
|
|
// of the calibration
|
|
|
|
uhd::prefs::suspend_guided_mode();
|
|
auto resume_guided_mode = uhd::utils::scope_exit::make(
|
|
[&]() { uhd::prefs::resume_guided_mode(); });
|
|
|
|
std::vector<std::future<void>> cal_futures;
|
|
for (size_t i = 0; i < num_channels; i++) {
|
|
auto abs_ch = get_block_id().get_block_count() * num_channels + i;
|
|
if (ch_list.size() == 0
|
|
or ch_list.find(std::to_string(abs_ch))
|
|
!= std::string::npos) {
|
|
// Run self-cal in parallel on FBX daughterboards
|
|
if (std::stol(pid) == uhd::usrp::fbx::FBX_PID) {
|
|
cal_futures.push_back(std::async(std::launch::async,
|
|
[&self_cal, i, args]() { self_cal.run(i, args); }));
|
|
} else {
|
|
RFNOC_LOG_INFO("Calibrating channel " << abs_ch << "...");
|
|
self_cal.run(i, args);
|
|
}
|
|
num_calibrations++;
|
|
}
|
|
}
|
|
// Wait for all channels to complete on FBX daughterboards
|
|
if (std::stol(pid) == uhd::usrp::fbx::FBX_PID) {
|
|
for (auto& f : cal_futures) {
|
|
f.get();
|
|
}
|
|
}
|
|
if (num_calibrations > 0) {
|
|
RFNOC_LOG_INFO(
|
|
"Calibrated "
|
|
<< num_calibrations
|
|
<< (num_calibrations > 1 ? " channels." : " channel."));
|
|
} else {
|
|
RFNOC_LOG_WARNING("Did not find any channels to calibrate!");
|
|
}
|
|
} else {
|
|
RFNOC_LOG_WARNING(
|
|
"Clocking reconfigured, ADC Self Cal requested for DB"
|
|
<< get_block_id().get_block_count()
|
|
<< " but ADC Self Cal feature not available.");
|
|
}
|
|
}
|
|
};
|
|
|
|
self_cal_runner("");
|
|
get_mb_controller()->register_sync_source_updater(self_cal_runner);
|
|
}
|
|
|
|
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,
|
|
size_t(ch_mode::ALL));
|
|
})
|
|
.set_publisher([this, chan_idx]() {
|
|
const auto freeze_states = _rpcc->get_cal_frozen(
|
|
get_block_id().get_block_count(), chan_idx, size_t(ch_mode::ALL));
|
|
return freeze_states.at(0) == 1;
|
|
});
|
|
|
|
// RFDC NCO
|
|
// The NCO is typically only used for converters used in Real mode, therefore we
|
|
// expose it on the FE path that corresponds to the current channel in Real mode.
|
|
// 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,
|
|
static_cast<size_t>(ch_mode::REAL));
|
|
})
|
|
.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,
|
|
static_cast<size_t>(ch_mode::REAL));
|
|
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,
|
|
static_cast<size_t>(ch_mode::REAL));
|
|
})
|
|
.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,
|
|
static_cast<size_t>(ch_mode::REAL));
|
|
return nco_freq;
|
|
});
|
|
}
|
|
}
|
|
|
|
void x400_radio_control_impl::_validate_master_clock_rate_args()
|
|
{
|
|
// auto block_args = get_block_args(); // FIXME use the block args!
|
|
// Comparing master_clock_rate with get_rate() further down is utterly
|
|
// pointless, because get_rate() gets initialized from the DB RPC call
|
|
// that returns us the MCR. What we need to do is to parse the block args,
|
|
// check if there was a master_clock_rate key in there, and then compare
|
|
// those two. FIXME resolve this.
|
|
|
|
// Note: MCR gets set during the init() call (prior to this), which takes
|
|
// in arguments from the device args. So if the block args contain a
|
|
// master_clock_rate key, then it should better be whatever the device is
|
|
// configured to do.
|
|
const double master_clock_rate = _db_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}) {
|
|
const size_t num_chans = (dir == RX_DIRECTION) ? get_num_input_ports()
|
|
: get_num_output_ports();
|
|
for (size_t chan_idx = 0; chan_idx < 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)
|
|
{
|
|
UHD_ASSERT_THROW(dir == RX_DIRECTION || dir == TX_DIRECTION);
|
|
const size_t num_chans = (dir == RX_DIRECTION) ? get_num_input_ports()
|
|
: get_num_output_ports();
|
|
UHD_ASSERT_THROW(chan_idx < 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();
|
|
}
|
|
|
|
std::vector<std::string> x400_radio_control_impl::get_gpio_banks() const
|
|
{
|
|
if (!_gpios) {
|
|
return {};
|
|
}
|
|
return {x400::GPIO_BANK_NAME};
|
|
}
|
|
|
|
uint32_t x400_radio_control_impl::get_gpio_attr(
|
|
const std::string& bank, const std::string& attr)
|
|
{
|
|
if (!_gpios) {
|
|
throw uhd::runtime_error("X410 does not have sufficient GPIO support!");
|
|
}
|
|
std::lock_guard<std::recursive_mutex> l(_lock);
|
|
if (bank != x400::GPIO_BANK_NAME) {
|
|
throw uhd::key_error("Invalid GPIO bank " + bank);
|
|
}
|
|
if (usrp::gpio_atr::gpio_attr_rev_map.count(attr) == 0) {
|
|
throw uhd::key_error("Invalid GPIO attribute " + attr);
|
|
}
|
|
return _gpios->get_gpio_attr(usrp::gpio_atr::gpio_attr_rev_map.at(attr));
|
|
}
|
|
|
|
void x400_radio_control_impl::set_gpio_attr(
|
|
const std::string& bank, const std::string& attr, const uint32_t value)
|
|
{
|
|
if (!_gpios) {
|
|
throw uhd::runtime_error("X410 does not have sufficient GPIO support!");
|
|
}
|
|
std::lock_guard<std::recursive_mutex> l(_lock);
|
|
if (bank != x400::GPIO_BANK_NAME) {
|
|
throw uhd::key_error("Invalid GPIO bank " + bank);
|
|
}
|
|
if (usrp::gpio_atr::gpio_attr_rev_map.count(attr) == 0) {
|
|
throw uhd::key_error("Invalid GPIO attribute " + attr);
|
|
}
|
|
_gpios->set_gpio_attr(usrp::gpio_atr::gpio_attr_rev_map.at(attr), value);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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, CLOCK_KEY_AUTO, CLOCK_KEY_AUTO)
|
|
}} // namespace uhd::rfnoc
|