aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/common/max287x.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/common/max287x.hpp')
-rw-r--r--host/lib/usrp/common/max287x.hpp799
1 files changed, 799 insertions, 0 deletions
diff --git a/host/lib/usrp/common/max287x.hpp b/host/lib/usrp/common/max287x.hpp
new file mode 100644
index 000000000..ee1dbb946
--- /dev/null
+++ b/host/lib/usrp/common/max287x.hpp
@@ -0,0 +1,799 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef MAX287X_HPP_INCLUDED
+#define MAX287X_HPP_INCLUDED
+
+#include <uhd/exception.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/assign.hpp>
+#include <boost/function.hpp>
+#include <boost/thread.hpp>
+#include <vector>
+#include "max2870_regs.hpp"
+#include "max2871_regs.hpp"
+
+/**
+ * MAX287x interface
+ */
+class max287x_iface
+{
+public:
+ typedef boost::shared_ptr<max287x_iface> sptr;
+
+ typedef boost::function<void(std::vector<boost::uint32_t>)> write_fn;
+
+ /**
+ * LD Pin Modes
+ */
+ typedef enum{
+ LD_PIN_MODE_LOW,
+ LD_PIN_MODE_DLD,
+ LD_PIN_MODE_ALD,
+ LD_PIN_MODE_HIGH
+ } ld_pin_mode_t;
+
+ /**
+ * MUXOUT Modes
+ */
+ typedef enum{
+ MUXOUT_TRI_STATE,
+ MUXOUT_HIGH,
+ MUXOUT_LOW,
+ MUXOUT_RDIV,
+ MUXOUT_NDIV,
+ MUXOUT_ALD,
+ MUXOUT_DLD,
+ MUXOUT_SYNC,
+ MUXOUT_SPI
+ } muxout_mode_t;
+
+ /**
+ * Charge Pump Currents
+ */
+ typedef enum{
+ CHARGE_PUMP_CURRENT_0_32MA,
+ CHARGE_PUMP_CURRENT_0_64MA,
+ CHARGE_PUMP_CURRENT_0_96MA,
+ CHARGE_PUMP_CURRENT_1_28MA,
+ CHARGE_PUMP_CURRENT_1_60MA,
+ CHARGE_PUMP_CURRENT_1_92MA,
+ CHARGE_PUMP_CURRENT_2_24MA,
+ CHARGE_PUMP_CURRENT_2_56MA,
+ CHARGE_PUMP_CURRENT_2_88MA,
+ CHARGE_PUMP_CURRENT_3_20MA,
+ CHARGE_PUMP_CURRENT_3_52MA,
+ CHARGE_PUMP_CURRENT_3_84MA,
+ CHARGE_PUMP_CURRENT_4_16MA,
+ CHARGE_PUMP_CURRENT_4_48MA,
+ CHARGE_PUMP_CURRENT_4_80MA,
+ CHARGE_PUMP_CURRENT_5_12MA
+ } charge_pump_current_t;
+
+ /**
+ * Output Powers
+ */
+ typedef enum{
+ OUTPUT_POWER_M4DBM,
+ OUTPUT_POWER_M1DBM,
+ OUTPUT_POWER_2DBM,
+ OUTPUT_POWER_5DBM
+ } output_power_t;
+
+ typedef enum {
+ LOW_NOISE_AND_SPUR_LOW_NOISE,
+ LOW_NOISE_AND_SPUR_LOW_SPUR_1,
+ LOW_NOISE_AND_SPUR_LOW_SPUR_2
+ } low_noise_and_spur_t;
+
+ typedef enum {
+ CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF,
+ CLOCK_DIV_MODE_FAST_LOCK,
+ CLOCK_DIV_MODE_PHASE
+ } clock_divider_mode_t;
+
+ /**
+ * Make a synthesizer
+ * @param write write function
+ * @return shared pointer to object
+ */
+ template <typename max287X_t> static sptr make(write_fn write)
+ {
+ return sptr(new max287X_t(write));
+ }
+
+ /**
+ * Destructor
+ */
+ virtual ~max287x_iface() {};
+
+ /**
+ * Power up the synthesizer
+ */
+ virtual void power_up(void) = 0;
+
+ /**
+ * Shut down the synthesizer
+ */
+ virtual void shutdown(void) = 0;
+
+ /**
+ * Check if the synthesizer is shut down
+ */
+ virtual bool is_shutdown(void) = 0;
+
+ /**
+ * Set frequency
+ * @param target_freq target frequency
+ * @param ref_freq reference frequency
+ * @param target_pfd_freq target phase detector frequency
+ * @param is_int_n enable integer-N tuning
+ * @return actual frequency
+ */
+ virtual double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n) = 0;
+
+ /**
+ * Set output power
+ * @param power output power
+ */
+ virtual void set_output_power(output_power_t power) = 0;
+
+ /**
+ * Set lock detect pin mode
+ * @param mode lock detect pin mode
+ */
+ virtual void set_ld_pin_mode(ld_pin_mode_t mode) = 0;
+
+ /**
+ * Set muxout pin mode
+ * @param mode muxout mode
+ */
+ virtual void set_muxout_mode(muxout_mode_t mode) = 0;
+
+ /**
+ * Set charge pump current
+ * @param cp_current charge pump current
+ */
+ virtual void set_charge_pump_current(charge_pump_current_t cp_current) = 0;
+
+ /**
+ * Enable or disable auto retune
+ * @param enabled enable auto retune
+ */
+ virtual void set_auto_retune(bool enabled) = 0;
+
+ /**
+ * Set clock divider mode
+ * @param mode clock divider mode
+ */
+ virtual void set_clock_divider_mode(clock_divider_mode_t mode) = 0;
+
+ /**
+ * Enable or disable cycle slip mode
+ * @param enabled enable cycle slip mode
+ */
+ virtual void set_cycle_slip_mode(bool enabled) = 0;
+
+ /**
+ * Set low noise and spur mode
+ * @param mode low noise and spur mode
+ */
+ virtual void set_low_noise_and_spur(low_noise_and_spur_t mode) = 0;
+
+ /**
+ * Set phase
+ * @param phase the phase offset
+ */
+ virtual void set_phase(boost::uint16_t phase) = 0;
+
+ /**
+ * Write values configured by the set_* functions.
+ */
+ virtual void commit(void) = 0;
+
+ /**
+ * Check whether this is in a state where it can be synchronized
+ */
+ virtual bool can_sync(void) = 0;
+};
+
+/**
+ * MAX287x
+ * Base class for all MAX287x synthesizers
+ */
+template <typename max287x_regs_t>
+class max287x : public max287x_iface
+{
+public:
+ max287x(write_fn func);
+ virtual ~max287x();
+ virtual void power_up(void);
+ virtual void shutdown(void);
+ virtual bool is_shutdown(void);
+ virtual double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n);
+ virtual void set_output_power(output_power_t power);
+ virtual void set_ld_pin_mode(ld_pin_mode_t mode);
+ virtual void set_muxout_mode(muxout_mode_t mode);
+ virtual void set_charge_pump_current(charge_pump_current_t cp_current);
+ virtual void set_auto_retune(bool enabled);
+ virtual void set_clock_divider_mode(clock_divider_mode_t mode);
+ virtual void set_cycle_slip_mode(bool enabled);
+ virtual void set_low_noise_and_spur(low_noise_and_spur_t mode);
+ virtual void set_phase(boost::uint16_t phase);
+ virtual void commit();
+ virtual bool can_sync();
+
+protected:
+ max287x_regs_t _regs;
+ bool _can_sync;
+ bool _write_all_regs;
+
+private:
+ write_fn _write;
+ bool _delay_after_write;
+};
+
+/**
+ * MAX2870
+ */
+class max2870 : public max287x<max2870_regs_t>
+{
+public:
+ max2870(write_fn func) : max287x(func) {}
+ ~max2870() {}
+ double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+ {
+ _regs.cpoc = is_int_n ? max2870_regs_t::CPOC_ENABLED : max2870_regs_t::CPOC_DISABLED;
+ _regs.feedback_select = target_freq >= 3.0e9 ?
+ max2870_regs_t::FEEDBACK_SELECT_DIVIDED :
+ max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+
+ return max287x::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n);
+ }
+ void commit(void)
+ {
+ // For MAX2870, we always need to write all registers.
+ _write_all_regs = true;
+ max287x::commit();
+ }
+};
+
+/**
+ * MAX2871
+ */
+class max2871 : public max287x<max2871_regs_t>
+{
+public:
+ max2871(write_fn func) : max287x(func) {}
+ ~max2871() {};
+ void set_muxout_mode(muxout_mode_t mode)
+ {
+ switch(mode)
+ {
+ case MUXOUT_SYNC:
+ _regs.muxout = max2871_regs_t::MUXOUT_SYNC;
+ break;
+ case MUXOUT_SPI:
+ _regs.muxout = max2871_regs_t::MUXOUT_SPI;
+ break;
+ default:
+ max287x::set_muxout_mode(mode);
+ }
+ }
+
+ double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+ {
+ _regs.feedback_select = max2871_regs_t::FEEDBACK_SELECT_DIVIDED;
+ double freq = max287x::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n);
+
+ // According to Maxim support, the following factors must be true to allow for synchronization
+ if (_regs.r_counter_10_bit == 1 and
+ _regs.reference_divide_by_2 == max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED and
+ _regs.reference_doubler == max2871_regs_t::REFERENCE_DOUBLER_DISABLED and
+ _regs.rf_divider_select <= max2871_regs_t::RF_DIVIDER_SELECT_DIV16 and
+ _regs.low_noise_and_spur == max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE)
+ {
+ _can_sync = true;
+ }
+ return freq;
+ }
+};
+
+
+// Implementation of max287x template class
+// To avoid linker errors, it was either include
+// it here or put it in a .cpp file and include
+// that file in this header file. Decided to just
+// include it here.
+
+template <typename max287x_regs_t>
+max287x<max287x_regs_t>::max287x(write_fn func) :
+ _can_sync(false),
+ _write_all_regs(true),
+ _write(func),
+ _delay_after_write(true)
+{
+ power_up();
+}
+
+template <typename max287x_regs_t>
+max287x<max287x_regs_t>::~max287x()
+{
+ shutdown();
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::power_up(void)
+{
+ _regs.power_down = max287x_regs_t::POWER_DOWN_NORMAL;
+ _regs.double_buffer = max287x_regs_t::DOUBLE_BUFFER_ENABLED;
+
+ // According to MAX287x data sheets:
+ // "Upon power-up, the registers should be programmed twice with at
+ // least a 20ms pause between writes. The first write ensures that
+ // the device is enabled, and the second write starts the VCO
+ // selection process."
+ // The first write and the 20ms wait are done here. The second write
+ // is done when any other function that does a write to the registers
+ // is called (such as tuning).
+ _write_all_regs = true;
+ _delay_after_write = true;
+ commit();
+ _write_all_regs = true; // Next call to commit() writes all regs
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::shutdown(void)
+{
+ _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_DISABLED;
+ _regs.aux_output_enable = max287x_regs_t::AUX_OUTPUT_ENABLE_DISABLED;
+ _regs.power_down = max287x_regs_t::POWER_DOWN_SHUTDOWN;
+ commit();
+}
+
+template <typename max287x_regs_t>
+bool max287x<max287x_regs_t>::is_shutdown(void)
+{
+ return (_regs.power_down == max287x_regs_t::POWER_DOWN_SHUTDOWN);
+}
+
+template <typename max287x_regs_t>
+double max287x<max287x_regs_t>::set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+{
+ _can_sync = false;
+
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, typename max287x_regs_t::rf_divider_select_t> rfdivsel_to_enum =
+ boost::assign::map_list_of
+ (1, max287x_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, max287x_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, max287x_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, max287x_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, max287x_regs_t::RF_DIVIDER_SELECT_DIV16)
+ (32, max287x_regs_t::RF_DIVIDER_SELECT_DIV32)
+ (64, max287x_regs_t::RF_DIVIDER_SELECT_DIV64)
+ (128, max287x_regs_t::RF_DIVIDER_SELECT_DIV128);
+
+ //map mode setting to valid integer divider (N) values
+ static const uhd::range_t int_n_mode_div_range(16,65536,1);
+ static const uhd::range_t frac_n_mode_div_range(19,4091,1);
+
+ //other ranges and constants from MAX287X datasheets
+ static const uhd::range_t clock_div_range(1,4095,1);
+ static const uhd::range_t r_range(1,1023,1);
+ static const double MIN_VCO_FREQ = 3e9;
+ static const double BS_FREQ = 50e3;
+ static const int MAX_BS_VALUE = 1023;
+
+ int T = 0;
+ int D = ref_freq <= 10.0e6 ? 1 : 0;
+ int R = 0;
+ int BS = 0;
+ int N = 0;
+ int FRAC = 0;
+ int MOD = 4095;
+ int RFdiv = 1;
+ double pfd_freq = target_pfd_freq;
+ bool feedback_divided = (_regs.feedback_select == max287x_regs_t::FEEDBACK_SELECT_DIVIDED);
+
+ //increase RF divider until acceptable VCO frequency (MIN freq for MAX287x VCO is 3GHz)
+ double vco_freq = target_freq;
+ while (vco_freq < MIN_VCO_FREQ)
+ {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ // The feedback frequency can be the fundamental VCO frequency or
+ // divided frequency. The output divider for MAX287x is actually
+ // 2 dividers, but only the first (1/2/4/8/16) is included in the
+ // feedback loop.
+ int fb_divisor = feedback_divided ? (RFdiv > 16 ? 16 : RFdiv) : 1;
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exits when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv
+ */
+ for(R = int(ref_freq*(1+D)/(target_pfd_freq*(1+T))); R <= r_range.stop(); R++)
+ {
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below target
+ if (pfd_freq > target_pfd_freq)
+ continue;
+
+ //ignore fractional part of tuning
+ N = int((vco_freq/pfd_freq)/fb_divisor);
+
+ //Fractional-N calculation
+ FRAC = int(boost::math::round(((vco_freq/pfd_freq)/fb_divisor - N)*MOD));
+
+ if(is_int_n)
+ {
+ if (FRAC > (MOD / 2)) //Round integer such that actual freq is closest to target
+ N++;
+ FRAC = 0;
+ }
+
+ //keep N within int divider requirements
+ if(is_int_n)
+ {
+ if(N < int_n_mode_div_range.start()) continue;
+ if(N > int_n_mode_div_range.stop()) continue;
+ }
+ else
+ {
+ if(N < frac_n_mode_div_range.start()) continue;
+ if(N > frac_n_mode_div_range.stop()) continue;
+ }
+
+ //keep pfd freq low enough to achieve 50kHz BS clock
+ BS = std::ceil(pfd_freq / BS_FREQ);
+ if(BS <= MAX_BS_VALUE) break;
+ }
+ UHD_ASSERT_THROW(R <= r_range.stop());
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0)
+ {
+ T = 1;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ double actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))) * fb_divisor / RFdiv;
+
+ UHD_LOGV(rarely)
+ << boost::format("MAX287x: Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f"
+ ) % ref_freq % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
+ << boost::format("MAX287x: tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") << std::endl
+ << boost::format("MAX287x: Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+
+ //load the register values
+ _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_ENABLED;
+
+ if(is_int_n) {
+ _regs.cpl = max287x_regs_t::CPL_DISABLED;
+ _regs.ldf = max287x_regs_t::LDF_INT_N;
+ _regs.int_n_mode = max287x_regs_t::INT_N_MODE_INT_N;
+ } else {
+ _regs.cpl = max287x_regs_t::CPL_ENABLED;
+ _regs.ldf = max287x_regs_t::LDF_FRAC_N;
+ _regs.int_n_mode = max287x_regs_t::INT_N_MODE_FRAC_N;
+ }
+
+ _regs.lds = pfd_freq <= 32e6 ? max287x_regs_t::LDS_SLOW : max287x_regs_t::LDS_FAST;
+
+ _regs.frac_12_bit = FRAC;
+ _regs.int_16_bit = N;
+ _regs.mod_12_bit = MOD;
+ _regs.clock_divider_12_bit = std::max(int(clock_div_range.start()), int(std::ceil(400e-6*pfd_freq/MOD)));
+ UHD_ASSERT_THROW(_regs.clock_divider_12_bit <= clock_div_range.stop());
+ _regs.r_counter_10_bit = R;
+ _regs.reference_divide_by_2 = T ?
+ max287x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ max287x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ _regs.reference_doubler = D ?
+ max287x_regs_t::REFERENCE_DOUBLER_ENABLED :
+ max287x_regs_t::REFERENCE_DOUBLER_DISABLED;
+ _regs.band_select_clock_div = BS & 0xFF;
+ _regs.bs_msb = (BS & 0x300) >> 8;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ _regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+
+ if (_regs.clock_div_mode == max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK)
+ {
+ // Charge pump current needs to be set to lowest value in fast lock mode
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA;
+ // Make sure the register containing the charge pump current is written
+ _write_all_regs = true;
+ }
+
+ return actual_freq;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_output_power(output_power_t power)
+{
+ switch (power)
+ {
+ case OUTPUT_POWER_M4DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_M4DBM;
+ break;
+ case OUTPUT_POWER_M1DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_M1DBM;
+ break;
+ case OUTPUT_POWER_2DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_2DBM;
+ break;
+ case OUTPUT_POWER_5DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_5DBM;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_ld_pin_mode(ld_pin_mode_t mode)
+{
+ switch(mode)
+ {
+ case LD_PIN_MODE_LOW:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_LOW;
+ break;
+ case LD_PIN_MODE_DLD:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_DLD;
+ break;
+ case LD_PIN_MODE_ALD:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_ALD;
+ break;
+ case LD_PIN_MODE_HIGH:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_HIGH;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_muxout_mode(muxout_mode_t mode)
+{
+ switch(mode)
+ {
+ case MUXOUT_TRI_STATE:
+ _regs.muxout = max287x_regs_t::MUXOUT_TRI_STATE;
+ break;
+ case MUXOUT_HIGH:
+ _regs.muxout = max287x_regs_t::MUXOUT_HIGH;
+ break;
+ case MUXOUT_LOW:
+ _regs.muxout = max287x_regs_t::MUXOUT_LOW;
+ break;
+ case MUXOUT_RDIV:
+ _regs.muxout = max287x_regs_t::MUXOUT_RDIV;
+ break;
+ case MUXOUT_NDIV:
+ _regs.muxout = max287x_regs_t::MUXOUT_NDIV;
+ break;
+ case MUXOUT_ALD:
+ _regs.muxout = max287x_regs_t::MUXOUT_ALD;
+ break;
+ case MUXOUT_DLD:
+ _regs.muxout = max287x_regs_t::MUXOUT_DLD;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_charge_pump_current(charge_pump_current_t cp_current)
+{
+ switch(cp_current)
+ {
+ case CHARGE_PUMP_CURRENT_0_32MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA;
+ break;
+ case CHARGE_PUMP_CURRENT_0_64MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_64MA;
+ break;
+ case CHARGE_PUMP_CURRENT_0_96MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_96MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_28MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_28MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_60MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_60MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_92MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_92MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_24MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_24MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_56MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_56MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_88MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_88MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_20MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_20MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_52MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_52MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_84MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_84MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_16MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_16MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_48MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_48MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_80MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_80MA;
+ break;
+ case CHARGE_PUMP_CURRENT_5_12MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_5_12MA;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_auto_retune(bool enabled)
+{
+ _regs.retune = enabled ? max287x_regs_t::RETUNE_ENABLED : max287x_regs_t::RETUNE_DISABLED;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_clock_divider_mode(clock_divider_mode_t mode)
+{
+ switch(mode)
+ {
+ case CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF;
+ break;
+ case CLOCK_DIV_MODE_FAST_LOCK:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK;
+ break;
+ case CLOCK_DIV_MODE_PHASE:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_PHASE;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_cycle_slip_mode(bool enabled)
+{
+ if (enabled)
+ throw uhd::runtime_error("Cycle slip mode not supported on this MAX287x synthesizer.");
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_low_noise_and_spur(low_noise_and_spur_t mode)
+{
+ switch(mode)
+ {
+ case LOW_NOISE_AND_SPUR_LOW_NOISE:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE;
+ break;
+ case LOW_NOISE_AND_SPUR_LOW_SPUR_1:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_1;
+ break;
+ case LOW_NOISE_AND_SPUR_LOW_SPUR_2:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_phase(boost::uint16_t phase)
+{
+ _regs.phase_12_bit = phase & 0xFFF;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::commit()
+{
+ std::vector<boost::uint32_t> regs;
+ std::set<boost::uint32_t> changed_regs;
+
+ // Get only regs with changes
+ if (_write_all_regs)
+ {
+ for (int addr = 5; addr >= 0; addr--)
+ regs.push_back(_regs.get_reg(boost::uint32_t(addr)));
+ } else {
+ try {
+ changed_regs = _regs.template get_changed_addrs<boost::uint32_t> ();
+ for (int addr = 5; addr >= 0; addr--)
+ {
+ if (changed_regs.find(boost::uint32_t(addr)) != changed_regs.end())
+ regs.push_back(_regs.get_reg(boost::uint32_t(addr)));
+ }
+ } catch (uhd::runtime_error& e) {
+ // No saved state - write all regs
+ for (int addr = 5; addr >= 0; addr--)
+ regs.push_back(_regs.get_reg(boost::uint32_t(addr)));
+ }
+ }
+
+ _write(regs);
+ _regs.save_state();
+ _write_all_regs = false;
+
+ if (_delay_after_write)
+ {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(20));
+ _delay_after_write = false;
+ }
+}
+
+
+template <typename max287x_regs_t>
+bool max287x<max287x_regs_t>::can_sync(void)
+{
+ return _can_sync;
+}
+
+#endif // MAX287X_HPP_INCLUDED