From d1799df5a43eb350a8d11f3b225a33254abb0401 Mon Sep 17 00:00:00 2001 From: mattprost Date: Wed, 22 Jul 2020 12:13:59 -0500 Subject: x300: change default dboard clock rate from 50 to 100 MHz This sets the reference clock for X300 daughterboards (other than UBX) to 100 MHz by default to improve RF performance. Note: The UBX daughterboard requires a clock rate of no more than the max pfd frequency (50 or 25 MHz depending on the hardware rev) in order to maintain phase synchronization. If a UBX daughterboard is present on the X300, the clock rate for all daughterboards will be set to the pfd frequency by default. This is because of the limitation on X300 that requires the daughterboards to use the same clock rate. Signed-off-by: mattprost --- host/lib/usrp/dboard/db_ubx.cpp | 18 +-------- host/lib/usrp/dboard/db_ubx.hpp | 57 ++++++++++++++++++++++++++ host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp | 51 ++++++++++++++---------- host/lib/usrp/x300/x300_device_args.hpp | 7 ++-- host/lib/usrp/x300/x300_impl.cpp | 62 ++++++++++++++++++++++++++++- 5 files changed, 153 insertions(+), 42 deletions(-) create mode 100644 host/lib/usrp/dboard/db_ubx.hpp (limited to 'host/lib/usrp') 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 #include #include @@ -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 + +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 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 #include #include +#include #include 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(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 +#include +#include #include #include #include @@ -140,6 +143,31 @@ device_addrs_t x300_find(const device_addr_t& hint_) return addrs; } +/*********************************************************************** + * Daughterboard detection before initialization in software + **********************************************************************/ +static std::vector get_dboard_ids(uhd::i2c_iface& zpu_i2c) +{ + std::vector 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 DB_OFFSETS{0x0, 0x2}; + static const std::vector 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 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)); + } + } + return dboard_ids; +} + /*********************************************************************** * Make **********************************************************************/ @@ -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_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()); //////////////////////////////////////////////////////////////////// -- cgit v1.2.3