diff options
author | Scott Torborg <storborg@gmail.com> | 2018-03-04 18:24:13 -0800 |
---|---|---|
committer | Brent Stapleton <bstapleton@g.hmc.edu> | 2018-11-16 10:39:53 -0800 |
commit | 2df06060b57f33e095bfd08ce8143a7053c6fb6e (patch) | |
tree | d1b0f0485a8a3e3f188218ccf2196f72e8735926 | |
parent | 1f0c8bd2c76a3d9149fd72a8b1a9a8356362018e (diff) | |
download | uhd-2df06060b57f33e095bfd08ce8143a7053c6fb6e.tar.gz uhd-2df06060b57f33e095bfd08ce8143a7053c6fb6e.tar.bz2 uhd-2df06060b57f33e095bfd08ce8143a7053c6fb6e.zip |
x300: New mode to configure master clock rate
Add a new clocking mode to automatically configure arbitrary master
clock rates.
Co-authored-by: Brent Stapleton <brent.stapleton@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
-rw-r--r-- | host/lib/usrp/x300/x300_clock_ctrl.cpp | 97 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_defaults.hpp | 7 | ||||
-rw-r--r-- | host/lib/usrp/x300/x300_device_args.hpp | 10 |
3 files changed, 104 insertions, 10 deletions
diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index 7d99dfd71..f5d49c97d 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -7,6 +7,7 @@ #include "lmk04816_regs.hpp" #include "x300_clock_ctrl.hpp" +#include "x300_defaults.hpp" #include <uhd/utils/safe_call.hpp> #include <uhd/utils/math.hpp> #include <stdint.h> @@ -18,6 +19,10 @@ static const double X300_REF_CLK_OUT_RATE = 10e6; static const uint16_t X300_MAX_CLKOUT_DIV = 1045; +constexpr double MIN_VCO_FREQ = 2370e6; +constexpr double MAX_VCO_FREQ = 2600e6; +constexpr double VCXO_FREQ = 96.0e6; // VCXO runs at 96MHz +constexpr int VCXO_PLL2_N = 2; // Assume that the PLL2 N predivider is set to /2. struct x300_clk_delays { x300_clk_delays() : @@ -44,6 +49,7 @@ static const x300_clk_delays X300_REV7_CLK_DELAYS = x300_clk_delays( /*fpga=*/0.000, /*adc=*/0.000, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000); using namespace uhd; +using namespace uhd::math::fp_compare; x300_clock_ctrl::~x300_clock_ctrl(void){ /* NOP */ @@ -412,6 +418,68 @@ public: private: + double autoset_pll2_config(const double output_freq) + { + // VCXO runs at 96MHz, assume PLL2 reference doubler is enabled + const double ref = VCXO_FREQ * 2; + + const int lowest_vcodiv = std::ceil(MIN_VCO_FREQ / output_freq); + const int highest_vcodiv = std::floor(MAX_VCO_FREQ / output_freq); + + // Find the PLL2 configuration with the lowest frequency error, favoring + // higher phase comparison frequencies. + double best_error = 1e10; + double best_mcr = 0.0; + double best_vco_freq = _vco_freq; + int best_N = _lmk04816_regs.PLL2_N_30; + int best_R = _lmk04816_regs.PLL2_R_28; + + for (int vcodiv = lowest_vcodiv; vcodiv <= highest_vcodiv; vcodiv++) { + const double try_vco_freq = vcodiv * output_freq; + + // Start at R=2: with a min value of 2 for R, we don't have to worry + // about exceeding the maximum phase comparison frequency for PLL2. + for (int r = 2; r <= 50; r++) + { + // Note: We could accomplish somewhat higher resolution if we change + // the N predivider to odd values as well, and we may be able to get + // better spur performance by balancing the predivider and the + // divider. + const int n = + boost::math::round((r * try_vco_freq) / (VCXO_PLL2_N * ref)); + + const double actual_mcr = (ref * VCXO_PLL2_N * n) / (vcodiv * r); + const double error = std::abs(actual_mcr - output_freq); + if (error < best_error) { + best_error = error; + best_mcr = actual_mcr; + best_vco_freq = try_vco_freq; + best_N = n; + best_R = r; + } + } + } + UHD_ASSERT_THROW(best_mcr > 0.0); + + _vco_freq = best_vco_freq; + _lmk04816_regs.PLL2_N_30 = best_N; + _lmk04816_regs.PLL2_R_28 = best_R; + _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; + + if (fp_compare_epsilon<double>(best_error) > 0.0) { + UHD_LOGGER_WARNING("X300") + << boost::format("Attempted master clock rate %0.2f MHz, got %0.2f MHz") + % (output_freq / 1e6) % (best_mcr / 1e6); + } + + UHD_LOGGER_TRACE("X300") << boost::format( + "Using automatic LMK04816 PLL2 config: N=%d, R=%d, VCO=%0.2f MHz, MCR=%0.2f MHz") + % _lmk04816_regs.PLL2_N_30 % _lmk04816_regs.PLL2_R_28 + % (_vco_freq / 1e6) % (best_mcr / 1e6); + + return best_mcr; + } + void init() { /* The X3xx has two primary rates. The first is the * _system_ref_rate, which is sourced from the "clock_source"/"value" field @@ -429,12 +497,14 @@ private: m23_04M_184_32M_ZDEL, // LTE with 23.04 MHz ref m30_72M_184_32M_ZDEL, // LTE with external ref, aka CPRI Mode m10M_184_32M_NOZDEL, // LTE with 10 MHz ref - m10M_120M_ZDEL }; // NI USRP 120 MHz Clocking + m10M_120M_ZDEL, // NI USRP 120 MHz Clocking + m10M_AUTO_NOZDEL }; // automatic for arbitrary clock from 10MHz ref /* The default clocking mode is 10MHz reference generating a 200 MHz master * clock, in zero-delay mode. */ opmode_t clocking_mode = INVALID; + using namespace uhd::math::fp_compare; if (math::frequencies_are_equal(_system_ref_rate, 10e6)) { if (math::frequencies_are_equal(_master_clock_rate, 184.32e6)) { /* 10MHz reference, 184.32 MHz master clock out, NOT Zero Delay. */ @@ -445,6 +515,15 @@ private: } else if (math::frequencies_are_equal(_master_clock_rate, 120e6)) { /* 10MHz reference, 120 MHz master clock rate, Zero Delay */ clocking_mode = m10M_120M_ZDEL; + } else if ( + fp_compare_epsilon<double>(_master_clock_rate) >= uhd::usrp::x300::MIN_TICK_RATE + && fp_compare_epsilon<double>(_master_clock_rate) <= uhd::usrp::x300::MAX_TICK_RATE + ) { + /* 10MHz reference, attempt to automatically configure PLL + * for arbitrary master clock rate, Zero Delay */ + UHD_LOGGER_WARNING("X300") + << "Using automatic master clock PLL config. This is an experimental feature."; + clocking_mode = m10M_AUTO_NOZDEL; } else { throw uhd::runtime_error(str( boost::format("Invalid master clock rate: %.2f MHz.\n" @@ -654,6 +733,19 @@ private: break; + case m10M_AUTO_NOZDEL: + _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; + + // PLL1 - 2MHz compare frequency + _lmk04816_regs.PLL1_N_28 = 48; + _lmk04816_regs.PLL1_R_27 = 5; + _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + + // PLL2 - this call will set _vco_freq and PLL2 P/N/R registers. + _master_clock_rate = autoset_pll2_config(_master_clock_rate); + + break; + default: UHD_THROW_INVALID_CODE_PATH(); break; @@ -790,7 +882,8 @@ private: const spi_iface::sptr _spiface; const size_t _slaveno; const size_t _hw_rev; - const double _master_clock_rate; + // This is technically constant, but it can be coerced during initialization + double _master_clock_rate; const double _dboard_clock_rate; const double _system_ref_rate; lmk04816_regs_t _lmk04816_regs; diff --git a/host/lib/usrp/x300/x300_defaults.hpp b/host/lib/usrp/x300/x300_defaults.hpp index 1f5a52fec..363a90314 100644 --- a/host/lib/usrp/x300/x300_defaults.hpp +++ b/host/lib/usrp/x300/x300_defaults.hpp @@ -31,9 +31,10 @@ static constexpr size_t SRC_ADDR0 = 0; static constexpr size_t SRC_ADDR1 = 1; static constexpr size_t DST_ADDR = 2; -static constexpr double DEFAULT_TICK_RATE = 200e6; // Hz -static constexpr double BUS_CLOCK_RATE = 187.5e6; //Hz -static const std::vector<double> TICK_RATE_OPTIONS{184.32e6, 200e6}; +static constexpr double DEFAULT_TICK_RATE = 200e6; // Hz +static constexpr double MAX_TICK_RATE = 200e6; // Hz +static constexpr double MIN_TICK_RATE = 187.5e6; // Hz +static constexpr double BUS_CLOCK_RATE = 187.5e6; // Hz static const std::string FW_FILE_NAME = "usrp_x300_fw.bin"; diff --git a/host/lib/usrp/x300/x300_device_args.hpp b/host/lib/usrp/x300/x300_device_args.hpp index 56b3c1078..db1a01212 100644 --- a/host/lib/usrp/x300/x300_device_args.hpp +++ b/host/lib/usrp/x300/x300_device_args.hpp @@ -8,6 +8,7 @@ #define INCLUDED_X300_DEV_ARGS_HPP #include "x300_impl.hpp" +#include "x300_defaults.hpp" #include <uhdlib/usrp/constrained_device_args.hpp> namespace uhd { namespace usrp { namespace x300 { @@ -117,10 +118,9 @@ private: // Some daughterboards may require other rates, but this default // works best for all newer daughterboards (i.e. CBX, WBX, SBX, // UBX, and TwinRX). - if (_master_clock_rate.get() == 200e6) { - _dboard_clock_rate.set(50e6); - } else if (_master_clock_rate.get() == 184.32e6) { - _dboard_clock_rate.set(46.08e6); + if (_master_clock_rate.get() >= MIN_TICK_RATE && + _master_clock_rate.get() <= MAX_TICK_RATE) { + _dboard_clock_rate.set(_master_clock_rate.get() / 4); } else { throw uhd::value_error( "Can't infer daughterboard clock rate. Specify " @@ -157,7 +157,7 @@ private: } //Sanity check params - _enforce_discrete(_master_clock_rate, TICK_RATE_OPTIONS); + _enforce_range(_master_clock_rate, MIN_TICK_RATE, MAX_TICK_RATE); _enforce_discrete(_system_ref_rate, EXTERNAL_FREQ_OPTIONS); _enforce_discrete(_clock_source, CLOCK_SOURCE_OPTIONS); _enforce_discrete(_time_source, TIME_SOURCE_OPTIONS); |