diff options
Diffstat (limited to 'host/lib/usrp/common')
| -rw-r--r-- | host/lib/usrp/common/max287x.hpp | 799 | 
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  | 
