diff options
-rw-r--r-- | host/lib/usrp/dboard/db_ubx.cpp | 18 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_ubx.hpp | 57 | ||||
-rw-r--r-- | host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp | 51 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_device_args.hpp | 7 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 62 |
5 files changed, 153 insertions, 42 deletions
diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp index 8ac96a2fe..733a4b8e2 100644 --- a/host/lib/usrp/dboard/db_ubx.cpp +++ b/host/lib/usrp/dboard/db_ubx.cpp @@ -7,6 +7,7 @@ /*********************************************************************** * Included Files and Libraries **********************************************************************/ +#include "db_ubx.hpp" #include <uhd/types/device_addr.hpp> #include <uhd/types/dict.hpp> #include <uhd/types/direction.hpp> @@ -30,6 +31,7 @@ using namespace uhd; using namespace uhd::usrp; +using namespace uhd::usrp::dboard::ubx; /*********************************************************************** * UBX Data Structures @@ -134,22 +136,6 @@ enum spi_dest_t { * UBX Constants **********************************************************************/ #define fMHz (1000000.0) -static const dboard_id_t UBX_PROTO_V3_TX_ID(0x73); -static const dboard_id_t UBX_PROTO_V3_RX_ID(0x74); -static const dboard_id_t UBX_PROTO_V4_TX_ID(0x75); -static const dboard_id_t UBX_PROTO_V4_RX_ID(0x76); -static const dboard_id_t UBX_V1_40MHZ_TX_ID(0x77); -static const dboard_id_t UBX_V1_40MHZ_RX_ID(0x78); -static const dboard_id_t UBX_V1_160MHZ_TX_ID(0x79); -static const dboard_id_t UBX_V1_160MHZ_RX_ID(0x7A); -static const dboard_id_t UBX_V2_40MHZ_TX_ID(0x7B); -static const dboard_id_t UBX_V2_40MHZ_RX_ID(0x7C); -static const dboard_id_t UBX_V2_160MHZ_TX_ID(0x7D); -static const dboard_id_t UBX_V2_160MHZ_RX_ID(0x7E); -static const dboard_id_t UBX_LP_160MHZ_TX_ID(0x0200); -static const dboard_id_t UBX_LP_160MHZ_RX_ID(0x0201); -static const dboard_id_t UBX_TDD_160MHZ_TX_ID(0x0202); -static const dboard_id_t UBX_TDD_160MHZ_RX_ID(0x0203); static const freq_range_t ubx_freq_range(10e6, 6.0e9); static const gain_range_t ubx_tx_gain_range(0, 31.5, double(0.5)); static const gain_range_t ubx_rx_gain_range(0, 31.5, double(0.5)); diff --git a/host/lib/usrp/dboard/db_ubx.hpp b/host/lib/usrp/dboard/db_ubx.hpp new file mode 100644 index 000000000..246a89eb0 --- /dev/null +++ b/host/lib/usrp/dboard/db_ubx.hpp @@ -0,0 +1,57 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <uhd/usrp/dboard_base.hpp> + +using namespace uhd; +using namespace uhd::usrp; + +namespace uhd { namespace usrp { namespace dboard { namespace ubx { + +static const dboard_id_t UBX_PROTO_V3_TX_ID(0x73); +static const dboard_id_t UBX_PROTO_V3_RX_ID(0x74); +static const dboard_id_t UBX_PROTO_V4_TX_ID(0x75); +static const dboard_id_t UBX_PROTO_V4_RX_ID(0x76); +static const dboard_id_t UBX_V1_40MHZ_TX_ID(0x77); +static const dboard_id_t UBX_V1_40MHZ_RX_ID(0x78); +static const dboard_id_t UBX_V1_160MHZ_TX_ID(0x79); +static const dboard_id_t UBX_V1_160MHZ_RX_ID(0x7A); +static const dboard_id_t UBX_V2_40MHZ_TX_ID(0x7B); +static const dboard_id_t UBX_V2_40MHZ_RX_ID(0x7C); +static const dboard_id_t UBX_V2_160MHZ_TX_ID(0x7D); +static const dboard_id_t UBX_V2_160MHZ_RX_ID(0x7E); +static const dboard_id_t UBX_LP_160MHZ_TX_ID(0x0200); +static const dboard_id_t UBX_LP_160MHZ_RX_ID(0x0201); +static const dboard_id_t UBX_TDD_160MHZ_TX_ID(0x0202); +static const dboard_id_t UBX_TDD_160MHZ_RX_ID(0x0203); +static const std::vector<dboard_id_t> ubx_ids{UBX_PROTO_V3_TX_ID, + UBX_PROTO_V4_TX_ID, + UBX_V1_40MHZ_TX_ID, + UBX_V1_160MHZ_TX_ID, + UBX_V2_40MHZ_TX_ID, + UBX_V2_160MHZ_TX_ID, + UBX_LP_160MHZ_TX_ID, + UBX_TDD_160MHZ_TX_ID, + UBX_PROTO_V3_RX_ID, + UBX_PROTO_V4_RX_ID, + UBX_V1_40MHZ_RX_ID, + UBX_V1_160MHZ_RX_ID, + UBX_V2_40MHZ_RX_ID, + UBX_V2_160MHZ_RX_ID, + UBX_LP_160MHZ_RX_ID, + UBX_TDD_160MHZ_RX_ID}; + +static UHD_INLINE double get_max_pfd_freq(dboard_id_t dboard_id) +{ + if ((dboard_id == UBX_PROTO_V3_TX_ID) || (dboard_id == UBX_PROTO_V3_RX_ID)) { + return 25e6; + } + return 50e6; +} + +}}}}; // namespace uhd::usrp::dboard::ubx diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp index 3752a2e70..3686f4348 100644 --- a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp +++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp @@ -13,6 +13,7 @@ #include <uhdlib/usrp/common/adf535x.hpp> #include <uhdlib/utils/narrow.hpp> #include <chrono> +#include <cmath> #include <thread> using namespace uhd; @@ -29,10 +30,9 @@ inline uint32_t bool2bin(bool x) return x ? 1 : 0; } -const double TWINRX_DESIRED_REFERENCE_FREQ = 50e6; -const double TWINRX_REV_AB_PFD_FREQ = 6.25e6; -const double TWINRX_REV_C_PFD_FREQ = 12.5e6; -const double TWINRX_SPI_CLOCK_FREQ = 3e6; +const double TWINRX_REV_AB_PFD_FREQ = 6.25e6; +const double TWINRX_REV_C_PFD_FREQ = 12.5e6; +const double TWINRX_SPI_CLOCK_FREQ = 3e6; } // namespace class twinrx_ctrl_impl : public twinrx_ctrl @@ -49,22 +49,29 @@ public: _spi_config.divider = uhd::narrow_cast<size_t>(std::ceil( _db_iface->get_codec_rate(dboard_iface::UNIT_TX) / TWINRX_SPI_CLOCK_FREQ)); - // Initialize dboard clocks - bool found_rate = false; - for (double rate : _db_iface->get_clock_rates(dboard_iface::UNIT_TX)) { - found_rate |= - uhd::math::frequencies_are_equal(rate, TWINRX_DESIRED_REFERENCE_FREQ); - } - for (double rate : _db_iface->get_clock_rates(dboard_iface::UNIT_RX)) { - found_rate |= - uhd::math::frequencies_are_equal(rate, TWINRX_DESIRED_REFERENCE_FREQ); - } - if (not found_rate) { - throw uhd::runtime_error("TwinRX not supported on this motherboard"); + // Daughterboard clock rates must be a multiple of the pfd frequency + if (rx_id == twinrx::TWINRX_REV_C_ID) { + if (fmod(_db_iface->get_clock_rate(dboard_iface::UNIT_RX), + TWINRX_REV_C_PFD_FREQ) + != 0) { + throw uhd::value_error( + str(boost::format( + "TwinRX clock rate %f is not a multiple of the pfd freq %f.") + % _db_iface->get_clock_rate(dboard_iface::UNIT_RX) + % TWINRX_REV_C_PFD_FREQ)); + } + } else { + if (fmod(_db_iface->get_clock_rate(dboard_iface::UNIT_RX), + TWINRX_REV_AB_PFD_FREQ) + != 0) { + throw uhd::value_error( + str(boost::format( + "TwinRX clock rate %f is not a multiple of the pfd freq %f.") + % _db_iface->get_clock_rate(dboard_iface::UNIT_RX) + % TWINRX_REV_AB_PFD_FREQ)); + } } - _db_iface->set_clock_rate(dboard_iface::UNIT_TX, TWINRX_DESIRED_REFERENCE_FREQ); - _db_iface->set_clock_rate(dboard_iface::UNIT_RX, TWINRX_DESIRED_REFERENCE_FREQ); - + // Initialize dboard clocks _db_iface->set_clock_enabled(dboard_iface::UNIT_TX, true); _db_iface->set_clock_enabled(dboard_iface::UNIT_RX, true); @@ -126,7 +133,8 @@ public: _lo1_iface[i]->set_pfd_freq(TWINRX_REV_AB_PFD_FREQ); } _lo1_iface[i]->set_output_power(adf535x_iface::OUTPUT_POWER_5DBM); - _lo1_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ); + _lo1_iface[i]->set_reference_freq( + _db_iface->get_clock_rate(dboard_iface::UNIT_TX)); _lo1_iface[i]->set_muxout_mode(adf535x_iface::MUXOUT_DLD); _lo1_iface[i]->set_frequency(3e9, 1.0e3); @@ -137,7 +145,8 @@ public: }); _lo2_iface[i]->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED); _lo2_iface[i]->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM); - _lo2_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ); + _lo2_iface[i]->set_reference_freq( + _db_iface->get_clock_rate(dboard_iface::UNIT_RX)); _lo2_iface[i]->set_muxout_mode(adf435x_iface::MUXOUT_DLD); _lo2_iface[i]->set_tuning_mode(adf435x_iface::TUNING_MODE_LOW_SPUR); _lo2_iface[i]->set_prescaler(adf435x_iface::PRESCALER_8_9); diff --git a/host/lib/usrp/x300/x300_device_args.hpp b/host/lib/usrp/x300/x300_device_args.hpp index 6c6680c4f..7e17c36ce 100644 --- a/host/lib/usrp/x300/x300_device_args.hpp +++ b/host/lib/usrp/x300/x300_device_args.hpp @@ -197,12 +197,11 @@ private: if (dev_args.has_key(_dboard_clock_rate.key())) { _dboard_clock_rate.parse(dev_args[_dboard_clock_rate.key()]); } else { - // Some daughterboards may require other rates, but this default - // works best for all newer daughterboards (i.e. CBX, WBX, SBX, - // UBX, and TwinRX). + // This default clock rate works best for most daughterboards (i.e. DBSRX2, + // WBX, SBX, CBX, and TwinRX). if (_master_clock_rate.get() >= MIN_TICK_RATE && _master_clock_rate.get() <= MAX_TICK_RATE) { - _dboard_clock_rate.set(_master_clock_rate.get() / 4); + _dboard_clock_rate.set(_master_clock_rate.get() / 2); } else { throw uhd::value_error("Can't infer daughterboard clock rate. Specify " "dboard_clk_rate in the device args."); diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index a414fd0f1..fa8425fac 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -7,6 +7,7 @@ // #include "x300_impl.hpp" +#include "../dboard/db_ubx.hpp" #include "x300_claim.hpp" #include "x300_eth_mgr.hpp" #include "x300_mb_controller.hpp" @@ -15,6 +16,8 @@ #include "x300_mboard_type.hpp" #include "x300_pcie_mgr.hpp" #include <uhd/transport/if_addrs.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/usrp/dboard_id.hpp> #include <uhd/utils/log.hpp> #include <uhd/utils/paths.hpp> #include <uhd/utils/safe_call.hpp> @@ -141,6 +144,31 @@ device_addrs_t x300_find(const device_addr_t& hint_) } /*********************************************************************** + * Daughterboard detection before initialization in software + **********************************************************************/ +static std::vector<dboard_id_t> get_dboard_ids(uhd::i2c_iface& zpu_i2c) +{ + std::vector<dboard_id_t> dboard_ids; + // Read dboard ids from the EEPROM + constexpr size_t BASE_ADDR = 0x50; + constexpr size_t RX_EEPROM_ADDR = 0x5; + constexpr size_t TX_EEPROM_ADDR = 0x4; + static const std::vector<size_t> DB_OFFSETS{0x0, 0x2}; + static const std::vector<size_t> EEPROM_ADDRS{RX_EEPROM_ADDR, TX_EEPROM_ADDR}; + for (size_t eeprom_addr : EEPROM_ADDRS) { + for (size_t db_offset : DB_OFFSETS) { + const size_t addr = eeprom_addr + db_offset; + // Load EEPROM + std::unordered_map<size_t, usrp::dboard_eeprom_t> db_eeproms; + db_eeproms[addr].load(zpu_i2c, BASE_ADDR | addr); + uint16_t dboard_id = db_eeproms[addr].id.to_uint16(); + dboard_ids.push_back(static_cast<dboard_id_t>(dboard_id)); + } + } + return dboard_ids; +} + +/*********************************************************************** * Make **********************************************************************/ static device::sptr x300_make(const device_addr_t& device_addr) @@ -331,13 +359,45 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr) // create clock control objects //////////////////////////////////////////////////////////////////// UHD_LOGGER_DEBUG("X300") << "Setting up RF frontend clocking..."; + + // The default daughterboard clock rate may have to be overridden. This is due to the + // limitation on X300 devices where both daughterboards must use the same clock rate. + // The daughterboards that require specific clock rates are UBX and TwinRX. TwinRX + // requires a clock rate of 100 MHz for the best RF performance. UBX daughterboards + // require a clock rate of no more than the max pfd frequency to maintain phase + // synchronization. If there is no UBX, the default daughterboard clock rate is half + // of the master clock rate for X300. + const double x300_dboard_clock_rate = [this, dev_addr, mb]() -> double { + // Do not override use-specified dboard clock rates + if (dev_addr.has_key("dboard_clock_rate")) { + return mb.args.get_dboard_clock_rate(); + } + const double mcr = mb.args.get_master_clock_rate(); + double dboard_clock_rate = mb.args.get_dboard_clock_rate(); + // Check for UBX daughterboards + std::vector<dboard_id_t> dboard_ids = get_dboard_ids(*mb.zpu_i2c); + for (dboard_id_t dboard_id : dboard_ids) { + if (std::find( + dboard::ubx::ubx_ids.begin(), dboard::ubx::ubx_ids.end(), dboard_id) + != dboard::ubx::ubx_ids.end()) { + double ubx_clock_rate = mcr; + for (int i = 2; ubx_clock_rate > dboard::ubx::get_max_pfd_freq(dboard_id); + i++) { + ubx_clock_rate = mcr / i; + } + dboard_clock_rate = std::min(dboard_clock_rate, ubx_clock_rate); + } + } + return dboard_clock_rate; + }(); + // Initialize clock control registers. // NOTE: This does not configure the LMK yet. mb.clock = x300_clock_ctrl::make(mb.zpu_spi, 1 /*slaveno*/, mb.hw_rev, mb.args.get_master_clock_rate(), - mb.args.get_dboard_clock_rate(), + x300_dboard_clock_rate, mb.args.get_system_ref_rate()); //////////////////////////////////////////////////////////////////// |