diff options
Diffstat (limited to 'host')
| -rw-r--r-- | host/lib/ic_reg_maps/CMakeLists.txt | 5 | ||||
| -rwxr-xr-x | host/lib/ic_reg_maps/gen_adf5355_regs.py | 166 | ||||
| -rw-r--r-- | host/lib/usrp/common/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/common/adf5355.cpp | 376 | ||||
| -rw-r--r-- | host/lib/usrp/common/adf5355.hpp | 57 | 
5 files changed, 605 insertions, 0 deletions
| diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index 1de50579f..f8ccd9814 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -122,4 +122,9 @@ LIBUHD_PYTHON_GEN_SOURCE(      ${CMAKE_CURRENT_BINARY_DIR}/lmk04816_regs.hpp  ) +LIBUHD_PYTHON_GEN_SOURCE( +    ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf5355_regs.py +    ${CMAKE_CURRENT_BINARY_DIR}/adf5355_regs.hpp +) +  SET(LIBUHD_PYTHON_GEN_SOURCE_DEPS) diff --git a/host/lib/ic_reg_maps/gen_adf5355_regs.py b/host/lib/ic_reg_maps/gen_adf5355_regs.py new file mode 100755 index 000000000..db7cc09a9 --- /dev/null +++ b/host/lib/ic_reg_maps/gen_adf5355_regs.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +# +# Copyright 2010 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/>. +# + +######################################################################## +# Template for raw text data describing registers +# name addr[bit range inclusive] default optional enums +######################################################################## +REGS_TMPL="""\ +######################################################################## +## address 0 +######################################################################## +int_16_bit              0[4:19]     0 +prescaler               0[20]       0       4_5, 8_9 +autocal_en              0[21]       0       disabled, enabled +reg0_reserved0          0[22:31]    0x000 +######################################################################## +## address 1 +######################################################################## +frac1_24_bit            1[4:27]     0 +reg1_reserved0          1[28:31]    0x0 +######################################################################## +## address 2 +######################################################################## +mod2_14_bit             2[4:17]     0 +frac2_14_bit            2[18:31]    0 +######################################################################## +## address 3 +######################################################################## +phase_24_bit            3[4:27]     0 +phase_adjust            3[28]       0       disabled, enabled +phase_resync            3[29]       0       disabled, enabled +sd_load_reset           3[30]       0       on_reg0_update, disabled +##reserved              3[31]       0 +######################################################################## +## address 4 +######################################################################## +counter_reset           4[4]        0       disabled, enabled +cp_three_state          4[5]        0       disabled, enabled +power_down              4[6]        0       disabled, enabled +pd_polarity             4[7]        1       negative, positive +mux_logic               4[8]        0       1_8V, 3_3V +ref_mode                4[9]        0       single, diff +<% current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) %>\ +charge_pump_current     4[10:13]    0       ${current_setting_enums} +double_buff_div         4[14]       0       disabled, enabled +r_counter_10_bit        4[15:24]    0 +reference_divide_by_2   4[25]       1       disabled, enabled +reference_doubler       4[26]       0       disabled, enabled +muxout                  4[27:29]    1       3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved +reg4_reserved0          4[30:31]    0 +######################################################################## +## address 5 +######################################################################## +reg5_reserved0          5[4:31]     0x0080002 +######################################################################## +## address 6 +######################################################################## +output_power            6[4:5]      0       m4dbm, m1dbm, 2dbm, 5dbm +rf_out_a_enabled        6[6]        0       disabled, enabled +reg6_reserved0          6[7:9]      0x0 +rf_out_b_enabled        6[10]       1       enabled, disabled +mute_till_lock_detect   6[11]       0       mute_disabled, mute_enabled +reg6_reserved1          6[12]       0 +cp_bleed_current        6[13:20]    0 +rf_divider_select       6[21:23]    0       div1, div2, div4, div8, div16, div32, div64 +feedback_select         6[24]       0       divided, fundamental +reg6_reserved2          6[25:28]    0xA +negative_bleed          6[29]       0       disabled, enabled +gated_bleed             6[30]       0       disabled, enabled +reg6_reserved3          6[31]       0 +######################################################################## +## address 7 +######################################################################## +ld_mode                 7[4]        0       frac_n, int_n +frac_n_ld_precision     7[5:6]      0       5ns, 6ns, 8ns, 12ns +loss_of_lock_mode       7[7]        0       disabled, enabled +ld_cyc_count            7[8:9]      0       1024, 2048, 4096, 8192 +reg7_reserved0          7[10:24]    0x0 +le_sync                 7[25]       0       disabled, le_synced_to_refin +reg7_reserved1          7[26:31]    0x4 +######################################################################## +## address 8 +######################################################################## +reg8_reserved0          8[4:31]     0x102D402 +######################################################################## +## address 9 +######################################################################## +synth_lock_timeout      9[4:8]      0 +auto_level_timeout      9[9:13]     0 +timeout                 9[14:23]    0 +vco_band_div            9[24:31]    0 +######################################################################## +## address 10 +######################################################################## +adc_enable             10[4]        0       disabled, enabled +adc_conversion         10[5]        0       disabled, enabled +adc_clock_divider      10[6:13]     1 +reg10_reserved0        10[14:31]    0x300 +######################################################################## +## address 11 +######################################################################## +reg11_reserved0        11[4:31]     0x0061300 +######################################################################## +## address 12 +######################################################################## +reg12_reserved0        12[4:15]     0x041 +phase_resync_clk_div   12[16:31]    0 +""" + +######################################################################## +# Template for methods in the body of the struct +######################################################################## +BODY_TMPL="""\ +enum addr_t{ +    ADDR_R0 = 0, +    ADDR_R1 = 1, +    ADDR_R2 = 2, +    ADDR_R3 = 3, +    ADDR_R4 = 4, +    ADDR_R5 = 5, +    ADDR_R6 = 6, +    ADDR_R7 = 7, +    ADDR_R8 = 8, +    ADDR_R9 = 9, +    ADDR_R10 = 10, +    ADDR_R11 = 11, +    ADDR_R12 = 12 +}; + +boost::uint32_t get_reg(boost::uint8_t addr){ +    boost::uint32_t reg = addr & 0xF; +    switch(addr){ +    % for addr in range(12+1): +    case ${addr}: +        % for reg in filter(lambda r: r.get_addr() == addr, regs): +        reg |= (boost::uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()}; +        % endfor +        break; +    % endfor +    } +    return reg; +} +""" + +if __name__ == '__main__': +    import common; common.generate( +        name='adf5355_regs', +        regs_tmpl=REGS_TMPL, +        body_tmpl=BODY_TMPL, +        file=__file__, +    ) diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 55dc92023..9f4cf09b5 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -30,6 +30,7 @@ INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver")  LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/adf435x.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/adf5355.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/adf5355.cpp b/host/lib/usrp/common/adf5355.cpp new file mode 100644 index 000000000..5162698a3 --- /dev/null +++ b/host/lib/usrp/common/adf5355.cpp @@ -0,0 +1,376 @@ +// +// 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 "adf5355.hpp" +#include "adf5355_regs.hpp" +#include <uhd/utils/math.hpp> +#include <boost/math/common_factor_rt.hpp> //gcd +#include <boost/thread.hpp> + +using namespace uhd; + +template<typename data_t> +data_t clamp(data_t val, data_t min, data_t max) { +    return (val < min) ? min : ((val > max) ? max : val); +} + +template<typename data_t> +double todbl(data_t val) { +    return static_cast<double>(val); +} + +static const double ADF5355_DOUBLER_MAX_REF_FREQ    = 60e6; +static const double ADF5355_MAX_FREQ_PFD            = 125e6; +static const double ADF5355_PRESCALER_THRESH        = 7e9; + +static const double ADF5355_MIN_VCO_FREQ            = 3.4e9; +static const double ADF5355_MAX_VCO_FREQ            = 6.8e9; +static const double ADF5355_MAX_OUT_FREQ            = 6.8e9; +static const double ADF5355_MIN_OUT_FREQ            = (3.4e9 / 64); +static const double ADF5355_MAX_OUTB_FREQ           = (6.8e9 * 2); +static const double ADF5355_MIN_OUTB_FREQ           = (3.4e9 * 2); + +static const double ADF5355_PHASE_RESYNC_TIME       = 400e-6; + +static const boost::uint32_t ADF5355_MOD1           = 16777216; +static const boost::uint32_t ADF5355_MAX_MOD2       = 16384; +static const boost::uint16_t ADF5355_MIN_INT_PRESCALER_89 = 75; + +class adf5355_impl : public adf5355_iface +{ +public: +    adf5355_impl(write_fn_t write_fn) : +        _write_fn(write_fn), +        _regs(), +        _rewrite_regs(true), +        _wait_time_us(0), +        _ref_freq(0.0), +        _pfd_freq(0.0), +        _fb_after_divider(false) +    { +        _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED; +        _regs.cp_three_state = adf5355_regs_t::CP_THREE_STATE_DISABLED; +        _regs.power_down = adf5355_regs_t::POWER_DOWN_DISABLED; +        _regs.pd_polarity = adf5355_regs_t::PD_POLARITY_POSITIVE; +        _regs.mux_logic = adf5355_regs_t::MUX_LOGIC_3_3V; +        _regs.ref_mode = adf5355_regs_t::REF_MODE_SINGLE; +        _regs.muxout = adf5355_regs_t::MUXOUT_DLD; +        _regs.double_buff_div = adf5355_regs_t::DOUBLE_BUFF_DIV_DISABLED; + +        _regs.rf_out_a_enabled = adf5355_regs_t::RF_OUT_A_ENABLED_ENABLED; +        _regs.rf_out_b_enabled = adf5355_regs_t::RF_OUT_B_ENABLED_DISABLED; +        _regs.mute_till_lock_detect = adf5355_regs_t::MUTE_TILL_LOCK_DETECT_MUTE_DISABLED; +        _regs.ld_mode = adf5355_regs_t::LD_MODE_FRAC_N; +        _regs.frac_n_ld_precision = adf5355_regs_t::FRAC_N_LD_PRECISION_5NS; +        _regs.ld_cyc_count = adf5355_regs_t::LD_CYC_COUNT_1024; +        _regs.le_sync = adf5355_regs_t::LE_SYNC_LE_SYNCED_TO_REFIN; +        _regs.phase_resync = adf5355_regs_t::PHASE_RESYNC_DISABLED; +        _regs.reference_divide_by_2 = adf5355_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +        _regs.reference_doubler = adf5355_regs_t::REFERENCE_DOUBLER_DISABLED; + +        _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED; +        _regs.prescaler = adf5355_regs_t::PRESCALER_4_5; +        _regs.charge_pump_current = adf5355_regs_t::CHARGE_PUMP_CURRENT_0_94MA; + +        _regs.gated_bleed = adf5355_regs_t::GATED_BLEED_DISABLED; +        _regs.negative_bleed = adf5355_regs_t::NEGATIVE_BLEED_ENABLED; +        _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; +        _regs.output_power = adf5355_regs_t::OUTPUT_POWER_5DBM; +        _regs.cp_bleed_current = 2; +        _regs.r_counter_10_bit = 8; + + +        _regs.ld_cyc_count = adf5355_regs_t::LD_CYC_COUNT_1024; +        _regs.loss_of_lock_mode = adf5355_regs_t::LOSS_OF_LOCK_MODE_DISABLED; +        _regs.frac_n_ld_precision = adf5355_regs_t::FRAC_N_LD_PRECISION_5NS; +        _regs.ld_mode = adf5355_regs_t::LD_MODE_FRAC_N; + +        _regs.vco_band_div = 3; +        _regs.timeout = 11; +        _regs.auto_level_timeout = 30; +        _regs.synth_lock_timeout = 12; + +        _regs.adc_clock_divider = 16; +        _regs.adc_conversion = adf5355_regs_t::ADC_CONVERSION_ENABLED; +        _regs.adc_enable = adf5355_regs_t::ADC_ENABLE_ENABLED; + +        _regs.phase_resync_clk_div = 0; +    } + +    ~adf5355_impl() +    { +        _regs.power_down = adf5355_regs_t::POWER_DOWN_ENABLED; +        commit(); +    } + +    void set_feedback_select(feedback_sel_t fb_sel) +    { +        _fb_after_divider = (fb_sel == FB_SEL_DIVIDED); + +        if (_fb_after_divider) { +            _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_DIVIDED; +        } else { +            _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; +        } +    } + +    void set_reference_freq(double fref, bool force = false) +    { +        //Skip the body if the reference frequency does not change +        if (uhd::math::frequencies_are_equal(fref, _ref_freq) and (not force)) +            return; + +        _ref_freq = fref; + +        //----------------------------------------------------------- +        //Set reference settings + +        //Reference doubler for 50% duty cycle +        bool doubler_en = (_ref_freq <= ADF5355_DOUBLER_MAX_REF_FREQ); + +        /* Calculate and maximize PFD frequency */ +        // TODO Target PFD should be configurable +        /* TwinRX requires PFD of 6.25 MHz or less */ +        const double TWINRX_PFD_FREQ = 6.25e6; +        _pfd_freq = TWINRX_PFD_FREQ; + +        int ref_div_factor = 16; + +        //Reference divide-by-2 for 50% duty cycle +        // if R even, move one divide by 2 to to regs.reference_divide_by_2 +        bool div2_en = (ref_div_factor % 2 == 0); +        if (div2_en) { +            ref_div_factor /= 2; +        } + +        _regs.reference_divide_by_2 = div2_en ? +            adf5355_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : +            adf5355_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +        _regs.reference_doubler = doubler_en ? +            adf5355_regs_t::REFERENCE_DOUBLER_ENABLED : +            adf5355_regs_t::REFERENCE_DOUBLER_DISABLED; +        _regs.r_counter_10_bit = ref_div_factor; +        UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((boost::uint16_t)~0x3FF)) == 0); + +        //----------------------------------------------------------- +        //Set timeouts (code from ADI driver) +        _regs.timeout = clamp<boost::uint16_t>( +            static_cast<boost::uint16_t>(ceil(_pfd_freq / (20e3 * 30))), 1, 1023); +        UHD_ASSERT_THROW((_regs.timeout & ((boost::uint16_t)~0x3FF)) == 0); +        _regs.synth_lock_timeout = +            static_cast<boost::uint8_t>(ceil((_pfd_freq * 2) / (100e3 * _regs.timeout))); +        UHD_ASSERT_THROW((_regs.synth_lock_timeout & ((boost::uint16_t)~0x1F)) == 0); +        _regs.auto_level_timeout = +            static_cast<boost::uint8_t>(ceil((_pfd_freq * 5) / (100e3 * _regs.timeout))); + +        //----------------------------------------------------------- +        //Set VCO band divider +        _regs.vco_band_div = +            static_cast<boost::uint8_t>(ceil(_pfd_freq / 2.4e6)); + +        //----------------------------------------------------------- +        //Set ADC delay (code from ADI driver) +        _regs.adc_enable = adf5355_regs_t::ADC_ENABLE_ENABLED; +        _regs.adc_conversion = adf5355_regs_t::ADC_CONVERSION_ENABLED; +        _regs.adc_clock_divider = clamp<boost::uint8_t>( +            static_cast<boost::uint8_t>(ceil(((_pfd_freq / 100e3) - 2) / 4)), 1, 255); +        _wait_time_us = static_cast<boost::uint32_t>( +            ceil(16e6 / (_pfd_freq / ((4 * _regs.adc_clock_divider) + 2)))); + +        //----------------------------------------------------------- +        //Phase resync +        _regs.phase_resync = adf5355_regs_t::PHASE_RESYNC_DISABLED; // Disabled during development +        _regs.phase_adjust = adf5355_regs_t::PHASE_ADJUST_DISABLED; +        _regs.sd_load_reset = adf5355_regs_t::SD_LOAD_RESET_ON_REG0_UPDATE; +        _regs.phase_resync_clk_div = static_cast<boost::uint16_t>( +            floor(ADF5355_PHASE_RESYNC_TIME * _pfd_freq)); + +        _rewrite_regs = true; +    } + +    void set_output_power(output_power_t power) +    { +        adf5355_regs_t::output_power_t setting; +        switch (power) { +            case OUTPUT_POWER_M4DBM: setting = adf5355_regs_t::OUTPUT_POWER_M4DBM; break; +            case OUTPUT_POWER_M1DBM: setting = adf5355_regs_t::OUTPUT_POWER_M1DBM; break; +            case OUTPUT_POWER_2DBM:  setting = adf5355_regs_t::OUTPUT_POWER_2DBM; break; +            case OUTPUT_POWER_5DBM:  setting = adf5355_regs_t::OUTPUT_POWER_5DBM; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +        if (_regs.output_power != setting) _rewrite_regs = true; +        _regs.output_power = setting; +    } + +    void set_output_enable(output_t output, bool enable) { + +        switch (output) { +            case RF_OUTPUT_A: _regs.rf_out_a_enabled = enable ? adf5355_regs_t::RF_OUT_A_ENABLED_ENABLED : +                                                                adf5355_regs_t::RF_OUT_A_ENABLED_DISABLED; +                              break; +            case RF_OUTPUT_B: _regs.rf_out_b_enabled = enable ? adf5355_regs_t::RF_OUT_B_ENABLED_ENABLED : +                                                                adf5355_regs_t::RF_OUT_B_ENABLED_DISABLED; +                              break; +        } +    } + +    void set_muxout_mode(muxout_t mode) +    { +        switch (mode) { +            case MUXOUT_3STATE: _regs.muxout = adf5355_regs_t::MUXOUT_3STATE; break; +            case MUXOUT_DVDD:   _regs.muxout = adf5355_regs_t::MUXOUT_DVDD; break; +            case MUXOUT_DGND:   _regs.muxout = adf5355_regs_t::MUXOUT_DGND; break; +            case MUXOUT_RDIV:   _regs.muxout = adf5355_regs_t::MUXOUT_RDIV; break; +            case MUXOUT_NDIV:   _regs.muxout = adf5355_regs_t::MUXOUT_NDIV; break; +            case MUXOUT_ALD:    _regs.muxout = adf5355_regs_t::MUXOUT_ANALOG_LD; break; +            case MUXOUT_DLD:    _regs.muxout = adf5355_regs_t::MUXOUT_DLD; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    double set_frequency(double target_freq, double freq_resolution, bool flush = false) +    { +        if (target_freq > ADF5355_MAX_OUT_FREQ or target_freq < ADF5355_MIN_OUT_FREQ) { +            throw uhd::runtime_error("requested frequency out of range."); +        } +        if ((boost::uint32_t) freq_resolution == 0) { +            throw uhd::runtime_error("requested resolution cannot be less than 1."); +        } + +        /* Calculate target VCOout frequency */ +        //Increase RF divider until acceptable VCO frequency +        double target_vco_freq = target_freq; +        boost::uint32_t rf_divider = 1; +        while (target_vco_freq < ADF5355_MIN_VCO_FREQ && rf_divider < 64) { +            target_vco_freq *= 2; +            rf_divider *= 2; +        } + +        switch (rf_divider) { +            case 1:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV1;  break; +            case 2:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV2;  break; +            case 4:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV4;  break; +            case 8:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV8;  break; +            case 16: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV16; break; +            case 32: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV32; break; +            case 64: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV64; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } + +        //Compute fractional PLL params +        double prescaler_input_freq = target_vco_freq; +        if (_fb_after_divider) { +            prescaler_input_freq /= rf_divider; +        } + +        double N = prescaler_input_freq / _pfd_freq; +        boost::uint16_t INT = static_cast<boost::uint16_t>(floor(N)); +        boost::uint32_t FRAC1 = static_cast<boost::uint32_t>(floor((N - INT) * ADF5355_MOD1)); +        double residue = ADF5355_MOD1 * (N - (INT + FRAC1 / ADF5355_MOD1)); + +        double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution)); +        boost::uint16_t MOD2 = static_cast<boost::uint16_t>(floor(_pfd_freq / gcd)); + +        if (MOD2 > ADF5355_MAX_MOD2) { +            MOD2 = ADF5355_MAX_MOD2; +        } +        boost::uint16_t FRAC2 = ceil(residue * MOD2); + +        double coerced_vco_freq = _pfd_freq * ( +            todbl(INT) + ( +                (todbl(FRAC1) + +                    (todbl(FRAC2) / todbl(MOD2))) +                / todbl(ADF5355_MOD1) +            ) +        ); + +        double coerced_out_freq = coerced_vco_freq / rf_divider; + +        /* Update registers */ +        _regs.int_16_bit = INT; +        _regs.frac1_24_bit = FRAC1; +        _regs.frac2_14_bit = FRAC2; +        _regs.mod2_14_bit = MOD2; +        _regs.phase_24_bit = 0; + +/* +        if (_regs.int_16_bit >= ADF5355_MIN_INT_PRESCALER_89) { +            _regs.prescaler = adf5355_regs_t::PRESCALER_8_9; +        } else { +            _regs.prescaler = adf5355_regs_t::PRESCALER_4_5; +        } + +        /* ADI: Tests have shown that the optimal bleed set is the following: +         * 4/N < IBLEED/ICP < 10/N */ +/* +        boost::uint32_t cp_curr_ua = +            (static_cast<boost::uint32_t>(_regs.charge_pump_current) + 1) * 315; +        _regs.cp_bleed_current = clamp<boost::uint8_t>( +            ceil((todbl(400)*cp_curr_ua) / (_regs.int_16_bit*375)), 1, 255); +        _regs.negative_bleed = adf5355_regs_t::NEGATIVE_BLEED_ENABLED; +        _regs.gated_bleed = adf5355_regs_t::GATED_BLEED_DISABLED; +*/ + +        if (flush) commit(); +        return coerced_out_freq; +    } + +    void commit() +    { +        if (_rewrite_regs) { +            //For a full state sync write registers in reverse order 12 - 0 +            addr_vtr_t regs; +            for (int addr = 12; addr >= 0; addr--) { +                regs.push_back(_regs.get_reg(boost::uint32_t(addr))); +            } +            _write_fn(regs); +            _rewrite_regs = false; + +        } else { +            //Frequency update sequence from data sheet +            static const size_t ONE_REG = 1; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6))); +            _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_ENABLED; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4))); +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2))); +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1))); +            _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_DISABLED; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +            _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4))); +            boost::this_thread::sleep(boost::posix_time::microsec(_wait_time_us)); +            _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +        } +    } + +private: //Members +    typedef std::vector<boost::uint32_t> addr_vtr_t; + +    write_fn_t      _write_fn; +    adf5355_regs_t  _regs; +    bool            _rewrite_regs; +    boost::uint32_t _wait_time_us; +    double          _ref_freq; +    double          _pfd_freq; +    double          _fb_after_divider; +}; + +adf5355_iface::sptr adf5355_iface::make(write_fn_t write) +{ +    return sptr(new adf5355_impl(write)); +} diff --git a/host/lib/usrp/common/adf5355.hpp b/host/lib/usrp/common/adf5355.hpp new file mode 100644 index 000000000..fb262cc9f --- /dev/null +++ b/host/lib/usrp/common/adf5355.hpp @@ -0,0 +1,57 @@ +// +// 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_ADF5355_HPP +#define INCLUDED_ADF5355_HPP + +#include <boost/function.hpp> +#include <vector> + +class adf5355_iface +{ +public: +    typedef boost::shared_ptr<adf5355_iface> sptr; +    typedef boost::function<void(std::vector<boost::uint32_t>)> write_fn_t; + +    static sptr make(write_fn_t write); + +    virtual ~adf5355_iface() {} + +    enum output_t { RF_OUTPUT_A, RF_OUTPUT_B }; + +    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 }; + +    enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD }; + +    virtual void set_reference_freq(double fref, bool force = false) = 0; + +    virtual void set_feedback_select(feedback_sel_t fb_sel) = 0; + +    virtual void set_output_power(output_power_t power) = 0; + +    virtual void set_output_enable(output_t output, bool enable) = 0; + +    virtual void set_muxout_mode(muxout_t mode) = 0; + +    virtual double set_frequency(double target_freq, double freq_resolution, bool flush = false) = 0; + +    virtual void commit(void) = 0; +}; + +#endif // INCLUDED_ADF5355_HPP | 
