diff options
| author | Mark Meserve <mark.meserve@ni.com> | 2019-05-03 16:46:34 -0500 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2019-05-07 16:30:36 -0700 | 
| commit | b1954e6fbe6cc003c6bab345d85ebe3e5687e10e (patch) | |
| tree | 006a0919d16f7ee519b1d14a306b1582da3e91d6 /host/lib/include/uhdlib | |
| parent | 39f69013423091227cb9126fefd194bb61db891e (diff) | |
| download | uhd-b1954e6fbe6cc003c6bab345d85ebe3e5687e10e.tar.gz uhd-b1954e6fbe6cc003c6bab345d85ebe3e5687e10e.tar.bz2 uhd-b1954e6fbe6cc003c6bab345d85ebe3e5687e10e.zip | |
adf435x: add low spur tuning mode
- adds a new mode to the adf435x driver which provides general spur performance
  improvements
Diffstat (limited to 'host/lib/include/uhdlib')
| -rw-r--r-- | host/lib/include/uhdlib/usrp/common/adf435x.hpp | 61 | 
1 files changed, 56 insertions, 5 deletions
| diff --git a/host/lib/include/uhdlib/usrp/common/adf435x.hpp b/host/lib/include/uhdlib/usrp/common/adf435x.hpp index e126bb896..9c573014f 100644 --- a/host/lib/include/uhdlib/usrp/common/adf435x.hpp +++ b/host/lib/include/uhdlib/usrp/common/adf435x.hpp @@ -12,6 +12,7 @@  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp>  #include <uhd/utils/log.hpp> +#include <uhdlib/utils/math.hpp>  #include <boost/function.hpp>  #include <boost/thread.hpp>  #include <boost/math/special_functions/round.hpp> @@ -40,6 +41,8 @@ public:      enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD }; +    enum tuning_mode_t { TUNING_MODE_HIGH_RESOLUTION, TUNING_MODE_LOW_SPUR }; +      /**       * Charge Pump Currents       */ @@ -79,6 +82,20 @@ public:      virtual void set_muxout_mode(muxout_t mode) = 0; +    //! Sets the tuning mode for subsequent tunes +    /** +     * High resolution mode will use the maximum modulus value to ensure an +     * exact tune whenever possible. Low spur mode will try to find the "best" +     * modulus for spur performance, which may result in loss of precision. +     * +     * To fully utilize low spur mode, the charge pump current should be +     * decreased.  This will vary based on board design. For example, for +     * TwinRX LO2, the current is decreased from 1.88 mA to 1.25 mA. In +     * addition, the 8/9 prescaler should be used instead of 4/5 whenever +     * possible. +     */ +    virtual void set_tuning_mode(tuning_mode_t mode) = 0; +      virtual void set_charge_pump_current(charge_pump_current_t cp_current) = 0;      virtual double set_charge_pump_current(double current, bool flush = false) = 0; @@ -179,6 +196,17 @@ public:          }      } +    void set_tuning_mode(tuning_mode_t mode) +    { +        // New mode applies to subsequent tunes i.e. do not re-tune now +        _tuning_mode = mode; + +        _regs.low_noise_and_spur = (_tuning_mode == TUNING_MODE_HIGH_RESOLUTION) +                                       ? adf435x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR +                                       : adf435x_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE; +        _regs.phase_12_bit = (_tuning_mode == TUNING_MODE_HIGH_RESOLUTION) ? 0 : 1; +    } +      void set_charge_pump_current(charge_pump_current_t cp_current)      {          switch (cp_current) { @@ -306,14 +334,26 @@ public:              }          } done_loop: -        //Fractional-N calculation -        MOD = 4095; //max fractional accuracy -        FRAC = static_cast<uint16_t>(boost::math::round((feedback_freq/pfd_freq - N)*MOD)); +        double frac_part = (feedback_freq / pfd_freq) - N;          if (int_n_mode) { -            if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target +            if (frac_part >= 0.5) {  +                // Round integer such that actual freq is closest to target                  N++;              }              FRAC = 0; +            MOD = 2; +        } else if (_tuning_mode == TUNING_MODE_LOW_SPUR) { +            std::tie(FRAC, MOD) = +                uhd::math::rational_approximation(frac_part, 4095, 0.0001); +            if (MOD < 2) +            { +                FRAC *= 2; +                MOD *= 2; +            } +        } else +        { +            MOD  = 4095; // max fractional accuracy +            FRAC = static_cast<uint16_t>(std::round(frac_part * MOD));          }          //Reference divide-by-2 for 50% duty cycle @@ -334,11 +374,21 @@ public:              double((N + (double(FRAC)/double(MOD))) *              (_reference_freq*(D?2:1)/(R*(T?2:1))))          ) / rf_div_compensation; +         +        uint16_t clock_div = std::max<uint16_t>(1, uint16_t(std::ceil(PHASE_RESYNC_TIME * pfd_freq / MOD))); +        if (clock_div > 4095) +        { +            // if clock_div is larger than 12-bits, increase modulus so it  +            // fits. Asserts later will ensure these values are not too large +            FRAC *= (clock_div >> 12) + 1; +            MOD *= (clock_div >> 12) + 1; +            clock_div = uint16_t(std::ceil(PHASE_RESYNC_TIME * pfd_freq / MOD)); +        }          _regs.frac_12_bit            = FRAC;          _regs.int_16_bit             = N;          _regs.mod_12_bit             = MOD; -        _regs.clock_divider_12_bit   = std::max<uint16_t>(1, uint16_t(std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD))); +        _regs.clock_divider_12_bit   = clock_div;          _regs.feedback_select        = _fb_after_divider ?                                          adf435x_regs_t::FEEDBACK_SELECT_DIVIDED :                                          adf435x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; @@ -414,6 +464,7 @@ protected:      double          _fb_after_divider;      double          _reference_freq;      int             _N_min; +    tuning_mode_t _tuning_mode;  };  template <> | 
