diff options
Diffstat (limited to 'host/lib/usrp')
| -rw-r--r-- | host/lib/usrp/common/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/common/adf435x_common.cpp | 156 | ||||
| -rw-r--r-- | host/lib/usrp/common/adf435x_common.hpp | 63 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_cbx.cpp | 56 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.cpp | 186 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.hpp | 52 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_version3.cpp | 22 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_version4.cpp | 32 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_common.cpp | 35 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_common.hpp | 39 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_simple.cpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_version2.cpp | 191 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_version3.cpp | 192 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_version4.cpp | 214 | 
14 files changed, 583 insertions, 658 deletions
| diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 1728b63f9..b99464873 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})  LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/adf435x_common.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp diff --git a/host/lib/usrp/common/adf435x_common.cpp b/host/lib/usrp/common/adf435x_common.cpp new file mode 100644 index 000000000..e9d018fec --- /dev/null +++ b/host/lib/usrp/common/adf435x_common.cpp @@ -0,0 +1,156 @@ +// +// 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 <uhd/types/tune_request.hpp> +#include <uhd/utils/log.hpp> + +using namespace uhd; + +/*********************************************************************** + * ADF 4350/4351 Tuning Utility + **********************************************************************/ +adf435x_tuning_settings tune_adf435x_synth( +    double target_freq, +    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_rf = f_vco/RFdiv) +     * f_actual = f_rf/2 +     */ +    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; + +        //ignore fractional part of tuning +        //N is computed from target_freq and not vco_freq because the feedback +        //mode is set to FEEDBACK_SELECT_DIVIDED +        N = boost::uint16_t(std::floor(target_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 +    //N is computed from target_freq and not vco_freq because the feedback +    //mode is set to FEEDBACK_SELECT_DIVIDED +    FRAC = static_cast<boost::uint16_t>((target_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; + +    //actual frequency calculation +    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(D?2:1)/(R*(T?2:1))); + +    //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, 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 = BS; +    settings.rf_divider = RFdiv; +    settings.feedback_after_divider = true; + +    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): VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" +        ) % (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 new file mode 100644 index 000000000..715b1fd53 --- /dev/null +++ b/host/lib/usrp/common/adf435x_common.hpp @@ -0,0 +1,63 @@ +// +// 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; +    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; +    bool            feedback_after_divider; +}; + +adf435x_tuning_settings tune_adf435x_synth( +    double target_freq, +    double ref_freq, +    const adf435x_tuning_constraints& constraints, +    double& actual_freq +); + +#endif /* INCLUDED_ADF435X_COMMON_HPP */ diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp index 04399e64e..ae41a7971 100644 --- a/host/lib/usrp/dboard/db_cbx.cpp +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-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 @@ -46,6 +46,16 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)          "CBX tune: target frequency %f Mhz"      ) % (target_freq/1e6) << std::endl; +    /* +     * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to +     * tune in Integer-N mode, which can result in better spur +     * performance on some mixers. The default is fractional tuning. +     */ +    property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() +                                                                  : self_base->get_tx_subtree(); +    device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get(); +    bool is_int_n = (tune_args.get("mode_n","") == "int-n"); +      //clip the input      target_freq = cbx_freq_range.clip(target_freq); @@ -64,14 +74,13 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)          (64,  max2870_regs_t::RF_DIVIDER_SELECT_DIV64)          (128, max2870_regs_t::RF_DIVIDER_SELECT_DIV128)      ; -     +      double actual_freq, pfd_freq;      double ref_freq = self_base->get_iface()->get_clock_rate(unit); -    max2870_regs_t::int_n_mode_t int_n_mode;      int R=0, BS=0, N=0, FRAC=0, MOD=4095;      int RFdiv = 1;      max2870_regs_t::reference_divide_by_2_t T     = max2870_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    max2870_regs_t::reference_doubler_t     D     = max2870_regs_t::REFERENCE_DOUBLER_DISABLED;     +    max2870_regs_t::reference_doubler_t     D     = max2870_regs_t::REFERENCE_DOUBLER_DISABLED;      //Reference doubler for 50% duty cycle      // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 @@ -115,11 +124,15 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)          //Fractional-N calculation          FRAC = int((vco_freq/pfd_freq - N)*MOD); -        //are we in int-N or frac-N mode? -        int_n_mode = (FRAC == 0) ? max2870_regs_t::INT_N_MODE_INT_N : max2870_regs_t::INT_N_MODE_FRAC_N; +        if(is_int_n) { +            if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target +                N++; +            } +            FRAC = 0; +        }          //keep N within int divider requirements -        if(int_n_mode == max2870_regs_t::INT_N_MODE_INT_N) { +        if(is_int_n) {              if(N < int_n_mode_div_range.start()) continue;              if(N > int_n_mode_div_range.stop()) continue;          } else { @@ -144,17 +157,20 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)      //actual frequency calculation      actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); +    boost::uint16_t rx_id = self_base->get_rx_id().to_uint16(); +    std::string board_name = (rx_id == 0x0085) ? "CBX-120" : "CBX";      UHD_LOGV(often) -        << boost::format("CBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl -        << boost::format("CBX tune: 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 -        << boost::format("CBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" -            ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; +        << boost::format("%s Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f" +            ) % board_name.c_str() % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl +        << boost::format("%s tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s" +            ) % board_name.c_str() % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") << std::endl +        << boost::format("%s Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" +            ) % board_name.c_str() % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;      //load the register values      max2870_regs_t regs; -    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))          regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM;      else          regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM; @@ -163,7 +179,7 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)      max2870_regs_t::cpl_t cpl;      max2870_regs_t::ldf_t ldf;      max2870_regs_t::cpoc_t cpoc; -    if(int_n_mode == max2870_regs_t::INT_N_MODE_INT_N) { +    if(is_int_n) {          cpl = max2870_regs_t::CPL_DISABLED;          cpoc = max2870_regs_t::CPOC_ENABLED;          ldf = max2870_regs_t::LDF_INT_N; @@ -184,10 +200,10 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)      regs.band_select_clock_div = BS;      UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));      regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; -    regs.int_n_mode = int_n_mode; +    regs.int_n_mode = (is_int_n) ? max2870_regs_t::INT_N_MODE_INT_N : max2870_regs_t::INT_N_MODE_FRAC_N;      regs.cpl = cpl;      regs.ldf = ldf; -    regs.cpoc = cpoc;     +    regs.cpoc = cpoc;      //write the registers      //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) @@ -195,8 +211,8 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)      for(addr=5; addr>=0; addr--){          UHD_LOGV(often) << boost::format( -            "CBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; +            "%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 @@ -205,8 +221,8 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)      //return the actual frequency      UHD_LOGV(often) << boost::format( -        "CBX tune: actual frequency %f Mhz" -    ) % (actual_freq/1e6) << std::endl; +        "%s tune: actual frequency %f Mhz" +    ) % board_name.c_str() % (actual_freq/1e6) << std::endl;      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index 5b713c6d7..49e30949e 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-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 @@ -21,137 +21,6 @@ using namespace uhd;  using namespace uhd::usrp;  using namespace boost::assign; -/*********************************************************************** - * ADF 4350/4351 Tuning Utility - **********************************************************************/ -sbx_xcvr::sbx_versionx::adf435x_tuning_settings sbx_xcvr::sbx_versionx::_tune_adf435x_synth( -    double target_freq, -    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_rf = f_vco/RFdiv) -     * f_actual = f_rf/2 -     */ -    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; - -        //ignore fractional part of tuning -        //N is computed from target_freq and not vco_freq because the feedback -        //mode is set to FEEDBACK_SELECT_DIVIDED -        N = boost::uint16_t(std::floor(target_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 -    //N is computed from target_freq and not vco_freq because the feedback -    //mode is set to FEEDBACK_SELECT_DIVIDED -    FRAC = static_cast<boost::uint16_t>((target_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; - -    //actual frequency calculation -    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(D?2:1)/(R*(T?2:1))); - -    //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, 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 = BS; -    settings.rf_divider = RFdiv; -    settings.feedback_after_divider = true; - -    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): VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" -        ) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << 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; -} -  /***********************************************************************   * Register the SBX dboard (min freq, max freq, rx div2, tx div2) @@ -164,6 +33,8 @@ UHD_STATIC_BLOCK(reg_sbx_dboards){      dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX");      dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4");      dboard_manager::register_dboard(0x0067, 0x0066, &make_sbx, "CBX"); +    dboard_manager::register_dboard(0x0083, 0x0082, &make_sbx, "SBX-120"); +    dboard_manager::register_dboard(0x0085, 0x0084, &make_sbx, "CBX-120");  } @@ -244,15 +115,23 @@ double sbx_xcvr::set_rx_gain(double gain, const std::string &name){   **********************************************************************/  sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      switch(get_rx_id().to_uint16()) { -        case 0x054: +        case 0x0054:              db_actual = sbx_versionx_sptr(new sbx_version3(this));              freq_range = sbx_freq_range;              break; -        case 0x065: +        case 0x0065:              db_actual = sbx_versionx_sptr(new sbx_version4(this));              freq_range = sbx_freq_range;              break; -        case 0x067: +        case 0x0067: +            db_actual = sbx_versionx_sptr(new cbx(this)); +            freq_range = cbx_freq_range; +            break; +        case 0x0083: +            db_actual = sbx_versionx_sptr(new sbx_version4(this)); +            freq_range = sbx_freq_range; +            break; +        case 0x0085:              db_actual = sbx_versionx_sptr(new cbx(this));              freq_range = cbx_freq_range;              break; @@ -264,9 +143,14 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      ////////////////////////////////////////////////////////////////////      // Register RX properties      //////////////////////////////////////////////////////////////////// -    if(get_rx_id() == 0x054) this->get_rx_subtree()->create<std::string>("name").set("SBXv3 RX"); -    else if(get_rx_id() == 0x065) this->get_rx_subtree()->create<std::string>("name").set("SBXv4 RX"); -    else if(get_rx_id() == 0x067) this->get_rx_subtree()->create<std::string>("name").set("CBX RX"); +    this->get_rx_subtree()->create<device_addr_t>("tune_args").set(device_addr_t()); + +    boost::uint16_t rx_id = get_rx_id().to_uint16(); +    if(rx_id == 0x0054) this->get_rx_subtree()->create<std::string>("name").set("SBXv3 RX"); +    else if(rx_id == 0x0065) this->get_rx_subtree()->create<std::string>("name").set("SBXv4 RX"); +    else if(rx_id == 0x0067) this->get_rx_subtree()->create<std::string>("name").set("CBX RX"); +    else if(rx_id == 0x0083) this->get_rx_subtree()->create<std::string>("name").set("SBX-120 RX"); +    else if(rx_id == 0x0085) this->get_rx_subtree()->create<std::string>("name").set("CBX-120 RX");      else this->get_rx_subtree()->create<std::string>("name").set("SBX/CBX RX");      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") @@ -290,16 +174,24 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      this->get_rx_subtree()->create<std::string>("connection").set("IQ");      this->get_rx_subtree()->create<bool>("enabled").set(true); //always enabled      this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); -    this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + +    //Value of bw low-pass dependent on board, we want complex double-sided +    double rx_bw = ((rx_id != 0x0083) && (rx_id != 0x0085)) ? 20.0e6 : 60.0e6; +    this->get_rx_subtree()->create<double>("bandwidth/value").set(2*rx_bw);      this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") -        .set(freq_range_t(2*20.0e6, 2*20.0e6)); +        .set(freq_range_t(2*rx_bw, 2*rx_bw));      ////////////////////////////////////////////////////////////////////      // Register TX properties      //////////////////////////////////////////////////////////////////// -    if(get_tx_id() == 0x055) this->get_tx_subtree()->create<std::string>("name").set("SBXv3 TX"); -    else if(get_tx_id() == 0x064) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX"); -    else if(get_tx_id() == 0x066) this->get_tx_subtree()->create<std::string>("name").set("CBX TX"); +    this->get_tx_subtree()->create<device_addr_t>("tune_args").set(device_addr_t()); + +    boost::uint16_t tx_id = get_tx_id().to_uint16(); +    if(tx_id == 0x0055) this->get_tx_subtree()->create<std::string>("name").set("SBXv3 TX"); +    else if(tx_id == 0x0064) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX"); +    else if(tx_id == 0x0066) this->get_tx_subtree()->create<std::string>("name").set("CBX TX"); +    else if(tx_id == 0x0082) this->get_tx_subtree()->create<std::string>("name").set("SBX-120 TX"); +    else if(tx_id == 0x0084) this->get_tx_subtree()->create<std::string>("name").set("CBX-120 TX");      else this->get_tx_subtree()->create<std::string>("name").set("SBX/CBX TX");      this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") @@ -323,9 +215,12 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      this->get_tx_subtree()->create<std::string>("connection").set("QI");      this->get_tx_subtree()->create<bool>("enabled").set(true); //always enabled      this->get_tx_subtree()->create<bool>("use_lo_offset").set(false); -    this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + +    //Value of bw low-pass dependent on board, we want complex double-sided +    double tx_bw = ((tx_id != 0x0082) && (tx_id != 0x0084)) ? 20.0e6 : 60.0e6; +    this->get_tx_subtree()->create<double>("bandwidth/value").set(2*tx_bw);      this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") -        .set(freq_range_t(2*20.0e6, 2*20.0e6)); +        .set(freq_range_t(2*tx_bw, 2*tx_bw));      //enable the clocks that we need      this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); @@ -493,3 +388,4 @@ void sbx_xcvr::flash_leds(void) {      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO));  } + diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index e9bb2434c..a4bbfde38 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-2013 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 @@ -15,14 +15,12 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // +#include <uhd/types/device_addr.hpp> +#include "../common/adf435x_common.hpp"  // Common IO Pins  #define LO_LPF_EN       (1 << 15) -#define SYNTH_CE        (1 << 3) -#define SYNTH_PDBRF     (1 << 2) -#define SYNTH_MUXOUT    (1 << 1)                // INPUT!!! -#define LOCKDET_MASK    (1 << 0)                // INPUT!!!  // TX IO Pins  #define TRSW            (1 << 14)               // 0 = TX, 1 = RX @@ -38,33 +36,29 @@  #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 -// RX Attenuator Pins -#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  #define TX_ATTN_MASK    (63 << TX_ATTN_SHIFT)   // valid bits of TX Attenuator Control  // Mixer functions -#define TX_MIXER_ENB    (SYNTH_PDBRF|TX_ENABLE) +#define TX_MIXER_ENB    (ADF435X_PDBRF|TX_ENABLE)  #define TX_MIXER_DIS    0 -#define RX_MIXER_ENB    (SYNTH_PDBRF) +#define RX_MIXER_ENB    (ADF435X_PDBRF)  #define RX_MIXER_DIS    0  // Pin functions  #define TX_LED_IO       (TX_LED_TXRX|TX_LED_LD)     // LED gpio lines, pull down for LED -#define TXIO_MASK       (LO_LPF_EN|TRSW|SYNTH_CE|SYNTH_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE) +#define TXIO_MASK       (LO_LPF_EN|TRSW|ADF435X_CE|ADF435X_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE)  #define RX_LED_IO       (RX_LED_RX1RX2|RX_LED_LD)   // LED gpio lines, pull down for LED -#define RXIO_MASK       (LO_LPF_EN|LNASW|SYNTH_CE|SYNTH_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE) +#define RXIO_MASK       (LO_LPF_EN|LNASW|ADF435X_CE|ADF435X_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE)  // Power functions -#define TX_POWER_UP     (SYNTH_CE) +#define TX_POWER_UP     (ADF435X_CE)  #define TX_POWER_DOWN   (DIS_POWER_TX) -#define RX_POWER_UP     (SYNTH_CE) +#define RX_POWER_UP     (ADF435X_CE)  #define RX_POWER_DOWN   (DIS_POWER_RX)  // Antenna constants @@ -181,34 +175,6 @@ protected:          ~sbx_versionx(void) {}          virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0; -    protected: -        struct adf435x_tuning_constraints { -            bool            force_frac0; -            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; -            bool            feedback_after_divider; -        }; - -        adf435x_tuning_settings _tune_adf435x_synth( -            double target_freq, -            double ref_freq, -            const adf435x_tuning_constraints& constraints, -            double& actual_freq);      };      /*! diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index b0c9cd18f..ef0126557 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-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 @@ -18,7 +18,8 @@  #include "adf4350_regs.hpp"  #include "db_sbx_common.hpp" - +#include "../common/adf435x_common.hpp" +#include <uhd/types/tune_request.hpp>  using namespace uhd;  using namespace uhd::usrp; @@ -45,6 +46,16 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar          "SBX tune: target frequency %f Mhz"      ) % (target_freq/1e6) << std::endl; +    /* +     * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to +     * tune in Integer-N mode, which can result in better spur +     * performance on some mixers. The default is fractional tuning. +     */ +    property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() +                                                                  : self_base->get_tx_subtree(); +    device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get(); +    bool is_int_n = (tune_args.get("mode_n","") == "int-n"); +      //clip the input      target_freq = sbx_freq_range.clip(target_freq); @@ -67,7 +78,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar      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 = false; +    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 @@ -75,7 +86,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar      tuning_constraints.rf_divider_range = uhd::range_t(1, 16);      double actual_freq; -    adf435x_tuning_settings tuning_settings = _tune_adf435x_synth( +    adf435x_tuning_settings tuning_settings = tune_adf435x_synth(          target_freq, self_base->get_iface()->get_clock_rate(unit),          tuning_constraints, actual_freq); @@ -106,6 +117,9 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar      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; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index 8d95b0655..6c0cebb4b 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-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 @@ -18,7 +18,8 @@  #include "adf4351_regs.hpp"  #include "db_sbx_common.hpp" - +#include "../common/adf435x_common.hpp" +#include <uhd/types/tune_request.hpp>  using namespace uhd;  using namespace uhd::usrp; @@ -46,6 +47,16 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar          "SBX tune: target frequency %f Mhz"      ) % (target_freq/1e6) << std::endl; +    /* +     * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to +     * tune in Integer-N mode, which can result in better spur +     * performance on some mixers. The default is fractional tuning. +     */ +    property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() +                                                                  : self_base->get_tx_subtree(); +    device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get(); +    bool is_int_n = (tune_args.get("mode_n","") == "int-n"); +      //clip the input      target_freq = sbx_freq_range.clip(target_freq); @@ -70,7 +81,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      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 = false; +    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 @@ -78,7 +89,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      tuning_constraints.rf_divider_range = uhd::range_t(1, 64);      double actual_freq; -    adf435x_tuning_settings tuning_settings = _tune_adf435x_synth( +    adf435x_tuning_settings tuning_settings = tune_adf435x_synth(          target_freq, self_base->get_iface()->get_clock_rate(unit),          tuning_constraints, actual_freq); @@ -109,6 +120,9 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      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; @@ -119,10 +133,12 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      //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( -            "SBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; +            "%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 @@ -131,8 +147,8 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      //return the actual frequency      UHD_LOGV(often) << boost::format( -        "SBX tune: actual frequency %f Mhz" -    ) % (actual_freq/1e6) << std::endl; +        "%s tune: actual frequency %f Mhz" +    ) % board_name.c_str() % (actual_freq/1e6) << std::endl;      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_wbx_common.cpp b/host/lib/usrp/dboard/db_wbx_common.cpp index 503e5aabf..97357bc90 100644 --- a/host/lib/usrp/dboard/db_wbx_common.cpp +++ b/host/lib/usrp/dboard/db_wbx_common.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-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 @@ -63,8 +63,11 @@ wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){      this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);      //////////////////////////////////////////////////////////////////// -    // Register RX properties +    // Register RX and TX properties      //////////////////////////////////////////////////////////////////// +    boost::uint16_t rx_id = this->get_rx_id().to_uint16(); + +    this->get_rx_subtree()->create<device_addr_t>("tune_args").set(device_addr_t());      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")          .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_RX));      BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){ @@ -79,30 +82,34 @@ wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){          .subscribe(boost::bind(&wbx_base::set_rx_enabled, this, _1))          .set(true); //start enabled      this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); -    this->get_rx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided + +    //Value of bw low-pass dependent on board, we want complex double-sided +    double bw = (rx_id != 0x0081) ? 20.0e6 : 60.0e6; +    this->get_rx_subtree()->create<double>("bandwidth/value").set(2*bw);      this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") -        .set(freq_range_t(2*20.0e6, 2*20.0e6)); +        .set(freq_range_t(2*bw, 2*bw)); +    this->get_tx_subtree()->create<double>("bandwidth/value").set(2*bw); +    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") +        .set(freq_range_t(2*bw, 2*bw)); -    //////////////////////////////////////////////////////////////////// -    // Register TX properties -    //////////////////////////////////////////////////////////////////// +    this->get_tx_subtree()->create<device_addr_t>("tune_args").set(device_addr_t());      this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked")          .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_TX));      this->get_tx_subtree()->create<std::string>("connection").set("IQ");      this->get_tx_subtree()->create<bool>("use_lo_offset").set(false); -    this->get_tx_subtree()->create<double>("bandwidth/value").set(2*20.0e6); //20MHz low-pass, we want complex double-sided -    this->get_tx_subtree()->create<meta_range_t>("bandwidth/range") -        .set(freq_range_t(2*20.0e6, 2*20.0e6));      // instantiate subclass foo -    switch(get_rx_id().to_uint16()) { -        case 0x053: +    switch(rx_id) { +        case 0x0053:              db_actual = wbx_versionx_sptr(new wbx_version2(this));              return; -        case 0x057: +        case 0x0057:              db_actual = wbx_versionx_sptr(new wbx_version3(this));              return; -        case 0x063: +        case 0x0063: +            db_actual = wbx_versionx_sptr(new wbx_version4(this)); +            return; +        case 0x0081:              db_actual = wbx_versionx_sptr(new wbx_version4(this));              return;          default: diff --git a/host/lib/usrp/dboard/db_wbx_common.hpp b/host/lib/usrp/dboard/db_wbx_common.hpp index d1beb160e..7609beb19 100644 --- a/host/lib/usrp/dboard/db_wbx_common.hpp +++ b/host/lib/usrp/dboard/db_wbx_common.hpp @@ -1,5 +1,5 @@  // -// Copyright 2011 Ettus Research LLC +// Copyright 2011,2013 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 @@ -18,15 +18,9 @@  #ifndef INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP  #define INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP -// Common IO Pins -#define ADF4350_CE      (1 << 3) -#define ADF4350_PDBRF   (1 << 2) -#define ADF4350_MUXOUT  (1 << 1)                // INPUT!!! -#define ADF4351_CE      (1 << 3) -#define ADF4351_PDBRF   (1 << 2) -#define ADF4351_MUXOUT  (1 << 1)                // INPUT!!! +#include <uhd/types/device_addr.hpp> -#define LOCKDET_MASK    (1 << 0)                // INPUT!!! +#include "../common/adf435x_common.hpp"  // TX IO Pins  #define TX_PUP_5V       (1 << 7)                // enables 5.0V power supply @@ -38,10 +32,6 @@  #define RX_PUP_3V       (1 << 6)                // enables 3.3V supply  #define RXBB_PDB        (1 << 4)                // on UNIT_RX, 1 powers up RX baseband -// RX Attenuator Pins -#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 (v3 only)  #define TX_ATTN_16      (1 << 14)  #define TX_ATTN_8       (1 << 5) @@ -51,17 +41,17 @@  #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  // Mixer functions -#define TX_MIXER_ENB    (TXMOD_EN|ADF4350_PDBRF)    // for v3, TXMOD_EN tied to ADF4350_PDBRF rather than separate +#define TX_MIXER_ENB    (TXMOD_EN|ADF435X_PDBRF)    // for v3, TXMOD_EN tied to ADF435X_PDBRF rather than separate  #define TX_MIXER_DIS    0 -#define RX_MIXER_ENB    (RXBB_PDB|ADF4350_PDBRF) +#define RX_MIXER_ENB    (RXBB_PDB|ADF435X_PDBRF)  #define RX_MIXER_DIS    0  // Power functions  #define TX_POWER_UP     (TX_PUP_5V|TX_PUP_3V) // high enables power supply  #define TX_POWER_DOWN   0 -#define RX_POWER_UP     (RX_PUP_5V|RX_PUP_3V|ADF4350_CE) // high enables power supply +#define RX_POWER_UP     (RX_PUP_5V|RX_PUP_3V|ADF435X_CE) // high enables power supply  #define RX_POWER_DOWN   0 @@ -86,6 +76,23 @@ namespace uhd{ namespace usrp{  static const uhd::dict<std::string, gain_range_t> wbx_rx_gain_ranges = boost::assign::map_list_of      ("PGA0", gain_range_t(0, 31.5, 0.5)); +static const freq_range_t wbx_tx_lo_5dbm = boost::assign::list_of +    (range_t(0.05e9, 1.7e9)) +    (range_t(1.9e9, 2.2e9)) +; + +static const freq_range_t wbx_tx_lo_m1dbm = boost::assign::list_of +    (range_t(1.7e9, 1.9e9)) +; + +static const freq_range_t wbx_rx_lo_5dbm = boost::assign::list_of +    (range_t(0.05e9, 1.4e9)) +; + +static const freq_range_t wbx_rx_lo_2dbm = boost::assign::list_of +    (range_t(1.4e9, 2.2e9)) +; +  /***********************************************************************   * The WBX dboard base class diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp index 4ba30255d..c8f2be155 100644 --- a/host/lib/usrp/dboard/db_wbx_simple.cpp +++ b/host/lib/usrp/dboard/db_wbx_simple.cpp @@ -71,6 +71,8 @@ UHD_STATIC_BLOCK(reg_wbx_simple_dboards){      dboard_manager::register_dboard(0x0057, 0x004f, &make_wbx_simple, "WBX v3 + Simple GDB");      dboard_manager::register_dboard(0x0063, 0x0062, &make_wbx_simple, "WBX v4");      dboard_manager::register_dboard(0x0063, 0x004f, &make_wbx_simple, "WBX v4 + Simple GDB"); +    dboard_manager::register_dboard(0x0081, 0x0080, &make_wbx_simple, "WBX-120"); +    dboard_manager::register_dboard(0x0081, 0x004f, &make_wbx_simple, "WBX-120 + Simple GDB");  }  /*********************************************************************** diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index 5f6118a91..2afdce4cd 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-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 @@ -17,6 +17,8 @@  #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>  #include <uhd/types/ranges.hpp> @@ -104,14 +106,14 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) {          .set(true); //start enabled      //set attenuator control bits -    int v2_iobits = ADF4350_CE; -    int v2_tx_mod = TXMOD_EN|ADF4350_PDBRF; +    int v2_iobits = ADF435X_CE; +    int v2_tx_mod = TXMOD_EN|ADF435X_PDBRF;      //set the gpio directions and atr controls      self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, v2_tx_mod); -    self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF4350_PDBRF); +    self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXBB_PDB|ADF435X_PDBRF);      self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TX_PUP_5V|TX_PUP_3V|v2_tx_mod|v2_iobits); -    self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK); +    self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF435X_CE|RXBB_PDB|ADF435X_PDBRF|RX_ATTN_MASK);      //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts)      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); @@ -134,7 +136,7 @@ wbx_base::wbx_version2::~wbx_version2(void){   **********************************************************************/  void wbx_base::wbx_version2::set_tx_enabled(bool enb){      self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, -        (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | ADF4350_CE); +        (enb)? TX_POWER_UP | ADF435X_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | ADF435X_CE);  } @@ -166,8 +168,15 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar          "WBX tune: target frequency %f Mhz"      ) % (target_freq/1e6) << std::endl; -    //start with target_freq*2 because mixer has divide by 2 -    target_freq *= 2; +    /* +     * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to +     * tune in Integer-N mode, which can result in better spur +     * performance on some mixers. The default is fractional tuning. +     */ +    property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() +                                                                  : self_base->get_tx_subtree(); +    device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get(); +    bool is_int_n = (tune_args.get("mode_n","") == "int-n");      //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 @@ -184,137 +193,53 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar          (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16)      ; -    double actual_freq, pfd_freq; -    double ref_freq = self_base->get_iface()->get_clock_rate(unit); -    int R=0, BS=0, N=0, FRAC=0, MOD=0; -    int RFdiv = 1; -    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     - -    //Reference doubler for 50% duty cycle -    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 -    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - -    //increase RF divider until acceptable VCO frequency -    const bool do_sync = (target_freq/2 > ref_freq); -    double vco_freq = target_freq; -    while (vco_freq < 2.2e9) { -        vco_freq *= 2; -        RFdiv *= 2; -    } -    if (do_sync) vco_freq = target_freq; - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - -    /* -     * 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_rf = f_vco/RFdiv) -     * f_actual = f_rf/2 -     */ -    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*(1+D)/(R*(1+T)); - -        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) -        if (pfd_freq > 25e6) continue; - -        //ignore fractional part of tuning -        N = int(std::floor(vco_freq/pfd_freq)); - -        //keep N > minimum int divider requirement -        if (N < prescaler_to_min_int_div[prescaler]) continue; - -        for(BS=1; BS <= 255; BS+=1){ -            //keep the band select frequency at or below 100KHz -            //constraint on band select clock -            if (pfd_freq/BS > 100e3) continue; -            goto done_loop; -        } -    } done_loop: - -    //Fractional-N calculation -    MOD = 4095; //max fractional accuracy -    FRAC = int((vco_freq/pfd_freq - N)*MOD); - -    //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 = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; -        R /= 2; -    } +    adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; -    //actual frequency calculation -    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); +    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); -    UHD_LOGV(often) -        << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - -        << boost::format("WBX tune: 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 -        << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" -            ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; +    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      adf4350_regs_t regs; -    regs.frac_12_bit = FRAC; -    regs.int_16_bit = N; -    regs.mod_12_bit = MOD; -    if (do_sync) -    { -        regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); -        regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; -        regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; -    } -    regs.prescaler = prescaler; -    regs.r_counter_10_bit = R; -    regs.reference_divide_by_2 = T; -    regs.reference_doubler = D; -    regs.band_select_clock_div = BS; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); -    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - -    if (unit == dboard_iface::UNIT_RX) { -        freq_range_t rx_lo_5dbm = list_of -            (range_t(0.05e9, 1.4e9)) -        ; - -        freq_range_t rx_lo_2dbm = list_of -            (range_t(1.4e9, 2.2e9)) -        ; - -        if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -        if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; - -    } else if (unit == dboard_iface::UNIT_TX) { -        freq_range_t tx_lo_5dbm = list_of -            (range_t(0.05e9, 1.7e9)) -            (range_t(1.9e9, 2.2e9)) -        ; - -        freq_range_t tx_lo_m1dbm = list_of -            (range_t(1.7e9, 1.9e9)) -        ; - -        if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -        if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM; - -    } +    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_settings.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; diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index 3e8fc8095..e30d6c665 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-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 @@ -17,6 +17,7 @@  #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> @@ -111,17 +112,17 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) {      //set attenuator control bits      int v3_iobits = TX_ATTN_MASK; -    int v3_tx_mod = ADF4350_PDBRF; +    int v3_tx_mod = ADF435X_PDBRF;      //set the gpio directions and atr controls      self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, \              v3_tx_mod|v3_iobits);      self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, \ -            RXBB_PDB|ADF4350_PDBRF); +            RXBB_PDB|ADF435X_PDBRF);      self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, \              TX_PUP_5V|TX_PUP_3V|v3_tx_mod|v3_iobits);      self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, \ -            RX_PUP_5V|RX_PUP_3V|ADF4350_CE|RXBB_PDB|ADF4350_PDBRF|RX_ATTN_MASK); +            RX_PUP_5V|RX_PUP_3V|ADF435X_CE|RXBB_PDB|ADF435X_PDBRF|RX_ATTN_MASK);      //setup ATR for the mixer enables (always enabled to prevent phase      //slip between bursts).  set TX gain iobits to min gain (max attenuation) @@ -163,7 +164,7 @@ wbx_base::wbx_version3::~wbx_version3(void){   **********************************************************************/  void wbx_base::wbx_version3::set_tx_enabled(bool enb){      self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, -        (enb)? TX_POWER_UP | ADF4350_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0); +        (enb)? TX_POWER_UP | ADF435X_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0);  } @@ -198,8 +199,15 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar          "WBX tune: target frequency %f Mhz"      ) % (target_freq/1e6) << std::endl; -    //start with target_freq*2 because mixer has divide by 2 -    target_freq *= 2; +    /* +     * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to +     * tune in Integer-N mode, which can result in better spur +     * performance on some mixers. The default is fractional tuning. +     */ +    property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() +                                                                  : self_base->get_tx_subtree(); +    device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get(); +    bool is_int_n = (tune_args.get("mode_n","") == "int-n");      //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 @@ -216,137 +224,53 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar          (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16)      ; -    double actual_freq, pfd_freq; -    double ref_freq = self_base->get_iface()->get_clock_rate(unit); -    int R=0, BS=0, N=0, FRAC=0, MOD=0; -    int RFdiv = 1; -    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     - -    //Reference doubler for 50% duty cycle -    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 -    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - -    //increase RF divider until acceptable VCO frequency -    const bool do_sync = (target_freq/2 > ref_freq); -    double vco_freq = target_freq; -    while (vco_freq < 2.2e9) { -        vco_freq *= 2; -        RFdiv *= 2; -    } -    if (do_sync) vco_freq = target_freq; - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - -    /* -     * 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_rf = f_vco/RFdiv) -     * f_actual = f_rf/2 -     */ -    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*(1+D)/(R*(1+T)); - -        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) -        if (pfd_freq > 25e6) continue; - -        //ignore fractional part of tuning -        N = int(std::floor(vco_freq/pfd_freq)); - -        //keep N > minimum int divider requirement -        if (N < prescaler_to_min_int_div[prescaler]) continue; - -        for(BS=1; BS <= 255; BS+=1){ -            //keep the band select frequency at or below 100KHz -            //constraint on band select clock -            if (pfd_freq/BS > 100e3) continue; -            goto done_loop; -        } -    } done_loop: - -    //Fractional-N calculation -    MOD = 4095; //max fractional accuracy -    FRAC = int((vco_freq/pfd_freq - N)*MOD); - -    //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 = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; -        R /= 2; -    } - -    //actual frequency calculation -    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); - -    UHD_LOGV(often) -        << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - -        << boost::format("WBX tune: 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 -        << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" -            ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; +    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); +    tuning_constraints.pfd_freq_max = 25e6; +    tuning_constraints.rf_divider_range = uhd::range_t(1, 16); + +    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      adf4350_regs_t regs; -    regs.frac_12_bit = FRAC; -    regs.int_16_bit = N; -    regs.mod_12_bit = MOD; -    if (do_sync) -    { -        regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); -        regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; -        regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; -    } -    regs.prescaler = prescaler; -    regs.r_counter_10_bit = R; -    regs.reference_divide_by_2 = T; -    regs.reference_doubler = D; -    regs.band_select_clock_div = BS; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); -    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - -    if (unit == dboard_iface::UNIT_RX) { -        freq_range_t rx_lo_5dbm = list_of -            (range_t(0.05e9, 1.4e9)) -        ; - -        freq_range_t rx_lo_2dbm = list_of -            (range_t(1.4e9, 2.2e9)) -        ; - -        if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -        if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; - -    } else if (unit == dboard_iface::UNIT_TX) { -        freq_range_t tx_lo_5dbm = list_of -            (range_t(0.05e9, 1.7e9)) -            (range_t(1.9e9, 2.2e9)) -        ; - -        freq_range_t tx_lo_m1dbm = list_of -            (range_t(1.7e9, 1.9e9)) -        ; - -        if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -        if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4350_regs_t::OUTPUT_POWER_M1DBM; - -    } +    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_settings.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; diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index 1feea2c0b..dc1ae4df8 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2012 Ettus Research LLC +// Copyright 2011-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 @@ -17,6 +17,7 @@  #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> @@ -35,7 +36,7 @@ using namespace boost::assign;  /*********************************************************************** - * WBX Version 3 Constants + * WBX Version 4 Constants   **********************************************************************/  static const uhd::dict<std::string, gain_range_t> wbx_v4_tx_gain_ranges = map_list_of      ("PGA0", gain_range_t(0, 31, 1.0)) @@ -85,7 +86,10 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      ////////////////////////////////////////////////////////////////////      // Register RX properties      //////////////////////////////////////////////////////////////////// -    this->get_rx_subtree()->create<std::string>("name").set("WBXv4 RX"); +    boost::uint16_t rx_id = _self_wbx_base->get_rx_id().to_uint16(); + +    if(rx_id == 0x0063) this->get_rx_subtree()->create<std::string>("name").set("WBXv4 RX"); +    else if(rx_id == 0x0081) this->get_rx_subtree()->create<std::string>("name").set("WBX-120 RX");      this->get_rx_subtree()->create<double>("freq/value")           .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1))           .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0); @@ -94,7 +98,10 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      ////////////////////////////////////////////////////////////////////      // Register TX properties      //////////////////////////////////////////////////////////////////// -    this->get_tx_subtree()->create<std::string>("name").set("WBXv4 TX"); + +    //get_tx_id() will always return GDB ID, so use RX ID to determine WBXv4 vs. WBX-120 +    if(rx_id == 0x0063) this->get_tx_subtree()->create<std::string>("name").set("WBXv4 TX"); +    else if(rx_id == 0x0081) this->get_tx_subtree()->create<std::string>("name").set("WBX-120 TX");      BOOST_FOREACH(const std::string &name, wbx_v4_tx_gain_ranges.keys()){          self_base->get_tx_subtree()->create<double>("gains/"+name+"/value")              .coerce(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name)) @@ -112,17 +119,17 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      //set attenuator control bits      int v4_iobits = TX_ATTN_MASK; -    int v4_tx_mod = ADF4351_PDBRF; +    int v4_tx_mod = ADF435X_PDBRF;      //set the gpio directions and atr controls      self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, \              v4_tx_mod|v4_iobits);      self_base->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, \ -            RXBB_PDB|ADF4351_PDBRF); +            RXBB_PDB|ADF435X_PDBRF);      self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, \              TX_PUP_5V|TX_PUP_3V|v4_tx_mod|v4_iobits);      self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, \ -            RX_PUP_5V|RX_PUP_3V|ADF4351_CE|RXBB_PDB|ADF4351_PDBRF|RX_ATTN_MASK); +            RX_PUP_5V|RX_PUP_3V|ADF435X_CE|RXBB_PDB|ADF435X_PDBRF|RX_ATTN_MASK);      //setup ATR for the mixer enables (always enabled to prevent phase slip      //between bursts) set TX gain iobits to min gain (max attenuation) when @@ -164,7 +171,7 @@ wbx_base::wbx_version4::~wbx_version4(void){   **********************************************************************/  void wbx_base::wbx_version4::set_tx_enabled(bool enb) {      self_base->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, -        (enb)? TX_POWER_UP | ADF4351_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0); +        (enb)? TX_POWER_UP | ADF435X_CE : TX_POWER_DOWN, TX_POWER_UP | TX_POWER_DOWN | 0);  } @@ -200,8 +207,15 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar          "WBX tune: target frequency %f Mhz"      ) % (target_freq/1e6) << std::endl; -    //start with target_freq*2 because mixer has divide by 2 -    target_freq *= 2; +    /* +     * If the user sets 'mode_n=int-n' in the tuning args, the user wishes to +     * tune in Integer-N mode, which can result in better spur +     * performance on some mixers. The default is fractional tuning. +     */ +    property_tree::sptr subtree = (unit == dboard_iface::UNIT_RX) ? self_base->get_rx_subtree() +                                                                  : self_base->get_tx_subtree(); +    device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get(); +    bool is_int_n = (tune_args.get("mode_n","") == "int-n");      //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 @@ -220,137 +234,53 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar          (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64)      ; -    double actual_freq, pfd_freq; -    double ref_freq = self_base->get_iface()->get_clock_rate(unit); -    int R=0, BS=0, N=0, FRAC=0, MOD=0; -    int RFdiv = 1; -    adf4351_regs_t::reference_divide_by_2_t T     = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    adf4351_regs_t::reference_doubler_t     D     = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED;     - -    //Reference doubler for 50% duty cycle -    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 -    if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; - -    //increase RF divider until acceptable VCO frequency -    const bool do_sync = (target_freq/2 > ref_freq); -    double vco_freq = target_freq; -    while (vco_freq < 2.2e9) { -        vco_freq *= 2; -        RFdiv *= 2; -    } -    if (do_sync) vco_freq = target_freq; - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; - -    /* -     * 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_rf = f_vco/RFdiv) -     * f_actual = f_rf/2 -     */ -    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*(1+D)/(R*(1+T)); - -        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) -        if (pfd_freq > 25e6) continue; - -        //ignore fractional part of tuning -        N = int(std::floor(vco_freq/pfd_freq)); - -        //keep N > minimum int divider requirement -        if (N < prescaler_to_min_int_div[prescaler]) continue; - -        for(BS=1; BS <= 255; BS+=1){ -            //keep the band select frequency at or below 100KHz -            //constraint on band select clock -            if (pfd_freq/BS > 100e3) continue; -            goto done_loop; -        } -    } done_loop: - -    //Fractional-N calculation -    MOD = 4095; //max fractional accuracy -    FRAC = int((vco_freq/pfd_freq - N)*MOD); - -    //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 = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; -        R /= 2; -    } - -    //actual frequency calculation -    actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); - -    UHD_LOGV(often) -        << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - -        << boost::format("WBX tune: 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 -        << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" -            ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; +    adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? 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); +    tuning_constraints.pfd_freq_max = 25e6; +    tuning_constraints.rf_divider_range = uhd::range_t(1, 64); + +    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; -    regs.frac_12_bit = FRAC; -    regs.int_16_bit = N; -    regs.mod_12_bit = MOD; -    if (do_sync) -    { -        regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); -        regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; -        regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; -    } -    regs.prescaler = prescaler; -    regs.r_counter_10_bit = R; -    regs.reference_divide_by_2 = T; -    regs.reference_doubler = D; -    regs.band_select_clock_div = BS; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); -    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - -    if (unit == dboard_iface::UNIT_RX) { -        freq_range_t rx_lo_5dbm = list_of -            (range_t(0.05e9, 1.4e9)) -        ; - -        freq_range_t rx_lo_2dbm = list_of -            (range_t(1.4e9, 2.2e9)) -        ; - -        if (actual_freq == rx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; - -        if (actual_freq == rx_lo_2dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM; - -    } else if (unit == dboard_iface::UNIT_TX) { -        freq_range_t tx_lo_5dbm = list_of -            (range_t(0.05e9, 1.7e9)) -            (range_t(1.9e9, 2.2e9)) -        ; - -        freq_range_t tx_lo_m1dbm = list_of -            (range_t(1.7e9, 1.9e9)) -        ; - -        if (actual_freq == tx_lo_5dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; - -        if (actual_freq == tx_lo_m1dbm.clip(actual_freq)) regs.output_power = adf4351_regs_t::OUTPUT_POWER_M1DBM; - -    } +    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_settings.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; @@ -361,10 +291,12 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      //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( -            "WBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; +            "%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 @@ -373,7 +305,7 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      //return the actual frequency      UHD_LOGV(often) << boost::format( -        "WBX tune: actual frequency %f Mhz" -    ) % (actual_freq/1e6) << std::endl; +        "%s tune: actual frequency %f Mhz" +    ) % board_name.c_str() % (actual_freq/1e6) << std::endl;      return actual_freq;  } | 
