aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Meserve <mark.meserve@ni.com>2019-05-03 16:46:34 -0500
committermichael-west <michael.west@ettus.com>2019-05-21 21:37:19 -0700
commit9609aeb4be7249d52a4def856d86de336bfc0fbe (patch)
tree0350373d899118ddd0ec13392f8db82908ff031b
parent248e0e62bc765905091cfffde6bc23d51c1d3d7e (diff)
downloaduhd-9609aeb4be7249d52a4def856d86de336bfc0fbe.tar.gz
uhd-9609aeb4be7249d52a4def856d86de336bfc0fbe.tar.bz2
uhd-9609aeb4be7249d52a4def856d86de336bfc0fbe.zip
adf435x: add low spur tuning mode
- adds a new mode to the adf435x driver which provides general spur performance improvements
-rw-r--r--host/lib/include/uhdlib/usrp/common/adf435x.hpp61
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 <>