diff options
| author | Ashish Chaudhari <ashish@ettus.com> | 2015-10-06 12:47:14 -0700 | 
|---|---|---|
| committer | Ashish Chaudhari <ashish@ettus.com> | 2016-02-12 11:13:20 -0800 | 
| commit | b0ad2ec29f3a1bd5f2980ed1f905dac50f87c21a (patch) | |
| tree | 43bb34d910823a57bf965637f1eabd8be3f0b9d0 | |
| parent | 7c7622c1fe4779c84e7c7149caa6dd08400e2774 (diff) | |
| download | uhd-b0ad2ec29f3a1bd5f2980ed1f905dac50f87c21a.tar.gz uhd-b0ad2ec29f3a1bd5f2980ed1f905dac50f87c21a.tar.bz2 uhd-b0ad2ec29f3a1bd5f2980ed1f905dac50f87c21a.zip | |
adf435x: Refactored ADF435X control code
- Removed adf435x_common and replaced with a real encapsulated interface
- Looks similar to the MAX287X code
- Updated all DB classes to use the new common code
| -rw-r--r-- | host/lib/usrp/common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | host/lib/usrp/common/adf435x.cpp | 34 | ||||
| -rw-r--r-- | host/lib/usrp/common/adf435x.hpp | 330 | ||||
| -rw-r--r-- | host/lib/usrp/common/adf435x_common.cpp | 161 | ||||
| -rw-r--r-- | host/lib/usrp/common/adf435x_common.hpp | 63 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.hpp | 19 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_version3.cpp | 111 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_version4.cpp | 122 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_common.cpp | 6 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_common.hpp | 14 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_version2.cpp | 114 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_version3.cpp | 114 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_version4.cpp | 112 | 
13 files changed, 524 insertions, 678 deletions
| diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 270314dcc..55dc92023 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -29,7 +29,7 @@ INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver")  LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp -    ${CMAKE_CURRENT_SOURCE_DIR}/adf435x_common.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/adf435x.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ad936x_manager.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver/ad9361_device.cpp diff --git a/host/lib/usrp/common/adf435x.cpp b/host/lib/usrp/common/adf435x.cpp new file mode 100644 index 000000000..f1ba6ad05 --- /dev/null +++ b/host/lib/usrp/common/adf435x.cpp @@ -0,0 +1,34 @@ +// +// Copyright 2013-2014 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/>. +// + +#include "adf435x.hpp" + +using namespace uhd; + +adf435x_iface::~adf435x_iface() +{ +} + +adf435x_iface::sptr adf435x_iface::make_adf4350(write_fn_t write) +{ +    return sptr(new adf435x_impl<adf4350_regs_t>(write)); +} + +adf435x_iface::sptr adf435x_iface::make_adf4351(write_fn_t write) +{ +    return sptr(new adf435x_impl<adf4351_regs_t>(write)); +} diff --git a/host/lib/usrp/common/adf435x.hpp b/host/lib/usrp/common/adf435x.hpp new file mode 100644 index 000000000..16557b514 --- /dev/null +++ b/host/lib/usrp/common/adf435x.hpp @@ -0,0 +1,330 @@ +// +// 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 INCLUDED_ADF435X_HPP +#define INCLUDED_ADF435X_HPP + +#include <uhd/exception.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/log.hpp> +#include <boost/function.hpp> +#include <boost/thread.hpp> +#include <boost/math/special_functions/round.hpp> +#include <vector> +#include "adf4350_regs.hpp" +#include "adf4351_regs.hpp" + +class adf435x_iface +{ +public: +    typedef boost::shared_ptr<adf435x_iface> sptr; +    typedef boost::function<void(std::vector<boost::uint32_t>)> write_fn_t; + +    static sptr make_adf4350(write_fn_t write); +    static sptr make_adf4351(write_fn_t write); + +    virtual ~adf435x_iface() = 0; + +    enum prescaler_t { PRESCALER_4_5, PRESCALER_8_9 }; + +    enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED }; + +    enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM }; + +    virtual void set_reference_freq(double fref) = 0; + +    virtual void set_prescaler(prescaler_t prescaler) = 0; + +    virtual void set_feedback_select(feedback_sel_t fb_sel) = 0; + +    virtual void set_output_power(output_power_t power) = 0; + +    virtual uhd::range_t get_int_range() = 0; + +    virtual double set_frequency(double target_freq, bool int_n_mode, bool flush = false) = 0; + +    virtual void commit(void) = 0; +}; + +template <typename adf435x_regs_t> +class adf435x_impl : public adf435x_iface +{ +public: +    adf435x_impl(write_fn_t write_fn) : +        _write_fn(write_fn), +        _regs(), +        _fb_after_divider(false), +        _reference_freq(0.0), +        _N_min(-1) +    {} + +    virtual ~adf435x_impl() {}; + +    void set_reference_freq(double fref) +    { +        _reference_freq = fref; +    } + +    void set_feedback_select(feedback_sel_t fb_sel) +    { +        _fb_after_divider = (fb_sel == FB_SEL_DIVIDED); +    } + +    void set_prescaler(prescaler_t prescaler) +    { +        if (prescaler == PRESCALER_8_9) { +            _regs.prescaler = adf435x_regs_t::PRESCALER_8_9; +            _N_min = 75; +        } else { +            _regs.prescaler = adf435x_regs_t::PRESCALER_4_5; +            _N_min = 23; +        } +    } + +    void set_output_power(output_power_t power) +    { +        switch (power) { +            case OUTPUT_POWER_M4DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_M4DBM; break; +            case OUTPUT_POWER_M1DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_M1DBM; break; +            case OUTPUT_POWER_2DBM:  _regs.output_power = adf435x_regs_t::OUTPUT_POWER_2DBM; break; +            case OUTPUT_POWER_5DBM:  _regs.output_power = adf435x_regs_t::OUTPUT_POWER_5DBM; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    uhd::range_t get_int_range() +    { +        if (_N_min < 0) throw uhd::runtime_error("set_prescaler must be called before get_int_range"); +        return uhd::range_t(_N_min, 4095); +    } + +    double set_frequency(double target_freq, bool int_n_mode, bool flush = false) +    { +        static const double REF_DOUBLER_THRESH_FREQ = 12.5e6; +        static const double PFD_FREQ_MAX            = 25.0e6; +        static const double BAND_SEL_FREQ_MAX       = 100e3; +        static const double VCO_FREQ_MIN            = 2.2e9; +        static const double VCO_FREQ_MAX            = 4.4e9; + +        //Default invalid value for actual_freq +        double actual_freq = 0; + +        uhd::range_t rf_divider_range = _get_rfdiv_range(); +        uhd::range_t int_range = get_int_range(); + +        double pfd_freq = 0; +        boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0; +        boost::uint16_t RFdiv = static_cast<boost::uint16_t>(rf_divider_range.start()); +        bool D = false, T = false; + +        //Reference doubler for 50% duty cycle +        D = (_reference_freq <= REF_DOUBLER_THRESH_FREQ); + +        //increase RF divider until acceptable VCO frequency +        double vco_freq = target_freq; +        while (vco_freq < VCO_FREQ_MIN && RFdiv < static_cast<boost::uint16_t>(rf_divider_range.stop())) { +            vco_freq *= 2; +            RFdiv *= 2; +        } + +        /* +         * 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. +         * +         * from pg.21 +         * +         * 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_actual = f_vco/RFdiv) +         */ +        double feedback_freq = _fb_after_divider ? target_freq : vco_freq; + +        for(R = 1; R <= 1023; R+=1){ +            //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +            pfd_freq = _reference_freq*(D?2:1)/(R*(T?2:1)); + +            //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) +            if (pfd_freq > PFD_FREQ_MAX) continue; + +            //First, ignore fractional part of tuning +            N = boost::uint16_t(std::floor(feedback_freq/pfd_freq)); + +            //keep N > minimum int divider requirement +            if (N < static_cast<boost::uint16_t>(int_range.start())) continue; + +            for(BS=1; BS <= 255; BS+=1){ +                //keep the band select frequency at or below band_sel_freq_max +                //constraint on band select clock +                if (pfd_freq/BS > BAND_SEL_FREQ_MAX) continue; +                goto done_loop; +            } +        } done_loop: + +        //Fractional-N calculation +        MOD = 4095; //max fractional accuracy +        FRAC = static_cast<boost::uint16_t>(boost::math::round((feedback_freq/pfd_freq - N)*MOD)); +        if (int_n_mode) { +            if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target +                N++; +            } +            FRAC = 0; +        } + +        //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 = true; +            R /= 2; +        } + +        //Typical phase resync time documented in data sheet pg.24 +        static const double PHASE_RESYNC_TIME = 400e-6; + +        //If feedback after divider, then compensation for the divider is pulled into the INT value +        int rf_div_compensation = _fb_after_divider ? 1 : RFdiv; + +        //Compute the actual frequency in terms of _reference_freq, N, FRAC, MOD, D, R and T. +        actual_freq = ( +            double((N + (double(FRAC)/double(MOD))) * +            (_reference_freq*(D?2:1)/(R*(T?2:1)))) +        ) / rf_div_compensation; + +        _regs.frac_12_bit            = FRAC; +        _regs.int_16_bit             = N; +        _regs.mod_12_bit             = MOD; +        _regs.clock_divider_12_bit   = std::max<boost::uint16_t>(1, boost::uint16_t(std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD))); +        _regs.feedback_select        = _fb_after_divider ? +                                        adf435x_regs_t::FEEDBACK_SELECT_DIVIDED : +                                        adf435x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; +        _regs.clock_div_mode         = _fb_after_divider ? +                                        adf435x_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : +                                        adf435x_regs_t::CLOCK_DIV_MODE_FAST_LOCK; +        _regs.r_counter_10_bit       = R; +        _regs.reference_divide_by_2  = T ? +                                        adf435x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : +                                        adf435x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +        _regs.reference_doubler      = D ? +                                        adf435x_regs_t::REFERENCE_DOUBLER_ENABLED : +                                        adf435x_regs_t::REFERENCE_DOUBLER_DISABLED; +        _regs.band_select_clock_div  = boost::uint8_t(BS); +        _regs.rf_divider_select      = static_cast<typename adf435x_regs_t::rf_divider_select_t>(_get_rfdiv_setting(RFdiv)); +        _regs.ldf                    = int_n_mode ? +                                        adf435x_regs_t::LDF_INT_N : +                                        adf435x_regs_t::LDF_FRAC_N; + +        std::string tuning_str = (int_n_mode) ? "Integer-N" : "Fractional"; +        UHD_LOGV(often) +            << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f" +            ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl +            << boost::format("ADF 435X Intermediates (MHz): Feedback=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" +            ) % (feedback_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (_reference_freq/1e6) << std::endl +            << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() << std::endl +            << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl; + +        UHD_ASSERT_THROW((_regs.frac_12_bit          & ((boost::uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.mod_12_bit           & ((boost::uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.r_counter_10_bit     & ((boost::uint16_t)~0x3FF)) == 0); + +        UHD_ASSERT_THROW(vco_freq >= VCO_FREQ_MIN and vco_freq <= VCO_FREQ_MAX); +        UHD_ASSERT_THROW(RFdiv >= static_cast<boost::uint16_t>(rf_divider_range.start())); +        UHD_ASSERT_THROW(RFdiv <= static_cast<boost::uint16_t>(rf_divider_range.stop())); +        UHD_ASSERT_THROW(_regs.int_16_bit >= static_cast<boost::uint16_t>(int_range.start())); +        UHD_ASSERT_THROW(_regs.int_16_bit <= static_cast<boost::uint16_t>(int_range.stop())); + +        if (flush) commit(); +        return actual_freq; +    } + +    void commit() +    { +        //reset counters +        _regs.counter_reset = adf435x_regs_t::COUNTER_RESET_ENABLED; +        std::vector<boost::uint32_t> regs; +        regs.push_back(_regs.get_reg(boost::uint32_t(2))); +        _write_fn(regs); +        _regs.counter_reset = adf435x_regs_t::COUNTER_RESET_DISABLED; + +        //write the registers +        //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) +        regs.clear(); +        for (int addr = 5; addr >= 0; addr--) { +            regs.push_back(_regs.get_reg(boost::uint32_t(addr))); +        } +        _write_fn(regs); +    } + +protected: +    uhd::range_t _get_rfdiv_range(); +    int _get_rfdiv_setting(boost::uint16_t div); + +    write_fn_t      _write_fn; +    adf435x_regs_t  _regs; +    double          _fb_after_divider; +    double          _reference_freq; +    int             _N_min; +}; + +template <> +inline uhd::range_t adf435x_impl<adf4350_regs_t>::_get_rfdiv_range() +{ +    return uhd::range_t(1, 16); +} + +template <> +inline uhd::range_t adf435x_impl<adf4351_regs_t>::_get_rfdiv_range() +{ +    return uhd::range_t(1, 64); +} + +template <> +inline int adf435x_impl<adf4350_regs_t>::_get_rfdiv_setting(boost::uint16_t div) +{ +    switch (div) { +        case 1:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV1); +        case 2:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV2); +        case 4:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV4); +        case 8:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV8); +        case 16: return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV16); +        default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <> +inline int adf435x_impl<adf4351_regs_t>::_get_rfdiv_setting(boost::uint16_t div) +{ +    switch (div) { +        case 1:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV1); +        case 2:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV2); +        case 4:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV4); +        case 8:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV8); +        case 16: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV16); +        case 32: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV32); +        case 64: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV64); +        default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +#endif // INCLUDED_ADF435X_HPP diff --git a/host/lib/usrp/common/adf435x_common.cpp b/host/lib/usrp/common/adf435x_common.cpp deleted file mode 100644 index 474a1c932..000000000 --- a/host/lib/usrp/common/adf435x_common.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// -// Copyright 2013-2014 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/>. -// - -#include "adf435x_common.hpp" - -#include <boost/math/special_functions/round.hpp> -#include <uhd/types/tune_request.hpp> -#include <uhd/utils/log.hpp> -#include <cmath> - - -using namespace uhd; - -/*********************************************************************** - * ADF 4350/4351 Tuning Utility - **********************************************************************/ -adf435x_tuning_settings tune_adf435x_synth( -    const double target_freq, -    const double ref_freq, -    const adf435x_tuning_constraints& constraints, -    double& actual_freq) -{ -    //Default invalid value for actual_freq -    actual_freq = 0; - -    double pfd_freq = 0; -    boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0; -    boost::uint16_t RFdiv = static_cast<boost::uint16_t>(constraints.rf_divider_range.start()); -    bool D = false, T = false; - -    //Reference doubler for 50% duty cycle -    //If ref_freq < 12.5MHz enable the reference doubler -    D = (ref_freq <= constraints.ref_doubler_threshold); - -    static const double MIN_VCO_FREQ = 2.2e9; -    static const double MAX_VCO_FREQ = 4.4e9; - -    //increase RF divider until acceptable VCO frequency -    double vco_freq = target_freq; -    while (vco_freq < MIN_VCO_FREQ && RFdiv < static_cast<boost::uint16_t>(constraints.rf_divider_range.stop())) { -        vco_freq *= 2; -        RFdiv *= 2; -    } - -    /* -     * 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. -     * -     * from pg.21 -     * -     * 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_actual = f_vco/RFdiv) -     */ -    double feedback_freq = constraints.feedback_after_divider ? target_freq : vco_freq; - -    for(R = 1; R <= 1023; R+=1){ -        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) -        pfd_freq = ref_freq*(D?2:1)/(R*(T?2:1)); - -        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) -        if (pfd_freq > constraints.pfd_freq_max) continue; - -        //First, ignore fractional part of tuning -        N = boost::uint16_t(std::floor(feedback_freq/pfd_freq)); - -        //keep N > minimum int divider requirement -        if (N < static_cast<boost::uint16_t>(constraints.int_range.start())) continue; - -        for(BS=1; BS <= 255; BS+=1){ -            //keep the band select frequency at or below band_sel_freq_max -            //constraint on band select clock -            if (pfd_freq/BS > constraints.band_sel_freq_max) continue; -            goto done_loop; -        } -    } done_loop: - -    //Fractional-N calculation -    MOD = 4095; //max fractional accuracy -    FRAC = static_cast<boost::uint16_t>(boost::math::round((feedback_freq/pfd_freq - N)*MOD)); -    if (constraints.force_frac0) { -        if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target -            N++; -        } -        FRAC = 0; -    } - -    //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 = true; -        R /= 2; -    } - -    //Typical phase resync time documented in data sheet pg.24 -    static const double PHASE_RESYNC_TIME = 400e-6; - -    //If feedback after divider, then compensation for the divider is pulled into the INT value -    int rf_div_compensation = constraints.feedback_after_divider ? 1 : RFdiv; - -    //Compute the actual frequency in terms of ref_freq, N, FRAC, MOD, D, R and T. -    actual_freq = ( -        double((N + (double(FRAC)/double(MOD))) * -        (ref_freq*(D?2:1)/(R*(T?2:1)))) -    ) / rf_div_compensation; - -    //load the settings -    adf435x_tuning_settings settings; -    settings.frac_12_bit = FRAC; -    settings.int_16_bit = N; -    settings.mod_12_bit = MOD; -    settings.clock_divider_12_bit = std::max<boost::uint16_t>(1, boost::uint16_t(std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD))); -    settings.r_counter_10_bit = R; -    settings.r_divide_by_2_en = T; -    settings.r_doubler_en = D; -    settings.band_select_clock_div = boost::uint8_t(BS); -    settings.rf_divider = RFdiv; - -    std::string tuning_str = (constraints.force_frac0) ? "Integer-N" : "Fractional"; -    UHD_LOGV(often) -        << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f" -        ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl -        << boost::format("ADF 435X Intermediates (MHz): Feedback=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" -        ) % (feedback_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl -        << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() << std::endl -        << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" -        ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl; - -    UHD_ASSERT_THROW((settings.frac_12_bit          & ((boost::uint16_t)~0xFFF)) == 0); -    UHD_ASSERT_THROW((settings.mod_12_bit           & ((boost::uint16_t)~0xFFF)) == 0); -    UHD_ASSERT_THROW((settings.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0); -    UHD_ASSERT_THROW((settings.r_counter_10_bit     & ((boost::uint16_t)~0x3FF)) == 0); - -    UHD_ASSERT_THROW(vco_freq >= MIN_VCO_FREQ and vco_freq <= MAX_VCO_FREQ); -    UHD_ASSERT_THROW(settings.rf_divider >= static_cast<boost::uint16_t>(constraints.rf_divider_range.start())); -    UHD_ASSERT_THROW(settings.rf_divider <= static_cast<boost::uint16_t>(constraints.rf_divider_range.stop())); -    UHD_ASSERT_THROW(settings.int_16_bit >= static_cast<boost::uint16_t>(constraints.int_range.start())); -    UHD_ASSERT_THROW(settings.int_16_bit <= static_cast<boost::uint16_t>(constraints.int_range.stop())); - -    return settings; -} diff --git a/host/lib/usrp/common/adf435x_common.hpp b/host/lib/usrp/common/adf435x_common.hpp deleted file mode 100644 index 617b9d97f..000000000 --- a/host/lib/usrp/common/adf435x_common.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright 2014 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 INCLUDED_ADF435X_COMMON_HPP -#define INCLUDED_ADF435X_COMMON_HPP - -#include <boost/cstdint.hpp> -#include <uhd/property_tree.hpp> -#include <uhd/types/ranges.hpp> - -//Common IO Pins -#define ADF435X_CE (1 << 3) -#define ADF435X_PDBRF (1 << 2) -#define ADF435X_MUXOUT (1 << 1) // INPUT!!! -#define LOCKDET_MASK (1 << 0) // INPUT!!! - -#define RX_ATTN_SHIFT 8 //lsb of RX Attenuator Control -#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) //valid bits of RX Attenuator Control - -struct adf435x_tuning_constraints { -    bool            force_frac0; -    bool            feedback_after_divider; -    double          ref_doubler_threshold; -    double          pfd_freq_max; -    double          band_sel_freq_max; -    uhd::range_t    rf_divider_range; -    uhd::range_t    int_range; -}; - -struct adf435x_tuning_settings { -    boost::uint16_t frac_12_bit; -    boost::uint16_t int_16_bit; -    boost::uint16_t mod_12_bit; -    boost::uint16_t r_counter_10_bit; -    bool            r_doubler_en; -    bool            r_divide_by_2_en; -    boost::uint16_t clock_divider_12_bit; -    boost::uint8_t  band_select_clock_div; -    boost::uint16_t rf_divider; -}; - -adf435x_tuning_settings tune_adf435x_synth( -    const double target_freq, -    const double ref_freq, -    const adf435x_tuning_constraints& constraints, -    double& actual_freq -); - -#endif /* INCLUDED_ADF435X_COMMON_HPP */ diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 4800bbd83..974cee114 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -16,10 +16,15 @@  //  #include <uhd/types/device_addr.hpp> - -#include "adf435x_common.hpp" +#include "adf435x.hpp"  #include "max287x.hpp" +// LO Related +#define ADF435X_CE      (1 << 3) +#define ADF435X_PDBRF   (1 << 2) +#define ADF435X_MUXOUT  (1 << 1) // INPUT!!! +#define LOCKDET_MASK    (1 << 0) // INPUT!!! +  // Common IO Pins  #define LO_LPF_EN       (1 << 15) @@ -36,6 +41,8 @@  #define RX_LED_LD       (1 << 6)                // LED for RX Lock Detect  #define DIS_POWER_RX    (1 << 5)                // on UNIT_RX, 0 powers up RX  #define RX_DISABLE      (1 << 4)                // on UNIT_RX, 1 disables RX Mixer and Baseband +#define RX_ATTN_SHIFT   8 //lsb of RX Attenuator Control +#define RX_ATTN_MASK    (63 << RX_ATTN_SHIFT) //valid bits of RX Attenuator Control  // TX Attenuator Pins  #define TX_ATTN_SHIFT   8                       // lsb of TX Attenuator Control @@ -190,6 +197,10 @@ protected:          /*! This is the registered instance of the wrapper class, sbx_base. */          sbx_xcvr *self_base; +    private: +        adf435x_iface::sptr _txlo; +        adf435x_iface::sptr _rxlo; +        void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s);      };      /*! @@ -206,6 +217,10 @@ protected:          /*! This is the registered instance of the wrapper class, sbx_base. */          sbx_xcvr *self_base; +    private: +        adf435x_iface::sptr _txlo; +        adf435x_iface::sptr _rxlo; +        void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s);      };      /*! diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index b848097d1..76ad7b04f 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -16,9 +16,7 @@  // -#include "adf4350_regs.hpp"  #include "db_sbx_common.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/types/tune_request.hpp>  #include <boost/algorithm/string.hpp> @@ -32,12 +30,21 @@ using namespace boost::assign;  sbx_xcvr::sbx_version3::sbx_version3(sbx_xcvr *_self_sbx_xcvr) {      //register the handle to our base SBX class      self_base = _self_sbx_xcvr; +    _txlo = adf435x_iface::make_adf4350(boost::bind(&sbx_xcvr::sbx_version3::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4350(boost::bind(&sbx_xcvr::sbx_version3::write_lo_regs, this, dboard_iface::UNIT_RX, _1));  }  sbx_xcvr::sbx_version3::~sbx_version3(void){      /* NOP */  } +void sbx_xcvr::sbx_version3::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) +{ +    BOOST_FOREACH(boost::uint32_t reg, regs) +    { +        self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32); +    } +}  /***********************************************************************   * Tuning @@ -57,95 +64,27 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); -    //clip the input -    target_freq = sbx_freq_range.clip(target_freq); - -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095);  //INT is a 12-bit field -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 16); -    tuning_constraints.feedback_after_divider = true; +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED); +    lo_iface->set_reference_freq(self_base->get_iface()->get_clock_rate(unit)); -    double actual_freq; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        target_freq, self_base->get_iface()->get_clock_rate(unit), -        tuning_constraints, actual_freq); +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(target_freq > 3e9 ? adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); -    //load the register values -    adf4350_regs_t regs; +    //Configure the LO +    double actual_freq = 0.0; +    actual_freq = lo_iface->set_frequency(sbx_freq_range.clip(target_freq), is_int_n); -    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  -        regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4350_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4350_regs_t::LDF_INT_N : -                                    adf4350_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "SBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) { +        lo_iface->set_output_power(adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "SBX tune: actual frequency %f MHz" -    ) % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit(); +      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index 8f7e747bc..639bce250 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -16,9 +16,7 @@  // -#include "adf4351_regs.hpp"  #include "db_sbx_common.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/types/tune_request.hpp>  #include <boost/algorithm/string.hpp> @@ -32,6 +30,8 @@ using namespace boost::assign;  sbx_xcvr::sbx_version4::sbx_version4(sbx_xcvr *_self_sbx_xcvr) {      //register the handle to our base SBX class      self_base = _self_sbx_xcvr; +    _txlo = adf435x_iface::make_adf4351(boost::bind(&sbx_xcvr::sbx_version4::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4351(boost::bind(&sbx_xcvr::sbx_version4::write_lo_regs, this, dboard_iface::UNIT_RX, _1));  } @@ -39,6 +39,14 @@ sbx_xcvr::sbx_version4::~sbx_version4(void){      /* NOP */  } +void sbx_xcvr::sbx_version4::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) +{ +    BOOST_FOREACH(boost::uint32_t reg, regs) +    { +        self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32); +    } +} +  /***********************************************************************   * Tuning @@ -58,99 +66,27 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); -    //clip the input -    target_freq = sbx_freq_range.clip(target_freq); - -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4351_regs_t::PRESCALER_4_5 -        (1,75) //adf4351_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) -        (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV32) -        (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) -    ; - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4351_regs_t::prescaler_t prescaler = target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; - -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095);  //INT is a 12-bit field -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 64); -    tuning_constraints.feedback_after_divider = true; - -    double actual_freq; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        target_freq, self_base->get_iface()->get_clock_rate(unit), -        tuning_constraints, actual_freq); - -    //load the register values -    adf4351_regs_t regs; - -    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  -        regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4351_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4351_regs_t::LDF_INT_N : -                                    adf4351_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4351_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    boost::uint16_t rx_id = self_base->get_rx_id().to_uint16(); -    std::string board_name = (rx_id == 0x0083) ? "SBX-120" : "SBX"; -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "%s SPI Reg (0x%02x): 0x%08x" -        ) % board_name.c_str() % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED); +    lo_iface->set_reference_freq(self_base->get_iface()->get_clock_rate(unit)); + +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(target_freq > 3.6e9 ? adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); + +    //Configure the LO +    double actual_freq = 0.0; +    actual_freq = lo_iface->set_frequency(sbx_freq_range.clip(target_freq), is_int_n); + +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) { +        lo_iface->set_output_power(adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "%s tune: actual frequency %f MHz" -    ) % board_name.c_str() % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit(); +      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_wbx_common.cpp b/host/lib/usrp/dboard/db_wbx_common.cpp index 0a7e1afa2..6539e798a 100644 --- a/host/lib/usrp/dboard/db_wbx_common.cpp +++ b/host/lib/usrp/dboard/db_wbx_common.cpp @@ -156,3 +156,9 @@ sensor_value_t wbx_base::get_locked(dboard_iface::unit_t unit){      const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0;      return sensor_value_t("LO", locked, "locked", "unlocked");  } + +void wbx_base::wbx_versionx::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) { +    BOOST_FOREACH(boost::uint32_t reg, regs) { +        self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32); +    } +} diff --git a/host/lib/usrp/dboard/db_wbx_common.hpp b/host/lib/usrp/dboard/db_wbx_common.hpp index 6a4224048..8a003fc8c 100644 --- a/host/lib/usrp/dboard/db_wbx_common.hpp +++ b/host/lib/usrp/dboard/db_wbx_common.hpp @@ -19,8 +19,13 @@  #define INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP  #include <uhd/types/device_addr.hpp> +#include "adf435x.hpp" -#include "../common/adf435x_common.hpp" +// LO Related +#define ADF435X_CE      (1 << 3) +#define ADF435X_PDBRF   (1 << 2) +#define ADF435X_MUXOUT  (1 << 1) // INPUT!!! +#define LOCKDET_MASK    (1 << 0) // INPUT!!!  // TX IO Pins  #define TX_PUP_5V       (1 << 7)                // enables 5.0V power supply @@ -40,6 +45,9 @@  #define TX_ATTN_1       (1 << 1)  #define TX_ATTN_MASK    (TX_ATTN_16|TX_ATTN_8|TX_ATTN_4|TX_ATTN_2|TX_ATTN_1)      // valid bits of TX Attenuator Control +#define RX_ATTN_SHIFT 8 //lsb of RX Attenuator Control +#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) //valid bits of RX Attenuator Control +  // Mixer functions  #define TX_MIXER_ENB    (TXMOD_EN|ADF435X_PDBRF)    // for v3, TXMOD_EN tied to ADF435X_PDBRF rather than separate  #define TX_MIXER_DIS    0 @@ -142,6 +150,10 @@ protected:          property_tree::sptr get_tx_subtree(void){              return self_base->get_tx_subtree();          } + +        adf435x_iface::sptr _txlo; +        adf435x_iface::sptr _rxlo; +        void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s);      }; diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index d175ea000..18dc383b7 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -16,8 +16,6 @@  //  #include "db_wbx_common.hpp" -#include "adf4350_regs.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/types/tune_request.hpp>  #include <uhd/utils/log.hpp>  #include <uhd/types/dict.hpp> @@ -77,6 +75,8 @@ static double tx_pga0_gain_to_dac_volts(double &gain){  wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) {      //register our handle on the primary wbx_base instance      self_base = _self_wbx_base; +    _txlo = adf435x_iface::make_adf4350(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4350(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_RX, _1));      ////////////////////////////////////////////////////////////////////      // Register RX properties @@ -178,108 +178,42 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar                                                                    : self_base->get_tx_subtree();      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); +    double reference_freq = self_base->get_iface()->get_clock_rate(unit); -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_reference_freq(reference_freq); -    double reference_freq = self_base->get_iface()->get_clock_rate(unit);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //frequency must 2x the target frequency      double synth_target_freq = target_freq * 2; -    //TODO: Document why the following has to be true -    bool div_resync_enabled = (target_freq > reference_freq); - -    adf4350_regs_t::prescaler_t prescaler = -        synth_target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(synth_target_freq > 3e9 ? +        adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); +      //When divider resync is enabled, a 180 deg phase error is introduced when syncing      //multiple WBX boards. Switching to fundamental mode works arounds this issue. -    tuning_constraints.feedback_after_divider = div_resync_enabled; +    //TODO: Document why the following has to be true +    lo_iface->set_feedback_select((target_freq > reference_freq) ? +        adf435x_iface::FB_SEL_DIVIDED : adf435x_iface::FB_SEL_FUNDAMENTAL); -    double synth_actual_freq = 0; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); +    double synth_actual_freq = lo_iface->set_frequency(synth_target_freq, is_int_n);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //actual_freq must /2 the synth_actual_freq      double actual_freq = synth_actual_freq / 2; -    //load the register values -    adf4350_regs_t regs; - -    if (unit == dboard_iface::UNIT_RX) -        regs.output_power = (actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? adf4350_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4350_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = (actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? adf4350_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4350_regs_t::OUTPUT_POWER_M1DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = div_resync_enabled ? -                                    adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : -                                    adf4350_regs_t::CLOCK_DIV_MODE_FAST_LOCK; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4350_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4350_regs_t::LDF_INT_N : -                                    adf4350_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "WBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    if (unit == dboard_iface::UNIT_RX) { +        lo_iface->set_output_power((actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power((actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_M1DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "WBX tune: actual frequency %f MHz" -    ) % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit(); +      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index b898a2fca..2add8d25d 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -16,8 +16,6 @@  //  #include "db_wbx_common.hpp" -#include "adf4350_regs.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/utils/log.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp> @@ -82,6 +80,8 @@ static int tx_pga0_gain_to_iobits(double &gain){  wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) {      //register our handle on the primary wbx_base instance      self_base = _self_wbx_base; +    _txlo = adf435x_iface::make_adf4350(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4350(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_RX, _1));      ////////////////////////////////////////////////////////////////////      // Register RX properties @@ -209,108 +209,42 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar                                                                    : self_base->get_tx_subtree();      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); +    double reference_freq = self_base->get_iface()->get_clock_rate(unit); -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_reference_freq(reference_freq); -    double reference_freq = self_base->get_iface()->get_clock_rate(unit);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //frequency must 2x the target frequency      double synth_target_freq = target_freq * 2; -    //TODO: Document why the following has to be true -    bool div_resync_enabled = (target_freq > reference_freq); - -    adf4350_regs_t::prescaler_t prescaler = -        synth_target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; -     -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(synth_target_freq > 3e9 ? +        adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); +      //When divider resync is enabled, a 180 deg phase error is introduced when syncing      //multiple WBX boards. Switching to fundamental mode works arounds this issue. -    tuning_constraints.feedback_after_divider = div_resync_enabled; +    //TODO: Document why the following has to be true +    lo_iface->set_feedback_select((target_freq > reference_freq) ? +        adf435x_iface::FB_SEL_DIVIDED : adf435x_iface::FB_SEL_FUNDAMENTAL); -    double synth_actual_freq = 0; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); +    double synth_actual_freq = lo_iface->set_frequency(synth_target_freq, is_int_n);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //actual_freq must /2 the synth_actual_freq      double actual_freq = synth_actual_freq / 2; -    //load the register values -    adf4350_regs_t regs; - -    if (unit == dboard_iface::UNIT_RX) -        regs.output_power = (actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? adf4350_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4350_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = (actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? adf4350_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4350_regs_t::OUTPUT_POWER_M1DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = div_resync_enabled ? -                                    adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : -                                    adf4350_regs_t::CLOCK_DIV_MODE_FAST_LOCK; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4350_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4350_regs_t::LDF_INT_N : -                                    adf4350_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "WBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    if (unit == dboard_iface::UNIT_RX) { +        lo_iface->set_output_power((actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power((actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_M1DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "WBX tune: actual frequency %f MHz" -    ) % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit(); +      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index 721f5ed45..dc351af1d 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -16,8 +16,6 @@  //  #include "db_wbx_common.hpp" -#include "adf4351_regs.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/utils/log.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp> @@ -83,6 +81,8 @@ static int tx_pga0_gain_to_iobits(double &gain){  wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      //register our handle on the primary wbx_base instance      self_base = _self_wbx_base; +    _txlo = adf435x_iface::make_adf4351(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4351(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_RX, _1));      ////////////////////////////////////////////////////////////////////      // Register RX properties @@ -217,116 +217,46 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar                                                                    : self_base->get_tx_subtree();      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); +    double reference_freq = self_base->get_iface()->get_clock_rate(unit); -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (adf4351_regs_t::PRESCALER_4_5, 23) -        (adf4351_regs_t::PRESCALER_8_9, 75) -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) -        (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV32) -        (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) -    ; +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_reference_freq(reference_freq); -    double reference_freq = self_base->get_iface()->get_clock_rate(unit);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //frequency must 2x the target frequency.  This introduces a 180 degree phase      //ambiguity when trying to synchronize the phase of multiple boards.      double synth_target_freq = target_freq * 2; -    adf4351_regs_t::prescaler_t prescaler = -        synth_target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(synth_target_freq > 3.6e9 ? +        adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 64);      //The feedback of the divided frequency must be disabled whenever the target frequency      //divided by the minimum PFD frequency cannot meet the minimum integer divider (N) value.      //If it is disabled, additional phase ambiguity will be introduced.  With a minimum PFD      //frequency of 10 MHz, synthesizer frequencies below 230 MHz (LO frequencies below 115 MHz)      //will have too much ambiguity to synchronize. -    tuning_constraints.feedback_after_divider = -        (int(synth_target_freq / 10e6) >= prescaler_to_min_int_div[prescaler]); +    lo_iface->set_feedback_select( +        (int(synth_target_freq / 10e6) >= lo_iface->get_int_range().start() ? +            adf435x_iface::FB_SEL_DIVIDED : adf435x_iface::FB_SEL_FUNDAMENTAL)); -    double synth_actual_freq = 0; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); +    double synth_actual_freq = lo_iface->set_frequency(synth_target_freq, is_int_n);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //actual_freq must /2 the synth_actual_freq      double actual_freq = synth_actual_freq / 2; -    //load the register values -    adf4351_regs_t regs; - -    if (unit == dboard_iface::UNIT_RX) -        regs.output_power = (actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? adf4351_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4351_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = (actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? adf4351_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4351_regs_t::OUTPUT_POWER_M1DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = tuning_constraints.feedback_after_divider ? -                                    adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : -                                    adf4351_regs_t::CLOCK_DIV_MODE_FAST_LOCK; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4351_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4351_regs_t::LDF_INT_N : -                                    adf4351_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4351_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    boost::uint16_t rx_id = self_base->get_rx_id().to_uint16(); -    std::string board_name = (rx_id == 0x0081) ? "WBX-120" : "WBX"; -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "%s SPI Reg (0x%02x): 0x%08x" -        ) % board_name.c_str() % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    if (unit == dboard_iface::UNIT_RX) { +        lo_iface->set_output_power((actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power((actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_M1DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "%s tune: actual frequency %f MHz" -    ) % board_name.c_str() % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit();      return actual_freq;  } | 
