diff options
Diffstat (limited to 'host/lib/usrp')
| -rw-r--r-- | host/lib/usrp/b200/b200_io_impl.cpp | 14 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_ctrl.cpp | 8 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_driver/ad9361_device.cpp | 3 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_driver/ad9361_device.h | 2 | ||||
| -rw-r--r-- | host/lib/usrp/common/max287x.hpp | 800 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_cbx.cpp | 194 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.hpp | 7 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_ubx.cpp | 811 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_version4.cpp | 27 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_io_impl.cpp | 10 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/codec_ctrl.cpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/fw_common.h | 2 | ||||
| -rw-r--r-- | host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_clock_ctrl.cpp | 767 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_clock_ctrl.hpp | 13 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_dboard_iface.cpp | 28 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.hpp | 4 | 
17 files changed, 1585 insertions, 1109 deletions
| diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 60b925517..d6df726af 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -118,8 +118,7 @@ void b200_impl::set_auto_tick_rate(      // Step 2: Determine whether if we can use lcm_rate (preferred),      // or have to give up because too large: -    const double max_tick_rate = -        ((num_chans <= 1) ? ad9361_device_t::AD9361_RECOMMENDED_MAX_CLOCK_RATE : ad9361_device_t::AD9361_MAX_CLOCK_RATE/2); +    const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE/num_chans;      double base_rate = static_cast<double>(lcm_rate);      if (base_rate > max_tick_rate) {          UHD_MSG(warning) @@ -176,7 +175,6 @@ void b200_impl::update_tick_rate(const double new_tick_rate)      }  } -  double b200_impl::coerce_rx_samp_rate(rx_dsp_core_3000::sptr ddc, size_t dspno, const double rx_rate)  {      // Have to set tick rate first, or the ddc will change the requested rate based on default tick rate @@ -187,6 +185,14 @@ double b200_impl::coerce_rx_samp_rate(rx_dsp_core_3000::sptr ddc, size_t dspno,      return ddc->set_host_rate(rx_rate);  } +#define CHECK_BANDWIDTH(dir) \ +    if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \ +        UHD_MSG(warning) \ +            << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \ +            << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \ +            << std::endl; \ +    } +  void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)  {      boost::shared_ptr<sph::recv_packet_streamer> my_streamer = @@ -195,6 +201,7 @@ void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)      my_streamer->set_samp_rate(rate);      const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment();      my_streamer->set_scale_factor(adj); +    CHECK_BANDWIDTH("Rx");  }  double b200_impl::coerce_tx_samp_rate(tx_dsp_core_3000::sptr duc, size_t dspno, const double tx_rate) @@ -215,6 +222,7 @@ void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate)      my_streamer->set_samp_rate(rate);      const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment();      my_streamer->set_scale_factor(adj); +    CHECK_BANDWIDTH("Tx");  }  /*********************************************************************** diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp index 9c17a582d..f3ab36247 100644 --- a/host/lib/usrp/common/ad9361_ctrl.cpp +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2012-2014 Ettus Research LLC +// Copyright 2012-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 @@ -133,12 +133,6 @@ public:      {          boost::lock_guard<boost::mutex> lock(_mutex); -        //warning for known trouble rates -        if (rate > ad9361_device_t::AD9361_RECOMMENDED_MAX_CLOCK_RATE) UHD_MSG(warning) << boost::format( -            "The requested clock rate %f MHz may cause slow configuration.\n" -            "The driver recommends a master clock rate less than %f MHz.\n" -        ) % (rate/1e6) % (ad9361_device_t::AD9361_RECOMMENDED_MAX_CLOCK_RATE/1e6) << std::endl; -          //clip to known bounds          const meta_range_t clock_rate_range = ad9361_ctrl::get_clock_rate_range();          const double clipped_rate = clock_rate_range.clip(rate); diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index c3eb5fb9d..8737837b3 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -79,8 +79,9 @@ int get_num_taps(int max_num_taps) {  const double ad9361_device_t::AD9361_MAX_GAIN        = 89.75;  const double ad9361_device_t::AD9361_MAX_CLOCK_RATE  = 61.44e6; -const double ad9361_device_t::AD9361_RECOMMENDED_MAX_CLOCK_RATE = 56e6;  const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6; +// Max bandwdith is due to filter rolloff in analog filter stage +const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6;  /* Program either the RX or TX FIR filter.   * diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index a242f35e9..0c7a7e4f7 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -132,8 +132,8 @@ public:      //Constants      static const double AD9361_MAX_GAIN;      static const double AD9361_MAX_CLOCK_RATE; -    static const double AD9361_RECOMMENDED_MAX_CLOCK_RATE;      static const double AD9361_CAL_VALID_WINDOW; +    static const double AD9361_RECOMMENDED_MAX_BANDWIDTH;  private:    //Methods      void _program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs); diff --git a/host/lib/usrp/common/max287x.hpp b/host/lib/usrp/common/max287x.hpp new file mode 100644 index 000000000..d084dcfbe --- /dev/null +++ b/host/lib/usrp/common/max287x.hpp @@ -0,0 +1,800 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef MAX287X_HPP_INCLUDED +#define MAX287X_HPP_INCLUDED + +#include <uhd/exception.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/log.hpp> +#include <boost/assign.hpp> +#include <boost/function.hpp> +#include <boost/thread.hpp> +#include <boost/math/special_functions/round.hpp> +#include <vector> +#include "max2870_regs.hpp" +#include "max2871_regs.hpp" + +/** + * MAX287x interface + */ +class max287x_iface +{ +public: +    typedef boost::shared_ptr<max287x_iface> sptr; + +    typedef boost::function<void(std::vector<boost::uint32_t>)> write_fn; + +    /** +     * LD Pin Modes +     */ +    typedef enum{ +        LD_PIN_MODE_LOW, +        LD_PIN_MODE_DLD, +        LD_PIN_MODE_ALD, +        LD_PIN_MODE_HIGH +    } ld_pin_mode_t; + +    /** +     * MUXOUT Modes +     */ +    typedef enum{ +        MUXOUT_TRI_STATE, +        MUXOUT_HIGH, +        MUXOUT_LOW, +        MUXOUT_RDIV, +        MUXOUT_NDIV, +        MUXOUT_ALD, +        MUXOUT_DLD, +        MUXOUT_SYNC, +        MUXOUT_SPI +    } muxout_mode_t; + +    /** +     * Charge Pump Currents +     */ +    typedef enum{ +        CHARGE_PUMP_CURRENT_0_32MA, +        CHARGE_PUMP_CURRENT_0_64MA, +        CHARGE_PUMP_CURRENT_0_96MA, +        CHARGE_PUMP_CURRENT_1_28MA, +        CHARGE_PUMP_CURRENT_1_60MA, +        CHARGE_PUMP_CURRENT_1_92MA, +        CHARGE_PUMP_CURRENT_2_24MA, +        CHARGE_PUMP_CURRENT_2_56MA, +        CHARGE_PUMP_CURRENT_2_88MA, +        CHARGE_PUMP_CURRENT_3_20MA, +        CHARGE_PUMP_CURRENT_3_52MA, +        CHARGE_PUMP_CURRENT_3_84MA, +        CHARGE_PUMP_CURRENT_4_16MA, +        CHARGE_PUMP_CURRENT_4_48MA, +        CHARGE_PUMP_CURRENT_4_80MA, +        CHARGE_PUMP_CURRENT_5_12MA +    } charge_pump_current_t; + +    /** +     * Output Powers +     */ +    typedef enum{ +        OUTPUT_POWER_M4DBM, +        OUTPUT_POWER_M1DBM, +        OUTPUT_POWER_2DBM, +        OUTPUT_POWER_5DBM +    } output_power_t; + +    typedef enum { +        LOW_NOISE_AND_SPUR_LOW_NOISE, +        LOW_NOISE_AND_SPUR_LOW_SPUR_1, +        LOW_NOISE_AND_SPUR_LOW_SPUR_2 +    } low_noise_and_spur_t; + +    typedef enum { +        CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF, +        CLOCK_DIV_MODE_FAST_LOCK, +        CLOCK_DIV_MODE_PHASE +    } clock_divider_mode_t; + +    /** +     * Make a synthesizer +     * @param write write function +     * @return shared pointer to object +     */ +    template <typename max287X_t> static sptr make(write_fn write) +    { +        return sptr(new max287X_t(write)); +    } + +    /** +     * Destructor +     */ +    virtual ~max287x_iface() {}; + +    /** +     * Power up the synthesizer +     */ +    virtual void power_up(void) = 0; + +    /** +     * Shut down the synthesizer +     */ +    virtual void shutdown(void) = 0; + +    /** +     * Check if the synthesizer is shut down +     */ +    virtual bool is_shutdown(void) = 0; + +    /** +     * Set frequency +     * @param target_freq target frequency +     * @param ref_freq reference frequency +     * @param target_pfd_freq target phase detector frequency +     * @param is_int_n enable integer-N tuning +     * @return actual frequency +     */ +    virtual double set_frequency( +                double target_freq, +                double ref_freq, +                double target_pfd_freq, +                bool is_int_n) = 0; + +    /** +     * Set output power +     * @param power output power +     */ +    virtual void set_output_power(output_power_t power) = 0; + +    /** +     * Set lock detect pin mode +     * @param mode lock detect pin mode +     */ +    virtual void set_ld_pin_mode(ld_pin_mode_t mode) = 0; + +    /** +     * Set muxout pin mode +     * @param mode muxout mode +     */ +    virtual void set_muxout_mode(muxout_mode_t mode) = 0; + +    /** +     * Set charge pump current +     * @param cp_current charge pump current +     */ +    virtual void set_charge_pump_current(charge_pump_current_t cp_current) = 0; + +    /** +     * Enable or disable auto retune +     * @param enabled enable auto retune +     */ +    virtual void set_auto_retune(bool enabled) = 0; + +    /** +     * Set clock divider mode +     * @param mode clock divider mode +     */ +    virtual void set_clock_divider_mode(clock_divider_mode_t mode) = 0; + +    /** +     * Enable or disable cycle slip mode +     * @param enabled enable cycle slip mode +     */ +    virtual void set_cycle_slip_mode(bool enabled) = 0; + +    /** +     * Set low noise and spur mode +     * @param mode low noise and spur mode +     */ +    virtual void set_low_noise_and_spur(low_noise_and_spur_t mode) = 0; + +    /** +     * Set phase +     * @param phase the phase offset +     */ +    virtual void set_phase(boost::uint16_t phase) = 0; + +    /** +     * Write values configured by the set_* functions. +     */ +    virtual void commit(void) = 0; + +    /** +     * Check whether this is in a state where it can be synchronized +     */ +    virtual bool can_sync(void) = 0; +}; + +/** + * MAX287x + * Base class for all MAX287x synthesizers + */ +template <typename max287x_regs_t> +class max287x : public max287x_iface +{ +public: +    max287x(write_fn func); +    virtual ~max287x(); +    virtual void power_up(void); +    virtual void shutdown(void); +    virtual bool is_shutdown(void); +    virtual double set_frequency( +        double target_freq, +        double ref_freq, +        double target_pfd_freq, +        bool is_int_n); +    virtual void set_output_power(output_power_t power); +    virtual void set_ld_pin_mode(ld_pin_mode_t mode); +    virtual void set_muxout_mode(muxout_mode_t mode); +    virtual void set_charge_pump_current(charge_pump_current_t cp_current); +    virtual void set_auto_retune(bool enabled); +    virtual void set_clock_divider_mode(clock_divider_mode_t mode); +    virtual void set_cycle_slip_mode(bool enabled); +    virtual void set_low_noise_and_spur(low_noise_and_spur_t mode); +    virtual void set_phase(boost::uint16_t phase); +    virtual void commit(); +    virtual bool can_sync(); + +protected: +    max287x_regs_t _regs; +    bool _can_sync; +    bool _write_all_regs; + +private: +    write_fn _write; +    bool _delay_after_write; +}; + +/** + * MAX2870 + */ +class max2870 : public max287x<max2870_regs_t> +{ +public: +    max2870(write_fn func) : max287x<max2870_regs_t>(func) {} +    ~max2870() {} +    double set_frequency( +        double target_freq, +        double ref_freq, +        double target_pfd_freq, +        bool is_int_n) +    { +        _regs.cpoc = is_int_n ? max2870_regs_t::CPOC_ENABLED : max2870_regs_t::CPOC_DISABLED; +        _regs.feedback_select = target_freq >= 3.0e9 ? +            max2870_regs_t::FEEDBACK_SELECT_DIVIDED : +            max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; + +        return max287x<max2870_regs_t>::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); +    } +    void commit(void) +    { +        // For MAX2870, we always need to write all registers. +        _write_all_regs = true; +        max287x<max2870_regs_t>::commit(); +    } +}; + +/** + * MAX2871 + */ +class max2871 : public max287x<max2871_regs_t> +{ +public: +    max2871(write_fn func) : max287x<max2871_regs_t>(func) {} +    ~max2871() {}; +    void set_muxout_mode(muxout_mode_t mode) +    { +        switch(mode) +        { +        case MUXOUT_SYNC: +            _regs.muxout = max2871_regs_t::MUXOUT_SYNC; +            break; +        case MUXOUT_SPI: +            _regs.muxout = max2871_regs_t::MUXOUT_SPI; +            break; +        default: +            max287x<max2871_regs_t>::set_muxout_mode(mode); +        } +    } + +    double set_frequency( +        double target_freq, +        double ref_freq, +        double target_pfd_freq, +        bool is_int_n) +    { +        _regs.feedback_select = max2871_regs_t::FEEDBACK_SELECT_DIVIDED; +        double freq = max287x<max2871_regs_t>::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); + +        // According to Maxim support, the following factors must be true to allow for synchronization +        if (_regs.r_counter_10_bit == 1 and +            _regs.reference_divide_by_2 == max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED and +            _regs.reference_doubler == max2871_regs_t::REFERENCE_DOUBLER_DISABLED and +            _regs.rf_divider_select <= max2871_regs_t::RF_DIVIDER_SELECT_DIV16 and +            _regs.low_noise_and_spur == max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE) +        { +            _can_sync = true; +        } +        return freq; +    } +}; + + +// Implementation of max287x template class +// To avoid linker errors, it was either include +// it here or put it in a .cpp file and include +// that file in this header file.  Decided to just +// include it here. + +template <typename max287x_regs_t> +max287x<max287x_regs_t>::max287x(write_fn func) : +        _can_sync(false), +        _write_all_regs(true), +        _write(func), +        _delay_after_write(true) +{ +    power_up(); +} + +template <typename max287x_regs_t> +max287x<max287x_regs_t>::~max287x() +{ +    shutdown(); +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::power_up(void) +{ +    _regs.power_down = max287x_regs_t::POWER_DOWN_NORMAL; +    _regs.double_buffer = max287x_regs_t::DOUBLE_BUFFER_ENABLED; + +    // According to MAX287x data sheets: +    // "Upon power-up, the registers should be programmed twice with at +    // least a 20ms pause between writes.  The first write ensures that +    // the device is enabled, and the second write starts the VCO +    // selection process." +    // The first write and the 20ms wait are done here.  The second write +    // is done when any other function that does a write to the registers +    // is called (such as tuning). +    _write_all_regs = true; +    _delay_after_write = true; +    commit(); +    _write_all_regs = true; // Next call to commit() writes all regs +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::shutdown(void) +{ +    _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_DISABLED; +    _regs.aux_output_enable = max287x_regs_t::AUX_OUTPUT_ENABLE_DISABLED; +    _regs.power_down = max287x_regs_t::POWER_DOWN_SHUTDOWN; +    commit(); +} + +template <typename max287x_regs_t> +bool max287x<max287x_regs_t>::is_shutdown(void) +{ +    return (_regs.power_down == max287x_regs_t::POWER_DOWN_SHUTDOWN); +} + +template <typename max287x_regs_t> +double max287x<max287x_regs_t>::set_frequency( +    double target_freq, +    double ref_freq, +    double target_pfd_freq, +    bool is_int_n) +{ +    _can_sync = false; + +    //map rf divider select output dividers to enums +    static const uhd::dict<int, typename max287x_regs_t::rf_divider_select_t> rfdivsel_to_enum = +        boost::assign::map_list_of +        (1,   max287x_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,   max287x_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,   max287x_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,   max287x_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16,  max287x_regs_t::RF_DIVIDER_SELECT_DIV16) +        (32,  max287x_regs_t::RF_DIVIDER_SELECT_DIV32) +        (64,  max287x_regs_t::RF_DIVIDER_SELECT_DIV64) +        (128, max287x_regs_t::RF_DIVIDER_SELECT_DIV128); + +    //map mode setting to valid integer divider (N) values +    static const uhd::range_t int_n_mode_div_range(16,65536,1); +    static const uhd::range_t frac_n_mode_div_range(19,4091,1); + +    //other ranges and constants from MAX287X datasheets +    static const uhd::range_t clock_div_range(1,4095,1); +    static const uhd::range_t r_range(1,1023,1); +    static const double MIN_VCO_FREQ = 3e9; +    static const double BS_FREQ = 50e3; +    static const int MAX_BS_VALUE = 1023; + +    int T = 0; +    int D = ref_freq <= 10.0e6 ? 1 : 0; +    int R = 0; +    int BS = 0; +    int N = 0; +    int FRAC = 0; +    int MOD = 4095; +    int RFdiv = 1; +    double pfd_freq = target_pfd_freq; +    bool feedback_divided = (_regs.feedback_select == max287x_regs_t::FEEDBACK_SELECT_DIVIDED); + +    //increase RF divider until acceptable VCO frequency (MIN freq for MAX287x VCO is 3GHz) +    double vco_freq = target_freq; +    while (vco_freq < MIN_VCO_FREQ) +    { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    // The feedback frequency can be the fundamental VCO frequency or +    // divided frequency.  The output divider for MAX287x is actually +    // 2 dividers, but only the first (1/2/4/8/16) is included in the +    // feedback loop. +    int fb_divisor = feedback_divided ? (RFdiv > 16 ? 16 : RFdiv) : 1; + +    /* +     * The goal here is to loop though possible R dividers, +     * band select clock dividers, N (int) dividers, and FRAC +     * (frac) dividers. +     * +     * Calculate the N and F dividers for each set of values. +     * The loop exits when it meets all of the constraints. +     * The resulting loop values are loaded into the registers. +     * +     * f_pfd = f_ref*(1+D)/(R*(1+T)) +     * f_vco = (N + (FRAC/MOD))*f_pfd +     *     N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +     * f_rf  = f_vco/RFdiv +     */ +    for(R = int(ref_freq*(1+D)/(target_pfd_freq*(1+T))); R <= r_range.stop(); R++) +    { +        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +        pfd_freq = ref_freq*(1+D)/(R*(1+T)); + +        //keep the PFD frequency at or below target +        if (pfd_freq > target_pfd_freq) +            continue; + +        //ignore fractional part of tuning +        N = int((vco_freq/pfd_freq)/fb_divisor); + +        //Fractional-N calculation +        FRAC = int(boost::math::round(((vco_freq/pfd_freq)/fb_divisor - N)*MOD)); + +        if(is_int_n) +        { +            if (FRAC > (MOD / 2)) //Round integer such that actual freq is closest to target +                N++; +            FRAC = 0; +        } + +        //keep N within int divider requirements +        if(is_int_n) +        { +            if(N < int_n_mode_div_range.start()) continue; +            if(N > int_n_mode_div_range.stop()) continue; +        } +        else +        { +            if(N < frac_n_mode_div_range.start()) continue; +            if(N > frac_n_mode_div_range.stop()) continue; +        } + +        //keep pfd freq low enough to achieve 50kHz BS clock +        BS = std::ceil(pfd_freq / BS_FREQ); +        if(BS <= MAX_BS_VALUE) break; +    } +    UHD_ASSERT_THROW(R <= r_range.stop()); + +    //Reference divide-by-2 for 50% duty cycle +    // if R even, move one divide by 2 to to regs.reference_divide_by_2 +    if(R % 2 == 0) +    { +        T = 1; +        R /= 2; +    } + +    //actual frequency calculation +    double actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))) * fb_divisor / RFdiv; + +    UHD_LOGV(rarely) +        << boost::format("MAX287x: Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f" +            ) % ref_freq % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl +        << boost::format("MAX287x: tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") << std::endl +        << boost::format("MAX287x: Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" +            ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + +    //load the register values +    _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_ENABLED; + +    if(is_int_n) { +        _regs.cpl = max287x_regs_t::CPL_DISABLED; +        _regs.ldf = max287x_regs_t::LDF_INT_N; +        _regs.int_n_mode = max287x_regs_t::INT_N_MODE_INT_N; +    } else { +        _regs.cpl = max287x_regs_t::CPL_ENABLED; +        _regs.ldf = max287x_regs_t::LDF_FRAC_N; +        _regs.int_n_mode = max287x_regs_t::INT_N_MODE_FRAC_N; +    } + +    _regs.lds = pfd_freq <= 32e6 ? max287x_regs_t::LDS_SLOW : max287x_regs_t::LDS_FAST; + +    _regs.frac_12_bit = FRAC; +    _regs.int_16_bit = N; +    _regs.mod_12_bit = MOD; +    _regs.clock_divider_12_bit = std::max(int(clock_div_range.start()), int(std::ceil(400e-6*pfd_freq/MOD))); +    UHD_ASSERT_THROW(_regs.clock_divider_12_bit <= clock_div_range.stop()); +    _regs.r_counter_10_bit = R; +    _regs.reference_divide_by_2 = T ? +        max287x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : +        max287x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    _regs.reference_doubler = D ? +        max287x_regs_t::REFERENCE_DOUBLER_ENABLED : +        max287x_regs_t::REFERENCE_DOUBLER_DISABLED; +    _regs.band_select_clock_div = BS & 0xFF; +    _regs.bs_msb = (BS & 0x300) >> 8; +    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); +    _regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + +    if (_regs.clock_div_mode == max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK) +    { +        // Charge pump current needs to be set to lowest value in fast lock mode +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA; +        // Make sure the register containing the charge pump current is written +        _write_all_regs = true; +    } + +    return actual_freq; +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_output_power(output_power_t power) +{ +    switch (power) +    { +    case OUTPUT_POWER_M4DBM: +        _regs.output_power = max287x_regs_t::OUTPUT_POWER_M4DBM; +        break; +    case OUTPUT_POWER_M1DBM: +        _regs.output_power = max287x_regs_t::OUTPUT_POWER_M1DBM; +        break; +    case OUTPUT_POWER_2DBM: +        _regs.output_power = max287x_regs_t::OUTPUT_POWER_2DBM; +        break; +    case OUTPUT_POWER_5DBM: +        _regs.output_power = max287x_regs_t::OUTPUT_POWER_5DBM; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_ld_pin_mode(ld_pin_mode_t mode) +{ +    switch(mode) +    { +    case LD_PIN_MODE_LOW: +        _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_LOW; +        break; +    case LD_PIN_MODE_DLD: +        _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_DLD; +        break; +    case LD_PIN_MODE_ALD: +        _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_ALD; +        break; +    case LD_PIN_MODE_HIGH: +        _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_HIGH; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_muxout_mode(muxout_mode_t mode) +{ +    switch(mode) +    { +    case MUXOUT_TRI_STATE: +        _regs.muxout = max287x_regs_t::MUXOUT_TRI_STATE; +        break; +    case MUXOUT_HIGH: +        _regs.muxout = max287x_regs_t::MUXOUT_HIGH; +        break; +    case MUXOUT_LOW: +        _regs.muxout = max287x_regs_t::MUXOUT_LOW; +        break; +    case MUXOUT_RDIV: +        _regs.muxout = max287x_regs_t::MUXOUT_RDIV; +        break; +    case MUXOUT_NDIV: +        _regs.muxout = max287x_regs_t::MUXOUT_NDIV; +        break; +    case MUXOUT_ALD: +        _regs.muxout = max287x_regs_t::MUXOUT_ALD; +        break; +    case MUXOUT_DLD: +        _regs.muxout = max287x_regs_t::MUXOUT_DLD; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_charge_pump_current(charge_pump_current_t cp_current) +{ +    switch(cp_current) +    { +    case CHARGE_PUMP_CURRENT_0_32MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA; +        break; +    case CHARGE_PUMP_CURRENT_0_64MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_64MA; +        break; +    case CHARGE_PUMP_CURRENT_0_96MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_96MA; +        break; +    case CHARGE_PUMP_CURRENT_1_28MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_28MA; +        break; +    case CHARGE_PUMP_CURRENT_1_60MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_60MA; +        break; +    case CHARGE_PUMP_CURRENT_1_92MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_92MA; +        break; +    case CHARGE_PUMP_CURRENT_2_24MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_24MA; +        break; +    case CHARGE_PUMP_CURRENT_2_56MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_56MA; +        break; +    case CHARGE_PUMP_CURRENT_2_88MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_88MA; +        break; +    case CHARGE_PUMP_CURRENT_3_20MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_20MA; +        break; +    case CHARGE_PUMP_CURRENT_3_52MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_52MA; +        break; +    case CHARGE_PUMP_CURRENT_3_84MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_84MA; +        break; +    case CHARGE_PUMP_CURRENT_4_16MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_16MA; +        break; +    case CHARGE_PUMP_CURRENT_4_48MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_48MA; +        break; +    case CHARGE_PUMP_CURRENT_4_80MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_80MA; +        break; +    case CHARGE_PUMP_CURRENT_5_12MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_5_12MA; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_auto_retune(bool enabled) +{ +    _regs.retune = enabled ? max287x_regs_t::RETUNE_ENABLED : max287x_regs_t::RETUNE_DISABLED; +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_clock_divider_mode(clock_divider_mode_t mode) +{ +    switch(mode) +    { +    case CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF: +        _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF; +        break; +    case CLOCK_DIV_MODE_FAST_LOCK: +        _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK; +        break; +    case CLOCK_DIV_MODE_PHASE: +        _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_PHASE; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_cycle_slip_mode(bool enabled) +{ +    if (enabled) +        throw uhd::runtime_error("Cycle slip mode not supported on this MAX287x synthesizer."); +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_low_noise_and_spur(low_noise_and_spur_t mode) +{ +    switch(mode) +    { +    case LOW_NOISE_AND_SPUR_LOW_NOISE: +        _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE; +        break; +    case LOW_NOISE_AND_SPUR_LOW_SPUR_1: +        _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_1; +        break; +    case LOW_NOISE_AND_SPUR_LOW_SPUR_2: +        _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_phase(boost::uint16_t phase) +{ +    _regs.phase_12_bit = phase & 0xFFF; +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::commit() +{ +    std::vector<boost::uint32_t> regs; +    std::set<boost::uint32_t> changed_regs; + +    // Get only regs with changes +    if (_write_all_regs) +    { +        for (int addr = 5; addr >= 0; addr--) +            regs.push_back(_regs.get_reg(boost::uint32_t(addr))); +    } else { +        try { +            changed_regs = _regs.template get_changed_addrs<boost::uint32_t> (); +            for (int addr = 5; addr >= 0; addr--) +            { +                if (changed_regs.find(boost::uint32_t(addr)) != changed_regs.end()) +                    regs.push_back(_regs.get_reg(boost::uint32_t(addr))); +            } +        } catch (uhd::runtime_error& e) { +            // No saved state - write all regs +            for (int addr = 5; addr >= 0; addr--) +                regs.push_back(_regs.get_reg(boost::uint32_t(addr))); +        } +    } + +    _write(regs); +    _regs.save_state(); +    _write_all_regs = false; + +    if (_delay_after_write) +    { +        boost::this_thread::sleep(boost::posix_time::milliseconds(20)); +        _delay_after_write = false; +    } +} + + +template <typename max287x_regs_t> +bool max287x<max287x_regs_t>::can_sync(void) +{ +    return _can_sync; +} + +#endif // MAX287X_HPP_INCLUDED diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp index ad255460e..8336117b8 100644 --- a/host/lib/usrp/dboard/db_cbx.cpp +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -15,8 +15,6 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // - -#include "max2870_regs.hpp"  #include "db_sbx_common.hpp"  #include <boost/algorithm/string.hpp>  #include <boost/math/special_functions/round.hpp> @@ -31,6 +29,8 @@ using namespace boost::assign;  sbx_xcvr::cbx::cbx(sbx_xcvr *_self_sbx_xcvr) {      //register the handle to our base CBX class      self_base = _self_sbx_xcvr; +    _txlo = max287x_iface::make<max2870>(boost::bind(&sbx_xcvr::cbx::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = max287x_iface::make<max2870>(boost::bind(&sbx_xcvr::cbx::write_lo_regs, this, dboard_iface::UNIT_RX, _1));  } @@ -38,6 +38,14 @@ sbx_xcvr::cbx::~cbx(void){      /* NOP */  } +void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s) +{ +    BOOST_FOREACH(boost::uint32_t reg, regs) +    { +        self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32); +    } +} +  /***********************************************************************   * Tuning @@ -47,6 +55,13 @@ 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; +    //clip the input +    target_freq = cbx_freq_range.clip(target_freq); + +    double ref_freq = self_base->get_iface()->get_clock_rate(unit); +    double target_pfd_freq = 25e6; +    double actual_freq = 0.0; +      /*       * If the user sets 'mode_n=integer' in the tuning args, the user wishes to       * tune in Integer-N mode, which can result in better spur @@ -57,174 +72,17 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); -    //clip the input -    target_freq = cbx_freq_range.clip(target_freq); - -    //map mode setting to valid integer divider (N) values -    static const uhd::range_t int_n_mode_div_range(16,4095,1); -    static const uhd::range_t frac_n_mode_div_range(19,4091,1); - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, max2870_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,   max2870_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,   max2870_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,   max2870_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,   max2870_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16,  max2870_regs_t::RF_DIVIDER_SELECT_DIV16) -        (32,  max2870_regs_t::RF_DIVIDER_SELECT_DIV32) -        (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); -    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; - -    //Reference doubler for 50% duty cycle -    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 -    //NOTE: MAX2870 goes down to 10MHz ref vs. 12.5MHz on ADF4351 -    if(ref_freq <= 10.0e6) D = max2870_regs_t::REFERENCE_DOUBLER_ENABLED; - -    //increase RF divider until acceptable VCO frequency -    double vco_freq = target_freq; -    //NOTE: MIN freq for MAX2870 VCO is 3GHz vs. 2.2GHz on ADF4351 -    while (vco_freq < 3e9) { -        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 -     */ -    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 -        if (pfd_freq > 25e6) continue; - -        //ignore fractional part of tuning -        N = int(vco_freq/pfd_freq); - -        //Fractional-N calculation -        FRAC = int(boost::math::round((vco_freq/pfd_freq - N)*MOD)); - -        if(is_int_n) { -            if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target -                N++; -            } -            FRAC = 0; -        } - -        //keep N within int divider requirements -        if(is_int_n) { -            if(N < int_n_mode_div_range.start()) continue; -            if(N > int_n_mode_div_range.stop()) continue; -        } else { -            if(N < frac_n_mode_div_range.start()) continue; -            if(N > frac_n_mode_div_range.stop()) continue; -        } - -        //keep pfd freq low enough to achieve 50kHz BS clock -        BS = int(std::ceil(pfd_freq / 50e3)); -        if(BS <= 1023) break; -    } - -    UHD_ASSERT_THROW(R <= 1023); - -    //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 = max2870_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)))/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("%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))) -        regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM; - -    //set frac/int CPL mode -    max2870_regs_t::cpl_t cpl; -    max2870_regs_t::ldf_t ldf; -    max2870_regs_t::cpoc_t cpoc; -    if(is_int_n) { -        cpl = max2870_regs_t::CPL_DISABLED; -        cpoc = max2870_regs_t::CPOC_ENABLED; -        ldf = max2870_regs_t::LDF_INT_N; +    if (unit == dboard_iface::UNIT_RX) +    { +        actual_freq = _rxlo->set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); +        _rxlo->commit();      } else { -        cpl = max2870_regs_t::CPL_ENABLED; -        ldf = max2870_regs_t::LDF_FRAC_N; -        cpoc = max2870_regs_t::CPOC_DISABLED; -    } - -    regs.frac_12_bit = FRAC; -    regs.int_16_bit = N; -    regs.mod_12_bit = MOD; -    regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); -    regs.feedback_select = (target_freq >= 3.0e9) ? max2870_regs_t::FEEDBACK_SELECT_DIVIDED : max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.r_counter_10_bit = R; -    regs.reference_divide_by_2 = T; -    regs.reference_doubler = D; -    regs.band_select_clock_div = (BS & 0x0FF); -    regs.bs_msb = (BS & 0x300) >>8; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); -    regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; -    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; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "%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 -        ); +        actual_freq = _txlo->set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); +        _txlo->set_output_power((actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)) +                                ? max287x_iface::OUTPUT_POWER_2DBM +                                : max287x_iface::OUTPUT_POWER_5DBM); +        _txlo->commit();      } - -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "%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.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 58f79a606..a08d22537 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -17,7 +17,8 @@  #include <uhd/types/device_addr.hpp> -#include "../common/adf435x_common.hpp" +#include "adf435x_common.hpp" +#include "max287x.hpp"  // Common IO Pins  #define LO_LPF_EN       (1 << 15) @@ -223,6 +224,10 @@ protected:          /*! This is the registered instance of the wrapper class, sbx_base. */          sbx_xcvr *self_base; +    private: +        void write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s); +        max287x_iface::sptr _txlo; +        max287x_iface::sptr _rxlo;      };      /*! diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp index 06bfad7d3..1e79c14b0 100644 --- a/host/lib/usrp/dboard/db_ubx.cpp +++ b/host/lib/usrp/dboard/db_ubx.cpp @@ -34,522 +34,11 @@  #include <boost/algorithm/string.hpp>  #include <boost/thread/mutex.hpp>  #include <map> +#include "max287x.hpp"  using namespace uhd;  using namespace uhd::usrp; -#define fMHz (1000000.0) -#define UBX_PROTO_V3_TX_ID   0x73 -#define UBX_PROTO_V3_RX_ID   0x74 -#define UBX_PROTO_V4_TX_ID   0x75 -#define UBX_PROTO_V4_RX_ID   0x76 -#define UBX_V1_40MHZ_TX_ID   0x77 -#define UBX_V1_40MHZ_RX_ID   0x78 -#define UBX_V1_160MHZ_TX_ID  0x79 -#define UBX_V1_160MHZ_RX_ID  0x7a - -/*********************************************************************** - * UBX Synthesizers - **********************************************************************/ -#include "max2870_regs.hpp" -#include "max2871_regs.hpp" - -typedef boost::function<void(std::vector<boost::uint32_t>)> max287x_write_fn; - -class max287x_synthesizer_iface -{ -public: -    virtual bool is_shutdown(void) = 0; -    virtual void shutdown(void) = 0; -    virtual void power_up(void) = 0; -    virtual double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power) = 0; -}; - -class max287x : public max287x_synthesizer_iface -{ -public: -    max287x(max287x_write_fn write_fn) : _write_fn(write_fn) {}; -    virtual ~max287x() {}; - -protected: -    virtual std::set<boost::uint32_t> get_changed_addrs(void) = 0; -    virtual boost::uint32_t get_reg(boost::uint32_t addr) = 0; -    virtual void save_state(void) = 0; - -    void write_regs(void) -    { -        std::vector<boost::uint32_t> regs; -        std::set<boost::uint32_t> changed_regs; - -        // Get only regs with changes -        try { -            changed_regs = get_changed_addrs(); -        } catch (uhd::runtime_error&) { -            // No saved state - write all regs -            for (int addr = 5; addr >= 0; addr--) -                changed_regs.insert(boost::uint32_t(addr)); -        } - -        for (int addr = 5; addr >= 0; addr--) -        { -            if (changed_regs.find(boost::uint32_t(addr)) != changed_regs.end()) -                regs.push_back(get_reg(boost::uint32_t(addr))); -        } - -        // writing reg 0 initiates VCO auto select, so this makes sure it is written -        if (changed_regs.size() and changed_regs.find(0) == changed_regs.end()) -            regs.push_back(get_reg(0)); - -        _write_fn(regs); -        save_state(); -    } - -    double calculate_freq_settings( -        double target_freq, -        double ref_freq, -        double target_pfd_freq, -        bool is_int_n, -        double &pfd_freq, -        int& T, -        int& D, -        int& R, -        int& BS, -        int& N, -        int& FRAC, -        int& MOD, -        int& RFdiv) -    { -        //map mode setting to valid integer divider (N) values -        static const uhd::range_t int_n_mode_div_range(16,4095,1); -        static const uhd::range_t frac_n_mode_div_range(19,4091,1); - -        double actual_freq = 0.0; - -        T = 0; -        D = ref_freq <= 10.0e6 ? 1 : 0; -        R = 0; -        BS = 0; -        N = 0; -        FRAC = 0; -        MOD = 4095; -        RFdiv = 1; - -        //increase RF divider until acceptable VCO frequency (MIN freq for MAX287x VCO is 3GHz) -        double vco_freq = target_freq; -        while (vco_freq < 3e9) -        { -            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. -         * -         * f_pfd = f_ref*(1+D)/(R*(1+T)) -         * f_vco = (N + (FRAC/MOD))*f_pfd -         *     N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD -         * f_rf  = f_vco/RFdiv -         */ -        for(R = int(ref_freq*(1+D)/(target_pfd_freq*(1+T))); R <= 1023; R++) -        { -            //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) -            pfd_freq = ref_freq*(1+D)/(R*(1+T)); - -            //keep the PFD frequency at or below target -            if (pfd_freq > target_pfd_freq) -                continue; - -            //ignore fractional part of tuning -            N = int(vco_freq/pfd_freq); - -            //Fractional-N calculation -            FRAC = int(boost::math::round((vco_freq/pfd_freq - N)*MOD)); - -            if(is_int_n) -            { -                if (FRAC > (MOD / 2)) //Round integer such that actual freq is closest to target -                    N++; -                FRAC = 0; -            } - -            //keep N within int divider requirements -            if(is_int_n) -            { -                if(N < int_n_mode_div_range.start()) continue; -                if(N > int_n_mode_div_range.stop()) continue; -            } -            else -            { -                if(N < frac_n_mode_div_range.start()) continue; -                if(N > frac_n_mode_div_range.stop()) continue; -            } - -            //keep pfd freq low enough to achieve 50kHz BS clock -            BS = int(std::ceil(pfd_freq / 50e3)); -            if(BS <= 1023) break; -        } -        UHD_ASSERT_THROW(R <= 1023); - -        //Reference divide-by-2 for 50% duty cycle -        // if R even, move one divide by 2 to to regs.reference_divide_by_2 -        if(R % 2 == 0) -        { -            T = 1; -            R /= 2; -        } - -        //actual frequency calculation -        actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); - -        UHD_LOGV(rarely) -            << boost::format("MAX287x: 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("MAX287x: tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s" -                ) % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") << std::endl -            << boost::format("MAX287x: Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" -                ) % (pfd_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; - -        return actual_freq; -    } - -    max287x_write_fn _write_fn; -}; - -class max2870 : public max287x -{ -public: -    max2870(max287x_write_fn write_fn) : max287x(write_fn), _first_tune(true) -    { -        // initialize register values (override defaults) -        _regs.retune = max2870_regs_t::RETUNE_DISABLED; -        _regs.clock_div_mode = max2870_regs_t::CLOCK_DIV_MODE_FAST_LOCK; - -        // MAX2870 data sheet says that all registers must be written twice -        // with at least a 20ms delay between writes upon power up.  One -        // write and a 20ms wait are done in power_up().  The second write -        // is done when any other function that does a write to the registers -        // is called.  To ensure all registers are written the second time, the -        // state of the registers is not saved during the first write. -        _save_state = false; -        power_up(); -        _save_state = true; -    }; - -    ~max2870() -    { -        shutdown(); -    }; - -    bool is_shutdown(void) -    { -        return (_regs.power_down == max2870_regs_t::POWER_DOWN_SHUTDOWN); -    }; - -    void shutdown(void) -    { -        _regs.rf_output_enable = max2870_regs_t::RF_OUTPUT_ENABLE_DISABLED; -        _regs.aux_output_enable = max2870_regs_t::AUX_OUTPUT_ENABLE_DISABLED; -        _regs.power_down = max2870_regs_t::POWER_DOWN_SHUTDOWN; -        _regs.muxout = max2870_regs_t::MUXOUT_LOW; -        _regs.ld_pin_mode = max2870_regs_t::LD_PIN_MODE_LOW; -        write_regs(); -    }; - -    void power_up(void) -    { -        _regs.muxout = max2870_regs_t::MUXOUT_DLD; -        _regs.ld_pin_mode = max2870_regs_t::LD_PIN_MODE_DLD; -        _regs.power_down = max2870_regs_t::POWER_DOWN_NORMAL; -        write_regs(); - -        // MAX270 data sheet says to wait at least 20 ms after exiting low power mode -        // before programming final VCO frequency -        boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - -        _first_tune = true; -    }; - -    double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power) -    { -        //map rf divider select output dividers to enums -        static const uhd::dict<int, max2870_regs_t::rf_divider_select_t> rfdivsel_to_enum = -            boost::assign::map_list_of -            (1,   max2870_regs_t::RF_DIVIDER_SELECT_DIV1) -            (2,   max2870_regs_t::RF_DIVIDER_SELECT_DIV2) -            (4,   max2870_regs_t::RF_DIVIDER_SELECT_DIV4) -            (8,   max2870_regs_t::RF_DIVIDER_SELECT_DIV8) -            (16,  max2870_regs_t::RF_DIVIDER_SELECT_DIV16) -            (32,  max2870_regs_t::RF_DIVIDER_SELECT_DIV32) -            (64,  max2870_regs_t::RF_DIVIDER_SELECT_DIV64) -            (128, max2870_regs_t::RF_DIVIDER_SELECT_DIV128); - -        int T = 0; -        int D = ref_freq <= 10.0e6 ? 1 : 0; -        int R, BS, N, FRAC, MOD, RFdiv; -        double pfd_freq = 25e6; - -        double actual_freq = calculate_freq_settings( -            target_freq, ref_freq, 25e6, is_int_n, pfd_freq, T, D, R, BS, N, FRAC, MOD, RFdiv); - -        //load the register values -        _regs.rf_output_enable = max2870_regs_t::RF_OUTPUT_ENABLE_ENABLED; - -        if(is_int_n) { -            _regs.cpl = max2870_regs_t::CPL_DISABLED; -            _regs.ldf = max2870_regs_t::LDF_INT_N; -            _regs.cpoc = max2870_regs_t::CPOC_ENABLED; -            _regs.int_n_mode = max2870_regs_t::INT_N_MODE_INT_N; -        } else { -            _regs.cpl = max2870_regs_t::CPL_ENABLED; -            _regs.ldf = max2870_regs_t::LDF_FRAC_N; -            _regs.cpoc = max2870_regs_t::CPOC_DISABLED; -            _regs.int_n_mode = max2870_regs_t::INT_N_MODE_FRAC_N; -        } - -        _regs.lds = pfd_freq <= 32e6 ? max2870_regs_t::LDS_SLOW : max2870_regs_t::LDS_FAST; - -        _regs.frac_12_bit = FRAC; -        _regs.int_16_bit = N; -        _regs.mod_12_bit = MOD; -        _regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); -        _regs.feedback_select = (target_freq >= 3.0e9) ? -            max2870_regs_t::FEEDBACK_SELECT_DIVIDED : -            max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -        _regs.r_counter_10_bit = R; -        _regs.reference_divide_by_2 = T ? -            max2870_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -            max2870_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -        _regs.reference_doubler = D ? -            max2870_regs_t::REFERENCE_DOUBLER_ENABLED : -            max2870_regs_t::REFERENCE_DOUBLER_DISABLED; -        _regs.band_select_clock_div = BS; -        _regs.bs_msb = (BS & 0x300) >> 8; -        UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); -        _regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - -        switch (output_power) -        { -        case -4: -            _regs.output_power = max2870_regs_t::OUTPUT_POWER_M4DBM; -            break; -        case -1: -            _regs.output_power = max2870_regs_t::OUTPUT_POWER_M1DBM; -            break; -        case 2: -            _regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM; -            break; -        case 5: -            _regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM; -            break; -        } - -        // Write the register values -        write_regs(); - -        // MAX2870 needs a 20ms delay after tuning for the first time -        // for the lock detect to be reliable. -        if (_first_tune) -        { -            boost::this_thread::sleep(boost::posix_time::milliseconds(20)); -            _first_tune = false; -        } - -        return actual_freq; -    }; - -private: -    std::set<boost::uint32_t> get_changed_addrs() -    { -        return _regs.get_changed_addrs<boost::uint32_t>(); -    }; - -    boost::uint32_t get_reg(boost::uint32_t addr) -    { -        return _regs.get_reg(addr); -    }; - -    void save_state() -    { -        if (_save_state) -            _regs.save_state(); -    } - -    max2870_regs_t _regs; -    bool _save_state; -    bool _first_tune; -}; - -class max2871 : public max287x -{ -public: -    max2871(max287x_write_fn write_fn) : max287x(write_fn), _first_tune(true) -    { -        // initialize register values (override defaults) -        _regs.retune = max2871_regs_t::RETUNE_DISABLED; -        //_regs.csm = max2871_regs_t::CSM_ENABLED;  // tried it - caused long lock times -        _regs.charge_pump_current = max2871_regs_t::CHARGE_PUMP_CURRENT_5_12MA; - -        // MAX2871 data sheet says that all registers must be written twice -        // with at least a 20ms delay between writes upon power up.  One -        // write and a 20ms wait are done in power_up().  The second write -        // is done when any other function that does a write to the registers -        // is called.  To ensure all registers are written the second time, the -        // state of the registers is not saved during the first write. -        _save_state = false; -        power_up(); -        _save_state = true; -    }; - -    ~max2871() -    { -        shutdown(); -    }; - -    bool is_shutdown(void) -    { -        return (_regs.power_down == max2871_regs_t::POWER_DOWN_SHUTDOWN); -    }; - -    void shutdown(void) -    { -        _regs.rf_output_enable = max2871_regs_t::RF_OUTPUT_ENABLE_DISABLED; -        _regs.aux_output_enable = max2871_regs_t::AUX_OUTPUT_ENABLE_DISABLED; -        _regs.power_down = max2871_regs_t::POWER_DOWN_SHUTDOWN; -        _regs.ld_pin_mode = max2871_regs_t::LD_PIN_MODE_LOW; -        _regs.muxout = max2871_regs_t::MUXOUT_TRI_STATE; -        write_regs(); -    }; - -    void power_up(void) -    { -        _regs.ld_pin_mode = max2871_regs_t::LD_PIN_MODE_DLD; -        _regs.power_down = max2871_regs_t::POWER_DOWN_NORMAL; -        _regs.muxout = max2871_regs_t::MUXOUT_TRI_STATE; -        write_regs(); - -        // MAX271 data sheet says to wait at least 20 ms after exiting low power mode -        // before programming final VCO frequency -        boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - -        _first_tune = true; -    }; - -    double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power) -    { -        //map rf divider select output dividers to enums -        static const uhd::dict<int, max2871_regs_t::rf_divider_select_t> rfdivsel_to_enum = -            boost::assign::map_list_of -            (1,   max2871_regs_t::RF_DIVIDER_SELECT_DIV1) -            (2,   max2871_regs_t::RF_DIVIDER_SELECT_DIV2) -            (4,   max2871_regs_t::RF_DIVIDER_SELECT_DIV4) -            (8,   max2871_regs_t::RF_DIVIDER_SELECT_DIV8) -            (16,  max2871_regs_t::RF_DIVIDER_SELECT_DIV16) -            (32,  max2871_regs_t::RF_DIVIDER_SELECT_DIV32) -            (64,  max2871_regs_t::RF_DIVIDER_SELECT_DIV64) -            (128, max2871_regs_t::RF_DIVIDER_SELECT_DIV128); - -        int T = 0; -        int D = ref_freq <= 10.0e6 ? 1 : 0; -        int R, BS, N, FRAC, MOD, RFdiv; -        double pfd_freq = 50e6; - -        double actual_freq = calculate_freq_settings( -            target_freq, ref_freq, 50e6, is_int_n, pfd_freq, T, D, R, BS, N, FRAC, MOD, RFdiv); - -        //load the register values -        _regs.rf_output_enable = max2871_regs_t::RF_OUTPUT_ENABLE_ENABLED; - -        if(is_int_n) { -            _regs.cpl = max2871_regs_t::CPL_DISABLED; -            _regs.ldf = max2871_regs_t::LDF_INT_N; -            _regs.int_n_mode = max2871_regs_t::INT_N_MODE_INT_N; -        } else { -            _regs.cpl = max2871_regs_t::CPL_ENABLED; -            _regs.ldf = max2871_regs_t::LDF_FRAC_N; -            _regs.int_n_mode = max2871_regs_t::INT_N_MODE_FRAC_N; -        } - -        _regs.lds = pfd_freq <= 32e6 ? max2871_regs_t::LDS_SLOW : max2871_regs_t::LDS_FAST; - -        _regs.frac_12_bit = FRAC; -        _regs.int_16_bit = N; -        _regs.mod_12_bit = MOD; -        _regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); -        _regs.feedback_select = (target_freq >= 3.0e9) ? -            max2871_regs_t::FEEDBACK_SELECT_DIVIDED : -            max2871_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -        _regs.r_counter_10_bit = R; -        _regs.reference_divide_by_2 = T ? -            max2871_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -            max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -        _regs.reference_doubler = D ? -            max2871_regs_t::REFERENCE_DOUBLER_ENABLED : -            max2871_regs_t::REFERENCE_DOUBLER_DISABLED; -        _regs.band_select_clock_div = BS; -        _regs.bs_msb = (BS & 0x300) >> 8; -        UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); -        _regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - -        switch (output_power) -        { -        case -4: -            _regs.output_power = max2871_regs_t::OUTPUT_POWER_M4DBM; -            break; -        case -1: -            _regs.output_power = max2871_regs_t::OUTPUT_POWER_M1DBM; -            break; -        case 2: -            _regs.output_power = max2871_regs_t::OUTPUT_POWER_2DBM; -            break; -        case 5: -            _regs.output_power = max2871_regs_t::OUTPUT_POWER_5DBM; -            break; -        default: -            UHD_THROW_INVALID_CODE_PATH(); -            break; -        } - -        write_regs(); - -        // MAX2871 needs a 20ms delay after tuning for the first time -        // for the lock detect to be reliable. -        if (_first_tune) -        { -            boost::this_thread::sleep(boost::posix_time::milliseconds(20)); -            _first_tune = false; -        } - -        return actual_freq; -    }; - -private: -    std::set<boost::uint32_t> get_changed_addrs() -    { -        return _regs.get_changed_addrs<boost::uint32_t>(); -    }; - -    boost::uint32_t get_reg(boost::uint32_t addr) -    { -        return _regs.get_reg(addr); -    }; - -    void save_state() -    { -        if (_save_state) -            _regs.save_state(); -    } - -    max2871_regs_t _regs; -    bool _save_state; -    bool _first_tune; -}; -  /***********************************************************************   * UBX Data Structures   **********************************************************************/ @@ -606,7 +95,7 @@ struct ubx_gpio_field_info_t      boost::uint32_t offset;      boost::uint32_t mask;      boost::uint32_t width; -    enum direction_t {OUTPUT,INPUT} direction; +    enum {OUTPUT,INPUT} direction;      bool is_atr_controlled;      boost::uint32_t atr_idle;      boost::uint32_t atr_tx; @@ -653,7 +142,16 @@ enum spi_dest_t {  /***********************************************************************   * UBX Constants   **********************************************************************/ -static const freq_range_t ubx_freq_range(1.0e7, 6.0e9); +#define fMHz (1000000.0) +static const dboard_id_t UBX_PROTO_V3_TX_ID(0x73); +static const dboard_id_t UBX_PROTO_V3_RX_ID(0x74); +static const dboard_id_t UBX_PROTO_V4_TX_ID(0x75); +static const dboard_id_t UBX_PROTO_V4_RX_ID(0x76); +static const dboard_id_t UBX_V1_40MHZ_TX_ID(0x77); +static const dboard_id_t UBX_V1_40MHZ_RX_ID(0x78); +static const dboard_id_t UBX_V1_160MHZ_TX_ID(0x79); +static const dboard_id_t UBX_V1_160MHZ_RX_ID(0x7A); +static const freq_range_t ubx_freq_range(10e6, 6.0e9);  static const gain_range_t ubx_tx_gain_range(0, 31.5, double(0.5));  static const gain_range_t ubx_rx_gain_range(0, 31.5, double(0.5));  static const std::vector<std::string> ubx_pgas = boost::assign::list_of("PGA-TX")("PGA-RX"); @@ -664,6 +162,7 @@ static const std::vector<std::string> ubx_power_modes = boost::assign::list_of("  static const std::vector<std::string> ubx_xcvr_modes = boost::assign::list_of("FDX")("TX")("TX/RX")("RX");  static const ubx_gpio_field_info_t ubx_proto_gpio_info[] = { +    //Field         Unit                  Offset Mask      Width    Direction                   ATR    IDLE,TX,RX,FDX      {SPI_ADDR,      dboard_iface::UNIT_TX,  0,  0x7,        3,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0},      {TX_EN_N,       dboard_iface::UNIT_TX,  3,  0x1<<3,     1,  ubx_gpio_field_info_t::INPUT,  true,   1,  0,  1,  0},      {RX_EN_N,       dboard_iface::UNIT_TX,  4,  0x1<<4,     1,  ubx_gpio_field_info_t::INPUT,  true,   1,  1,  0,  0}, @@ -676,6 +175,7 @@ static const ubx_gpio_field_info_t ubx_proto_gpio_info[] = {  };  static const ubx_gpio_field_info_t ubx_v1_gpio_info[] = { +    //Field         Unit                  Offset Mask      Width    Direction                   ATR    IDLE,TX,RX,FDX      {SPI_ADDR,      dboard_iface::UNIT_TX,   0,  0x7,        3,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0},      {CPLD_RST_N,    dboard_iface::UNIT_TX,   3,  0x1<<3,     1,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0},      {RX_ANT,        dboard_iface::UNIT_TX,   4,  0x1<<4,     1,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0}, @@ -695,7 +195,8 @@ static const ubx_gpio_field_info_t ubx_v1_gpio_info[] = {   * Macros for routing and writing SPI registers   **********************************************************************/  #define ROUTE_SPI(iface, dest)  \ -    iface->set_gpio_out(dboard_iface::UNIT_TX, dest, 0x7); +    set_gpio_field(SPI_ADDR, dest); \ +    write_gpio();  #define WRITE_SPI(iface, val)   \      iface->write_spi(dboard_iface::UNIT_TX, spi_config_t::EDGE_RISE, val, 32); @@ -708,6 +209,9 @@ class ubx_xcvr : public xcvr_dboard_base  public:      ubx_xcvr(ctor_args_t args) : xcvr_dboard_base(args)      { +        double bw = 40e6; +        double pfd_freq_max = 25e6; +          ////////////////////////////////////////////////////////////////////          // Setup GPIO hardware          //////////////////////////////////////////////////////////////////// @@ -716,12 +220,15 @@ public:          dboard_id_t tx_id = get_tx_id();          if (rx_id == UBX_PROTO_V3_RX_ID and tx_id == UBX_PROTO_V3_TX_ID)              _rev = 0; -        if (rx_id == UBX_PROTO_V4_RX_ID and tx_id == UBX_PROTO_V4_TX_ID) +        else if (rx_id == UBX_PROTO_V4_RX_ID and tx_id == UBX_PROTO_V4_TX_ID)              _rev = 1;          else if (rx_id == UBX_V1_40MHZ_RX_ID and tx_id == UBX_V1_40MHZ_TX_ID)              _rev = 1;          else if (rx_id == UBX_V1_160MHZ_RX_ID and tx_id == UBX_V1_160MHZ_TX_ID) +        { +            bw = 160e6;              _rev = 1; +        }          else              UHD_THROW_INVALID_CODE_PATH(); @@ -730,10 +237,12 @@ public:          case 0:              for (size_t i = 0; i < sizeof(ubx_proto_gpio_info) / sizeof(ubx_gpio_field_info_t); i++)                  _gpio_map[ubx_proto_gpio_info[i].id] = ubx_proto_gpio_info[i]; +            pfd_freq_max = 25e6;              break;          case 1:              for (size_t i = 0; i < sizeof(ubx_v1_gpio_info) / sizeof(ubx_gpio_field_info_t); i++)                  _gpio_map[ubx_v1_gpio_info[i].id] = ubx_v1_gpio_info[i]; +            pfd_freq_max = 50e6;              break;          } @@ -757,6 +266,34 @@ public:          }          // Enable the reference clocks that we need +        if (_rev >= 1) +        { +            // set dboard clock rates to as close to the max PFD freq as possible +            if (_iface->get_clock_rate(dboard_iface::UNIT_RX) > pfd_freq_max) +            { +                std::vector<double> rates = _iface->get_clock_rates(dboard_iface::UNIT_RX); +                double highest_rate = 0.0; +                BOOST_FOREACH(double rate, rates) +                { +                    if (rate <= pfd_freq_max and rate > highest_rate) +                        highest_rate = rate; +                } +                _iface->set_clock_rate(dboard_iface::UNIT_RX, highest_rate); +                _rx_target_pfd_freq = highest_rate; +            } +            if (_iface->get_clock_rate(dboard_iface::UNIT_TX) > pfd_freq_max) +            { +                std::vector<double> rates = _iface->get_clock_rates(dboard_iface::UNIT_TX); +                double highest_rate = 0.0; +                BOOST_FOREACH(double rate, rates) +                { +                    if (rate <= pfd_freq_max and rate > highest_rate) +                        highest_rate = rate; +                } +                _iface->set_clock_rate(dboard_iface::UNIT_TX, highest_rate); +                _tx_target_pfd_freq = highest_rate; +            } +        }          _iface->set_clock_enabled(dboard_iface::UNIT_TX, true);          _iface->set_clock_enabled(dboard_iface::UNIT_RX, true); @@ -801,17 +338,37 @@ public:          // Initialize LOs          if (_rev == 0)          { -            _txlo1.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1))); -            _txlo2.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1))); -            _rxlo1.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1))); -            _rxlo2.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1))); +            _txlo1 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1)); +            _txlo2 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1)); +            _rxlo1 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1)); +            _rxlo2 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1)); +            std::vector<max287x_iface::sptr> los = boost::assign::list_of(_txlo1)(_txlo2)(_rxlo1)(_rxlo2); +            BOOST_FOREACH(max287x_iface::sptr lo, los) +            { +                lo->set_auto_retune(false); +                lo->set_clock_divider_mode(max287x_iface::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF); +                lo->set_muxout_mode(max287x_iface::MUXOUT_DLD); +                lo->set_ld_pin_mode(max287x_iface::LD_PIN_MODE_DLD); +            }          }          else if (_rev == 1)          { -            _txlo1.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1))); -            _txlo2.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1))); -            _rxlo1.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1))); -            _rxlo2.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1))); +            _txlo1 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1)); +            _txlo2 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1)); +            _rxlo1 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1)); +            _rxlo2 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1)); +            std::vector<max287x_iface::sptr> los = boost::assign::list_of(_txlo1)(_txlo2)(_rxlo1)(_rxlo2); +            BOOST_FOREACH(max287x_iface::sptr lo, los) +            { +                lo->set_auto_retune(false); +                lo->set_clock_divider_mode(max287x_iface::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF); +                //lo->set_cycle_slip_mode(true);  // tried it - caused longer lock times +                lo->set_charge_pump_current(max287x_iface::CHARGE_PUMP_CURRENT_5_12MA); +                lo->set_muxout_mode(max287x_iface::MUXOUT_SYNC); +                lo->set_ld_pin_mode(max287x_iface::LD_PIN_MODE_DLD); +                lo->set_low_noise_and_spur(max287x_iface::LOW_NOISE_AND_SPUR_LOW_NOISE); +                lo->set_phase(0); +            }          }          else          { @@ -819,6 +376,7 @@ public:          }          // Initialize CPLD register +        _prev_cpld_value = 0xFFFF;          _cpld_reg.value = 0;          write_cpld_reg(); @@ -865,9 +423,9 @@ public:          get_tx_subtree()->create<bool>("use_lo_offset")              .set(false);          get_tx_subtree()->create<double>("bandwidth/value") -            .set(2*20.0e6); //20MHz low-pass, complex double-sided, so it should be 2x20MHz=40MHz +            .set(bw);          get_tx_subtree()->create<meta_range_t>("bandwidth/range") -            .set(freq_range_t(2*20.0e6, 2*20.0e6)); +            .set(freq_range_t(bw, bw));          ////////////////////////////////////////////////////////////////////          // Register RX properties @@ -898,9 +456,9 @@ public:          get_rx_subtree()->create<bool>("use_lo_offset")              .set(false);          get_rx_subtree()->create<double>("bandwidth/value") -            .set(2*20.0e6); //20MHz low-pass, complex double-sided, so it should be 2x20MHz=40MHz +            .set(bw);          get_rx_subtree()->create<meta_range_t>("bandwidth/range") -            .set(freq_range_t(2*20.0e6, 2*20.0e6)); +            .set(freq_range_t(bw, bw));      }      ~ubx_xcvr(void) @@ -938,14 +496,14 @@ private:      **********************************************************************/      void write_spi_reg(spi_dest_t dest, boost::uint32_t value)      { -        boost::mutex::scoped_lock lock(_spi_lock); +        boost::mutex::scoped_lock lock(_spi_mutex);          ROUTE_SPI(_iface, dest);          WRITE_SPI(_iface, value);      }      void write_spi_regs(spi_dest_t dest, std::vector<boost::uint32_t> values)      { -        boost::mutex::scoped_lock lock(_spi_lock); +        boost::mutex::scoped_lock lock(_spi_mutex);          ROUTE_SPI(_iface, dest);          BOOST_FOREACH(boost::uint32_t value, values)              WRITE_SPI(_iface, value); @@ -958,7 +516,11 @@ private:      void write_cpld_reg()      { -        write_spi_reg(CPLD, _cpld_reg.value); +        if (_cpld_reg.value != _prev_cpld_value) +        { +            write_spi_reg(CPLD, _cpld_reg.value); +            _prev_cpld_value = _cpld_reg.value; +        }      }      void set_gpio_field(ubx_gpio_field_id_t id, boost::uint32_t value) @@ -996,7 +558,10 @@ private:              return 0;          ubx_gpio_field_info_t field_info = entry->second;          if (field_info.direction == ubx_gpio_field_info_t::INPUT) -            return 0; +        { +            ubx_gpio_reg_t *reg = (field_info.unit == dboard_iface::UNIT_TX ? &_tx_gpio_reg : &_rx_gpio_reg); +            return (reg->value >> field_info.offset) & field_info.mask; +        }          // Read register          boost::uint32_t value = _iface->read_gpio(field_info.unit); @@ -1028,6 +593,7 @@ private:       **********************************************************************/      sensor_value_t get_locked(const std::string &pll_name)      { +        boost::mutex::scoped_lock lock(_mutex);          assert_has(ubx_plls, pll_name, "ubx pll name");          if(pll_name == "TXLO") @@ -1053,6 +619,7 @@ private:      // Set RX antennas      void set_rx_ant(const std::string &ant)      { +        boost::mutex::scoped_lock lock(_mutex);          //validate input          assert_has(ubx_rx_antennas, ant, "ubx rx antenna name"); @@ -1070,6 +637,7 @@ private:       **********************************************************************/      double set_tx_gain(double gain)      { +        boost::mutex::scoped_lock lock(_mutex);          gain = ubx_tx_gain_range.clip(gain);          int attn_code = int(std::floor(gain * 2));          _ubx_tx_atten_val = ((attn_code & 0x3F) << 10); @@ -1082,6 +650,7 @@ private:      double set_rx_gain(double gain)      { +        boost::mutex::scoped_lock lock(_mutex);          gain = ubx_rx_gain_range.clip(gain);          int attn_code = int(std::floor(gain * 2));          _ubx_rx_atten_val = ((attn_code & 0x3F) << 10); @@ -1097,6 +666,7 @@ private:      **********************************************************************/      double set_tx_freq(double freq)      { +        boost::mutex::scoped_lock lock(_mutex);          double freq_lo1 = 0.0;          double freq_lo2 = 0.0;          double ref_freq = _iface->get_clock_rate(dboard_iface::UNIT_TX); @@ -1131,11 +701,12 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 1);              set_cpld_field(TXHB_SEL, 0); -            write_cpld_reg();              // Set LO1 to IF of 2100 MHz (offset from RX IF to reduce leakage) -            freq_lo1 = _txlo1->set_freq_and_power(2100*fMHz, ref_freq, is_int_n, 5); +            freq_lo1 = _txlo1->set_frequency(2100*fMHz, ref_freq, _tx_target_pfd_freq, is_int_n); +            _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _txlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2); +            freq_lo2 = _txlo2->set_frequency(freq_lo1 - freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            _txlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= (500*fMHz)) && (freq <= (800*fMHz)))          { @@ -1144,8 +715,8 @@ private:              set_cpld_field(TXLO1_FSEL1, 1);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (800*fMHz)) && (freq <= (1000*fMHz)))          { @@ -1154,8 +725,8 @@ private:              set_cpld_field(TXLO1_FSEL1, 1);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          }          else if ((freq > (1000*fMHz)) && (freq <= (2200*fMHz)))          { @@ -1164,8 +735,8 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (2200*fMHz)) && (freq <= (2500*fMHz)))          { @@ -1174,8 +745,8 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (2500*fMHz)) && (freq <= (6000*fMHz)))          { @@ -1184,8 +755,50 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); +        } + +        // To reduce the number of commands issued to the device, write to the +        // SPI destination already addressed first.  This avoids the writes to +        // the GPIO registers to route the SPI to the same destination. +        switch (get_gpio_field(SPI_ADDR)) +        { +        case TXLO1: +            _txlo1->commit(); +            if (freq < (500*fMHz)) _txlo2->commit(); +            write_cpld_reg(); +            break; +        case TXLO2: +            if (freq < (500*fMHz)) _txlo2->commit(); +            _txlo1->commit();              write_cpld_reg(); -            freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5); +        break; +        default: +            write_cpld_reg(); +            _txlo1->commit(); +            if (freq < (500*fMHz)) _txlo2->commit(); +            break; +        } + +        if (_txlo1->can_sync()) +        { +            // Send phase sync signal only if the command time is set +            uhd::time_spec_t cmd_time = _iface->get_command_time(); +            if (cmd_time != uhd::time_spec_t(0.0)) +            { +                // Delay 400 microseconds to allow LOs to lock +                cmd_time += uhd::time_spec_t(0.0004); +                _iface->set_command_time(cmd_time); +                set_gpio_field(TXLO1_SYNC, 1); +                set_gpio_field(TXLO2_SYNC, 1); +                write_gpio(); +                // De-assert SYNC +                // Head of line blocking means the time does not need to be set. +                set_gpio_field(TXLO1_SYNC, 0); +                set_gpio_field(TXLO2_SYNC, 0); +                write_gpio(); +            }          }          _tx_freq = freq_lo1 - freq_lo2; @@ -1199,6 +812,7 @@ private:      double set_rx_freq(double freq)      { +        boost::mutex::scoped_lock lock(_mutex);          double freq_lo1 = 0.0;          double freq_lo2 = 0.0;          double ref_freq = _iface->get_clock_rate(dboard_iface::UNIT_RX); @@ -1231,11 +845,12 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 1);              set_cpld_field(RXHB_SEL, 0); -            write_cpld_reg();              // Set LO1 to IF of 2380 MHz (2440 MHz filter center minus 60 MHz offset to minimize LO leakage) -            freq_lo1 = _rxlo1->set_freq_and_power(2380*fMHz, ref_freq, is_int_n, 5); +            freq_lo1 = _rxlo1->set_frequency(2380*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _rxlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2); +            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 100*fMHz) && (freq < 500*fMHz))          { @@ -1246,11 +861,12 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 1);              set_cpld_field(RXHB_SEL, 0); -            write_cpld_reg();              // Set LO1 to IF of 2440 (center of filter) -            freq_lo1 = _rxlo1->set_freq_and_power(2440*fMHz, ref_freq, is_int_n, 5); +            freq_lo1 = _rxlo1->set_frequency(2440*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _rxlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2); +            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 500*fMHz) && (freq < 800*fMHz))          { @@ -1261,8 +877,8 @@ private:              set_cpld_field(RXLO1_FSEL1, 1);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 800*fMHz) && (freq < 1000*fMHz))          { @@ -1273,8 +889,8 @@ private:              set_cpld_field(RXLO1_FSEL1, 1);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          }          else if ((freq >= 1000*fMHz) && (freq < 1500*fMHz))          { @@ -1285,8 +901,8 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 1500*fMHz) && (freq < 2200*fMHz))          { @@ -1297,8 +913,8 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 2200*fMHz) && (freq < 2500*fMHz))          { @@ -1309,8 +925,8 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            write_cpld_reg(); -            freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 2500*fMHz) && (freq <= 6000*fMHz))          { @@ -1321,15 +937,59 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); +        } + +        // To reduce the number of commands issued to the device, write to the +        // SPI destination already addressed first.  This avoids the writes to +        // the GPIO registers to route the SPI to the same destination. +        switch (get_gpio_field(SPI_ADDR)) +        { +        case RXLO1: +            _rxlo1->commit(); +            if (freq < (500*fMHz)) _rxlo2->commit(); +            write_cpld_reg(); +            break; +        case RXLO2: +            if (freq < (500*fMHz)) _rxlo2->commit(); +            _rxlo1->commit(); +            write_cpld_reg(); +        break; +        default:              write_cpld_reg(); -            freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5); +            _rxlo1->commit(); +            if (freq < (500*fMHz)) _rxlo2->commit(); +            break; +        } + +        if (_rxlo1->can_sync()) +        { +            // Send phase sync signal only if the command time is set +            uhd::time_spec_t cmd_time = _iface->get_command_time(); +            if (cmd_time != uhd::time_spec_t(0.0)) +            { +                // Delay 400 microseconds to allow LOs to lock +                cmd_time += uhd::time_spec_t(0.0004); +                _iface->set_command_time(cmd_time); +                set_gpio_field(RXLO1_SYNC, 1); +                set_gpio_field(RXLO2_SYNC, 1); +                write_gpio(); +                // De-assert SYNC +                // Head of line blocking means the time does not need to be set. +                set_gpio_field(RXLO1_SYNC, 0); +                set_gpio_field(RXLO2_SYNC, 0); +                write_gpio(); +            }          } -        freq = freq_lo1 - freq_lo2; +        _rx_freq = freq_lo1 - freq_lo2; +        _rxlo1_freq = freq_lo1; +        _rxlo2_freq = freq_lo2; -        UHD_LOGV(rarely) << boost::format("UBX RX: the actual frequency is %f MHz") % (freq/1e6) << std::endl; +        UHD_LOGV(rarely) << boost::format("UBX RX: the actual frequency is %f MHz") % (_rx_freq/1e6) << std::endl; -        return freq; +        return _rx_freq;      }      /*********************************************************************** @@ -1337,6 +997,7 @@ private:      **********************************************************************/      void set_power_mode(std::string mode)      { +        boost::mutex::scoped_lock lock(_mutex);          if (mode == "performance")          {              // FIXME:  Response to ATR change is too slow for some components, @@ -1392,12 +1053,16 @@ private:      * Variables      **********************************************************************/      dboard_iface::sptr _iface; -    boost::mutex _spi_lock; +    boost::mutex _spi_mutex; +    boost::mutex _mutex;      ubx_cpld_reg_t _cpld_reg; -    boost::shared_ptr<max287x_synthesizer_iface> _txlo1; -    boost::shared_ptr<max287x_synthesizer_iface> _txlo2; -    boost::shared_ptr<max287x_synthesizer_iface> _rxlo1; -    boost::shared_ptr<max287x_synthesizer_iface> _rxlo2; +    boost::uint32_t _prev_cpld_value; +    boost::shared_ptr<max287x_iface> _txlo1; +    boost::shared_ptr<max287x_iface> _txlo2; +    boost::shared_ptr<max287x_iface> _rxlo1; +    boost::shared_ptr<max287x_iface> _rxlo2; +    double _tx_target_pfd_freq; +    double _rx_target_pfd_freq;      double _tx_gain;      double _rx_gain;      double _tx_freq; @@ -1431,8 +1096,8 @@ static dboard_base::sptr make_ubx(dboard_base::ctor_args_t args)  UHD_STATIC_BLOCK(reg_ubx_dboards)  { -    dboard_manager::register_dboard(0x0074, 0x0073, &make_ubx, "UBX v0.3"); -    dboard_manager::register_dboard(0x0076, 0x0075, &make_ubx, "UBX v0.4"); -    dboard_manager::register_dboard(0x0078, 0x0077, &make_ubx, "UBX-40 v1"); -    dboard_manager::register_dboard(0x007a, 0x0079, &make_ubx, "UBX-160 v1"); +    dboard_manager::register_dboard(UBX_PROTO_V3_RX_ID, UBX_PROTO_V3_TX_ID, &make_ubx, "UBX v0.3"); +    dboard_manager::register_dboard(UBX_PROTO_V4_RX_ID, UBX_PROTO_V4_TX_ID, &make_ubx, "UBX v0.4"); +    dboard_manager::register_dboard(UBX_V1_40MHZ_RX_ID, UBX_V1_40MHZ_TX_ID, &make_ubx, "UBX-40 v1"); +    dboard_manager::register_dboard(UBX_V1_160MHZ_RX_ID, UBX_V1_160MHZ_TX_ID, &make_ubx, "UBX-160 v1");  } diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index f80aeda77..81cdaefac 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -63,7 +63,7 @@ static int tx_pga0_gain_to_iobits(double &gain){              (attn_code &  8 ? 0 : TX_ATTN_8) |              (attn_code &  4 ? 0 : TX_ATTN_4) |              (attn_code &  2 ? 0 : TX_ATTN_2) | -            (attn_code &  1 ? 0 : TX_ATTN_1)  +            (attn_code &  1 ? 0 : TX_ATTN_1)          ) & TX_ATTN_MASK;      UHD_LOGV(often) << boost::format( @@ -220,8 +220,8 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler)      static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4351_regs_t::PRESCALER_4_5 -        (1,75) //adf4351_regs_t::PRESCALER_8_9 +        (adf4351_regs_t::PRESCALER_4_5, 23) +        (adf4351_regs_t::PRESCALER_8_9, 75)      ;      //map rf divider select output dividers to enums @@ -237,14 +237,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      double reference_freq = self_base->get_iface()->get_clock_rate(unit);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer -    //frequency must 2x the target frequency +    //frequency must 2x the target frequency.  This introduces a 180 degree phase +    //ambiguity when trying to synchronize the phase of multiple boards.      double synth_target_freq = target_freq * 2; -    //TODO: Document why the following has to be true -    bool div_resync_enabled = (target_freq > reference_freq);      adf4351_regs_t::prescaler_t prescaler = -        synth_target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; -     +        synth_target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; +      adf435x_tuning_constraints tuning_constraints;      tuning_constraints.force_frac0 = is_int_n;      tuning_constraints.band_sel_freq_max = 100e3; @@ -252,9 +251,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      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); -    //When divider resync is enabled, a 180 deg phase error is introduced when syncing -    //multiple WBX boards. Switching to fundamental mode works arounds this issue. -    tuning_constraints.feedback_after_divider = div_resync_enabled; +    //The feedback of the divided frequency must be disabled whenever the target frequency +    //divided by the minimum PFD frequency cannot meet the minimum integer divider (N) value. +    //If it is disabled, additional phase ambiguity will be introduced.  With a minimum PFD +    //frequency of 10 MHz, synthesizer frequencies below 230 MHz (LO frequencies below 115 MHz) +    //will have too much ambiguity to synchronize. +    tuning_constraints.feedback_after_divider = +        (int(synth_target_freq / 10e6) >= prescaler_to_min_int_div[prescaler]);      double synth_actual_freq = 0;      adf435x_tuning_settings tuning_settings = tune_adf435x_synth( @@ -281,7 +284,7 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      regs.feedback_select        = tuning_constraints.feedback_after_divider ?                                      adf4351_regs_t::FEEDBACK_SELECT_DIVIDED :                                      adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = div_resync_enabled ? +    regs.clock_div_mode         = tuning_constraints.feedback_after_divider ?                                      adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE :                                      adf4351_regs_t::CLOCK_DIV_MODE_FAST_LOCK;      regs.prescaler              = prescaler; diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp index fa4915ed1..dadfb71e9 100644 --- a/host/lib/usrp/e300/e300_io_impl.cpp +++ b/host/lib/usrp/e300/e300_io_impl.cpp @@ -91,12 +91,21 @@ void e300_impl::_update_tick_rate(const double rate)      }  } +#define CHECK_BANDWIDTH(dir) \ +    if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \ +        UHD_MSG(warning) \ +            << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \ +            << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \ +            << std::endl; \ +    } +  void e300_impl::_update_rx_samp_rate(const size_t dspno, const double rate)  {      boost::shared_ptr<sph::recv_packet_streamer> my_streamer =          boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[dspno].rx_streamer.lock());      if (my_streamer)          my_streamer->set_samp_rate(rate); +    CHECK_BANDWIDTH("Rx");  }  void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate) @@ -105,6 +114,7 @@ void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate)          boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock());      if (my_streamer)          my_streamer->set_samp_rate(rate); +    CHECK_BANDWIDTH("Tx");  }  /*********************************************************************** diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp index 2e922a3e2..8ba7b54ca 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.cpp +++ b/host/lib/usrp/usrp1/codec_ctrl.cpp @@ -357,7 +357,7 @@ double usrp1_codec_ctrl_impl::fine_tune(double codec_rate, double target_freq)      static const double scale_factor = std::pow(2.0, 24);      boost::uint32_t freq_word = boost::uint32_t( -        boost::math::round(abs((target_freq / codec_rate) * scale_factor))); +        boost::math::round(std::abs((target_freq / codec_rate) * scale_factor)));      double actual_freq = freq_word * codec_rate / scale_factor; diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 337a1ad6f..cfaee0ddc 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -30,7 +30,7 @@ extern "C" {  #endif  //fpga and firmware compatibility numbers -#define USRP2_FPGA_COMPAT_NUM 10 +#define USRP2_FPGA_COMPAT_NUM 11  #define USRP2_FW_COMPAT_NUM 12  #define USRP2_FW_VER_MINOR 4 diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp index 0276a7a66..e0544862d 100644 --- a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp @@ -33,7 +33,7 @@ static const size_t POKE32_CMD = (1 << 8);  static const size_t PEEK32_CMD = 0;  static const double ACK_TIMEOUT = 0.5;  static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command -static const boost::uint32_t MAX_SEQS_OUT = 15; +static const boost::uint32_t MAX_SEQS_OUT = 63;  #define SPI_DIV SR_SPI_CORE + 0  #define SPI_CTRL SR_SPI_CORE + 1 diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index 3aebfea68..04a9e4bec 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -25,6 +25,8 @@  #include <cstdlib>  static const double X300_REF_CLK_OUT_RATE  = 10e6; +static const boost::uint16_t X300_MAX_CLKOUT_DIV = 1045; +static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6;  using namespace uhd; @@ -36,375 +38,480 @@ class x300_clock_ctrl_impl : public x300_clock_ctrl    {  public: -~x300_clock_ctrl_impl(void) {} - -x300_clock_ctrl_impl(uhd::spi_iface::sptr spiface, -    const size_t slaveno, -    const size_t hw_rev, -    const double master_clock_rate, -    const double system_ref_rate): -    _spiface(spiface), -    _slaveno(slaveno), -    _hw_rev(hw_rev), -    _master_clock_rate(master_clock_rate), -    _system_ref_rate(system_ref_rate) -{ -} - -void reset_clocks() { -    set_master_clock_rate(_master_clock_rate); -} - -void sync_clocks(void) { -    //soft sync: -    //put the sync IO into output mode - FPGA must be input -    //write low, then write high - this triggers a soft sync -    _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_LOW; -    this->write_regs(11); -    _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_HIGH; -    this->write_regs(11); -} - -double get_master_clock_rate(void) { -    return _master_clock_rate; -} - -double get_sysref_clock_rate(void) { -    return _system_ref_rate; -} +    ~x300_clock_ctrl_impl(void) {} -double get_refout_clock_rate(void) { -    //We support only one reference output rate -    return X300_REF_CLK_OUT_RATE; -} - -void set_dboard_rate(const x300_clock_which_t, double rate) { -    if(not doubles_are_equal(rate, get_master_clock_rate())) { -        throw uhd::not_implemented_error("x3xx set dboard clock rate does not support setting an arbitrary clock rate"); +    x300_clock_ctrl_impl(uhd::spi_iface::sptr spiface, +        const size_t slaveno, +        const size_t hw_rev, +        const double master_clock_rate, +        const double system_ref_rate): +        _spiface(spiface), +        _slaveno(slaveno), +        _hw_rev(hw_rev), +        _master_clock_rate(master_clock_rate), +        _system_ref_rate(system_ref_rate) +    { +        init();      } -} - -std::vector<double> get_dboard_rates(const x300_clock_which_t) { -    /* Right now, the only supported daughterboard clock rate is the master clock -    * rate. TODO Implement divider settings for lower clock rates for legacy -    * daughterboard support. */ - -    std::vector<double> rates; -    rates.push_back(get_master_clock_rate()); -    return rates; -} -void set_ref_out(const bool enable) { -    // TODO  Implement divider configuration to allow for configurable output -    // rates -    if (enable) -        _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_LVDS; -    else -        _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_P_DOWN; -    this->write_regs(8); -} - -void write_regs(boost::uint8_t addr) { -    boost::uint32_t data = _lmk04816_regs.get_reg(addr); -    _spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32); -} - - -private: - -void set_master_clock_rate(double clock_rate) { -    /* The X3xx has two primary rates. The first is the -     * _system_ref_rate, which is sourced from the "clock_source"/"value" field -     * of the property tree, and whose value can be 10e6, 30.72e6, or 200e6. -     * The _system_ref_rate is the input to the clocking system, and -     * what comes out is a disciplined master clock running at the -     * _master_clock_rate. As such, only certain combinations of -     * system reference rates and master clock rates are supported. -     * Additionally, a subset of these will operate in "zero delay" mode. */ - -    enum opmode_t { INVALID, -                    m10M_200M_NOZDEL,      // used for debug purposes only -                    m10M_200M_ZDEL,        // Normal mode -                    m30_72M_184_32M_ZDEL,  // LTE with external ref, aka CPRI Mode -                    m10M_184_32M_NOZDEL,   // LTE with 10 MHz ref -                    m10M_120M_ZDEL };       // NI USRP 120 MHz Clocking - -    /* The default clocking mode is 10MHz reference generating a 200 MHz master -     * clock, in zero-delay mode. */ -    opmode_t clocking_mode = INVALID; - -    if(doubles_are_equal(_system_ref_rate, 10e6)) { -        if(doubles_are_equal(clock_rate, 184.32e6)) { -            /* 10MHz reference, 184.32 MHz master clock out, NOT Zero Delay. */ -            clocking_mode = m10M_184_32M_NOZDEL; -        } else if(doubles_are_equal(clock_rate, 200e6)) { -            /* 10MHz reference, 200 MHz master clock out, Zero Delay */ -            clocking_mode = m10M_200M_ZDEL; -        } else if(doubles_are_equal(clock_rate, 120e6)) { -            /* 10MHz reference, 120 MHz master clock rate, Zero Delay */ -            clocking_mode = m10M_120M_ZDEL; +    void reset_clocks() { +        _lmk04816_regs.RESET = lmk04816_regs_t::RESET_RESET; +        this->write_regs(0); +        _lmk04816_regs.RESET = lmk04816_regs_t::RESET_NO_RESET; +        for (size_t i = 0; i <= 16; ++i) { +            this->write_regs(i);          } -    } else if(doubles_are_equal(_system_ref_rate, 30.72e6)) { -        if(doubles_are_equal(clock_rate, 184.32e6)) { -            /* 30.72MHz reference, 184.32 MHz master clock out, Zero Delay */ -            clocking_mode = m30_72M_184_32M_ZDEL; +        for (size_t i = 24; i <= 31; ++i) { +            this->write_regs(i);          } +        sync_clocks();      } -    if(clocking_mode == INVALID) { -        throw uhd::runtime_error(str(boost::format("A master clock rate of %f cannot be derived from a system reference rate of %f") % clock_rate % _system_ref_rate)); +    void sync_clocks(void) { +        //soft sync: +        //put the sync IO into output mode - FPGA must be input +        //write low, then write high - this triggers a soft sync +        _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_LOW; +        this->write_regs(11); +        _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_HIGH; +        this->write_regs(11);      } -    // For 200 MHz output, the VCO is run at 2400 MHz -    // For the LTE/CPRI rate of 184.32 MHz, the VCO runs at 2580.48 MHz - -    int vco_div = 0; - -    // Note: PLL2 N2 prescaler is enabled for all cases -    //       PLL2 reference doubler is enabled for all cases - -    /* All LMK04816 settings are from the LMK datasheet for our clocking -     * architecture. Please refer to the datasheet for more information. */ -    switch (clocking_mode) { -        case m10M_200M_NOZDEL: -            vco_div = 12; -            _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; +    double get_master_clock_rate(void) { +        return _master_clock_rate; +    } -            // PLL1 - 2 MHz compare frequency -            _lmk04816_regs.PLL1_N_28 = 48; -            _lmk04816_regs.PLL1_R_27 = 5; -            _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; +    double get_sysref_clock_rate(void) { +        return _system_ref_rate; +    } -            // PLL2 - 48 MHz compare frequency -            _lmk04816_regs.PLL2_N_30 = 25; -            _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; -            _lmk04816_regs.PLL2_R_28 = 4; -            _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; +    double get_refout_clock_rate(void) { +        //We support only one reference output rate +        return X300_REF_CLK_OUT_RATE; +    } +    void set_dboard_rate(const x300_clock_which_t which, double rate) { +        boost::uint16_t div = boost::uint16_t(_vco_freq / rate); +        boost::uint16_t *reg = NULL; +        boost::uint8_t addr = 0xFF; + +        // Make sure requested rate is an even divisor of the VCO frequency +        if (not doubles_are_equal(_vco_freq / div, rate)) +            throw uhd::value_error("invalid dboard rate requested"); + +        switch (which) +        { +        case X300_CLOCK_WHICH_DB0_RX: +        case X300_CLOCK_WHICH_DB1_RX: +            reg = &_lmk04816_regs.CLKout2_3_DIV; +            addr = 1;              break; +        case X300_CLOCK_WHICH_DB0_TX: +        case X300_CLOCK_WHICH_DB1_TX: +            reg = &_lmk04816_regs.CLKout4_5_DIV; +            addr = 2; +            break; +        default: +            UHD_THROW_INVALID_CODE_PATH(); +        } -        case m10M_200M_ZDEL: -            vco_div = 12; -            _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; +        if (*reg == div) +            return; -            // PLL1 - 2 MHz compare frequency -            _lmk04816_regs.PLL1_N_28 = 100; -            _lmk04816_regs.PLL1_R_27 = 5; -            _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_1600UA; +        // Since the clock rate on one daughter board cannot be changed without +        // affecting the other daughter board, don't allow it. +        throw uhd::not_implemented_error("x3xx set dboard clock rate does not support changing the clock rate"); -            // PLL2 - 96 MHz compare frequency -            _lmk04816_regs.PLL2_N_30 = 5; -            _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_5; -            _lmk04816_regs.PLL2_R_28 = 2; +        // This is open source code and users may need to enable this function +        // to support other daughterboards.  If so, comment out the line above +        // that throws the error and allow the program to reach the code below. -            if(_hw_rev <= 4) -                _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_1600UA; -            else -                _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_400UA; +        // The LMK04816 datasheet says the register must be written twice if SYNC is enabled +        *reg = div; +        write_regs(addr); +        write_regs(addr); +        sync_clocks(); +    } +    double get_dboard_rate(const x300_clock_which_t which) +    { +        double rate = 0.0; +        switch (which) +        { +        case X300_CLOCK_WHICH_DB0_RX: +        case X300_CLOCK_WHICH_DB1_RX: +            rate = _vco_freq / _lmk04816_regs.CLKout2_3_DIV;              break; +        case X300_CLOCK_WHICH_DB0_TX: +        case X300_CLOCK_WHICH_DB1_TX: +            rate = _vco_freq / _lmk04816_regs.CLKout4_5_DIV; +            break; +        default: +            UHD_THROW_INVALID_CODE_PATH(); +        } +        return rate; +    } -        case m30_72M_184_32M_ZDEL: -            vco_div=14; -            _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; - -            // PLL1 - 2.048 MHz compare frequency -            _lmk04816_regs.PLL1_N_28 = 90; -            _lmk04816_regs.PLL1_R_27 = 15; -            _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; +    std::vector<double> get_dboard_rates(const x300_clock_which_t) +    { +        std::vector<double> rates; +        for (size_t div = size_t(_vco_freq / _master_clock_rate); div <= X300_MAX_CLKOUT_DIV; div++) +            rates.push_back(_vco_freq / div); +        return rates; +    } -            // PLL2 - 7.68 MHz compare frequency -            _lmk04816_regs.PLL2_N_30 = 168; -            _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; -            _lmk04816_regs.PLL2_R_28 = 25; -            _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; +    void enable_dboard_clock(const x300_clock_which_t which, const bool enable) +    { +        switch (which) +        { +        case X300_CLOCK_WHICH_DB0_RX: +            if (enable != (_lmk04816_regs.CLKout2_TYPE == lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP)) +            { +                _lmk04816_regs.CLKout2_TYPE = enable ? lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP : lmk04816_regs_t::CLKOUT2_TYPE_P_DOWN; +                write_regs(6); +            } +            break; +        case X300_CLOCK_WHICH_DB1_RX: +            if (enable != (_lmk04816_regs.CLKout3_TYPE == lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP)) +            { +                _lmk04816_regs.CLKout3_TYPE = enable ? lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP : lmk04816_regs_t::CLKOUT3_TYPE_P_DOWN; +                write_regs(6); +            } +            break; +        case X300_CLOCK_WHICH_DB0_TX: +            if (enable != (_lmk04816_regs.CLKout5_TYPE == lmk04816_regs_t::CLKOUT5_TYPE_LVPECL_700MVPP)) +            { +                _lmk04816_regs.CLKout5_TYPE = enable ? lmk04816_regs_t::CLKOUT5_TYPE_LVPECL_700MVPP : lmk04816_regs_t::CLKOUT5_TYPE_P_DOWN; +                write_regs(7); +            } +            break; +        case X300_CLOCK_WHICH_DB1_TX: +            if (enable != (_lmk04816_regs.CLKout4_TYPE == lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP)) +            { +                _lmk04816_regs.CLKout4_TYPE = enable ? lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP : lmk04816_regs_t::CLKOUT4_TYPE_P_DOWN; +                write_regs(7); +            } +            break; +        default: +            UHD_THROW_INVALID_CODE_PATH(); +        } +    } -            _lmk04816_regs.PLL2_R3_LF = lmk04816_regs_t::PLL2_R3_LF_1KILO_OHM; -            _lmk04816_regs.PLL2_C3_LF = lmk04816_regs_t::PLL2_C3_LF_39PF; +    void set_ref_out(const bool enable) { +        // TODO  Implement divider configuration to allow for configurable output +        // rates +        if (enable) +            _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_LVDS; +        else +            _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_P_DOWN; +        this->write_regs(8); +    } -            _lmk04816_regs.PLL2_R4_LF = lmk04816_regs_t::PLL2_R4_LF_1KILO_OHM; -            _lmk04816_regs.PLL2_C4_LF = lmk04816_regs_t::PLL2_C4_LF_34PF; +    void write_regs(boost::uint8_t addr) { +        boost::uint32_t data = _lmk04816_regs.get_reg(addr); +        _spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32); +    } -            break; -        case m10M_184_32M_NOZDEL: -            vco_div=14; -            _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; +private: -            // PLL1 - 2 MHz compare frequency -            _lmk04816_regs.PLL1_N_28 = 48; -            _lmk04816_regs.PLL1_R_27 = 5; -            _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; +    void init() { +        /* The X3xx has two primary rates. The first is the +         * _system_ref_rate, which is sourced from the "clock_source"/"value" field +         * of the property tree, and whose value can be 10e6, 30.72e6, or 200e6. +         * The _system_ref_rate is the input to the clocking system, and +         * what comes out is a disciplined master clock running at the +         * _master_clock_rate. As such, only certain combinations of +         * system reference rates and master clock rates are supported. +         * Additionally, a subset of these will operate in "zero delay" mode. */ + +        enum opmode_t { INVALID, +                        m10M_200M_NOZDEL,      // used for debug purposes only +                        m10M_200M_ZDEL,        // Normal mode +                        m30_72M_184_32M_ZDEL,  // LTE with external ref, aka CPRI Mode +                        m10M_184_32M_NOZDEL,   // LTE with 10 MHz ref +                        m10M_120M_ZDEL };       // NI USRP 120 MHz Clocking + +        /* The default clocking mode is 10MHz reference generating a 200 MHz master +         * clock, in zero-delay mode. */ +        opmode_t clocking_mode = INVALID; + +        if(doubles_are_equal(_system_ref_rate, 10e6)) { +            if(doubles_are_equal(_master_clock_rate, 184.32e6)) { +                /* 10MHz reference, 184.32 MHz master clock out, NOT Zero Delay. */ +                clocking_mode = m10M_184_32M_NOZDEL; +            } else if(doubles_are_equal(_master_clock_rate, 200e6)) { +                /* 10MHz reference, 200 MHz master clock out, Zero Delay */ +                clocking_mode = m10M_200M_ZDEL; +            } else if(doubles_are_equal(_master_clock_rate, 120e6)) { +                /* 10MHz reference, 120 MHz master clock rate, Zero Delay */ +                clocking_mode = m10M_120M_ZDEL; +            } +        } else if(doubles_are_equal(_system_ref_rate, 30.72e6)) { +            if(doubles_are_equal(_master_clock_rate, 184.32e6)) { +                /* 30.72MHz reference, 184.32 MHz master clock out, Zero Delay */ +                clocking_mode = m30_72M_184_32M_ZDEL; +            } +        } -            // PLL2 - 7.68 MHz compare frequency -            _lmk04816_regs.PLL2_N_30 = 168; -            _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; -            _lmk04816_regs.PLL2_R_28 = 25; -            _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; +        if(clocking_mode == INVALID) { +            throw uhd::runtime_error(str(boost::format("A master clock rate of %f cannot be derived from a system reference rate of %f") % _master_clock_rate % _system_ref_rate)); +        } -            _lmk04816_regs.PLL2_R3_LF = lmk04816_regs_t::PLL2_R3_LF_4KILO_OHM; -            _lmk04816_regs.PLL2_C3_LF = lmk04816_regs_t::PLL2_C3_LF_39PF; +        // For 200 MHz output, the VCO is run at 2400 MHz +        // For the LTE/CPRI rate of 184.32 MHz, the VCO runs at 2580.48 MHz -            _lmk04816_regs.PLL2_R4_LF = lmk04816_regs_t::PLL2_R4_LF_1KILO_OHM; -            _lmk04816_regs.PLL2_C4_LF = lmk04816_regs_t::PLL2_C4_LF_71PF; +        // Note: PLL2 N2 prescaler is enabled for all cases +        //       PLL2 reference doubler is enabled for all cases -            break; +        /* All LMK04816 settings are from the LMK datasheet for our clocking +         * architecture. Please refer to the datasheet for more information. */ +        switch (clocking_mode) { +            case m10M_200M_NOZDEL: +                _vco_freq = 2400e6; +                _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; -        case m10M_120M_ZDEL: -            vco_div = 20; -            _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; +                // PLL1 - 2 MHz compare frequency +                _lmk04816_regs.PLL1_N_28 = 48; +                _lmk04816_regs.PLL1_R_27 = 5; +                _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; -            // PLL1 - 2 MHz compare frequency -            _lmk04816_regs.PLL1_N_28 = 60; -            _lmk04816_regs.PLL1_R_27 = 5; -            _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; +                // PLL2 - 48 MHz compare frequency +                _lmk04816_regs.PLL2_N_30 = 25; +                _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; +                _lmk04816_regs.PLL2_R_28 = 4; +                _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; -            // PLL2 - 96 MHz compare frequency -            _lmk04816_regs.PLL2_N_30 = 5; -            _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_5; -            _lmk04816_regs.PLL2_R_28 = 2; +                break; -            if(_hw_rev <= 4) -                _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_1600UA; -            else -                _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_400UA; +            case m10M_200M_ZDEL: +                _vco_freq = 2400e6; +                _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; -            break; +                // PLL1 - 2 MHz compare frequency +                _lmk04816_regs.PLL1_N_28 = 5; +                _lmk04816_regs.PLL1_R_27 = 5; +                _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_1600UA; + +                // PLL2 - 96 MHz compare frequency +                _lmk04816_regs.PLL2_N_30 = 5; +                _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_5; +                _lmk04816_regs.PLL2_R_28 = 2; + +                if(_hw_rev <= 4) +                    _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_1600UA; +                else +                    _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_400UA; + +                break; + +            case m30_72M_184_32M_ZDEL: +                _vco_freq = 2580.48e6; +                _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; + +                // PLL1 - 2.048 MHz compare frequency +                _lmk04816_regs.PLL1_N_28 = 15; +                _lmk04816_regs.PLL1_R_27 = 15; +                _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + +                // PLL2 - 7.68 MHz compare frequency +                _lmk04816_regs.PLL2_N_30 = 168; +                _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; +                _lmk04816_regs.PLL2_R_28 = 25; +                _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; + +                _lmk04816_regs.PLL2_R3_LF = lmk04816_regs_t::PLL2_R3_LF_1KILO_OHM; +                _lmk04816_regs.PLL2_C3_LF = lmk04816_regs_t::PLL2_C3_LF_39PF; + +                _lmk04816_regs.PLL2_R4_LF = lmk04816_regs_t::PLL2_R4_LF_1KILO_OHM; +                _lmk04816_regs.PLL2_C4_LF = lmk04816_regs_t::PLL2_C4_LF_34PF; + +                break; + +            case m10M_184_32M_NOZDEL: +                _vco_freq = 2580.48e6; +                _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT; + +                // PLL1 - 2 MHz compare frequency +                _lmk04816_regs.PLL1_N_28 = 48; +                _lmk04816_regs.PLL1_R_27 = 5; +                _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + +                // PLL2 - 7.68 MHz compare frequency +                _lmk04816_regs.PLL2_N_30 = 168; +                _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_2A; +                _lmk04816_regs.PLL2_R_28 = 25; +                _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_3200UA; + +                _lmk04816_regs.PLL2_R3_LF = lmk04816_regs_t::PLL2_R3_LF_4KILO_OHM; +                _lmk04816_regs.PLL2_C3_LF = lmk04816_regs_t::PLL2_C3_LF_39PF; + +                _lmk04816_regs.PLL2_R4_LF = lmk04816_regs_t::PLL2_R4_LF_1KILO_OHM; +                _lmk04816_regs.PLL2_C4_LF = lmk04816_regs_t::PLL2_C4_LF_71PF; + +                break; + +            case m10M_120M_ZDEL: +                _vco_freq = 2400e6; +                _lmk04816_regs.MODE = lmk04816_regs_t::MODE_DUAL_INT_ZER_DELAY; + +                // PLL1 - 2 MHz compare frequency +                _lmk04816_regs.PLL1_N_28 = 5; +                _lmk04816_regs.PLL1_R_27 = 5; +                _lmk04816_regs.PLL1_CP_GAIN_27 = lmk04816_regs_t::PLL1_CP_GAIN_27_100UA; + +                // PLL2 - 96 MHz compare frequency +                _lmk04816_regs.PLL2_N_30 = 5; +                _lmk04816_regs.PLL2_P_30 = lmk04816_regs_t::PLL2_P_30_DIV_5; +                _lmk04816_regs.PLL2_R_28 = 2; + +                if(_hw_rev <= 4) +                    _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_1600UA; +                else +                    _lmk04816_regs.PLL2_CP_GAIN_26 = lmk04816_regs_t::PLL2_CP_GAIN_26_400UA; + +                break; + +            default: +                UHD_THROW_INVALID_CODE_PATH(); +                break; +        }; + +        boost::uint16_t master_clock_div = static_cast<boost::uint16_t>( +            std::ceil(_vco_freq / _master_clock_rate)); + +        boost::uint16_t dboard_div = static_cast<boost::uint16_t>( +            std::ceil(_vco_freq / X300_DEFAULT_DBOARD_CLK_RATE)); + +        /* Reset the LMK clock controller. */ +        _lmk04816_regs.RESET = lmk04816_regs_t::RESET_RESET; +        this->write_regs(0); +        _lmk04816_regs.RESET = lmk04816_regs_t::RESET_NO_RESET; +        this->write_regs(0); + +        /* Initial power-up */ +        _lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP; +        this->write_regs(0); +        _lmk04816_regs.CLKout0_1_DIV = master_clock_div; +        _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_EV_X; +        _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH; +        _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH; +        this->write_regs(0); + +        // Register 1 +        _lmk04816_regs.CLKout2_3_PD = lmk04816_regs_t::CLKOUT2_3_PD_POWER_UP; +        _lmk04816_regs.CLKout2_3_DIV = dboard_div; +        // Register 2 +        _lmk04816_regs.CLKout4_5_PD = lmk04816_regs_t::CLKOUT4_5_PD_POWER_UP; +        _lmk04816_regs.CLKout4_5_DIV = dboard_div; +        // Register 3 +        _lmk04816_regs.CLKout6_7_DIV = master_clock_div; +        _lmk04816_regs.CLKout6_7_OSCin_Sel = lmk04816_regs_t::CLKOUT6_7_OSCIN_SEL_VCO; +        // Register 4 +        _lmk04816_regs.CLKout8_9_DIV = master_clock_div; +        // Register 5 +        _lmk04816_regs.CLKout10_11_PD = lmk04816_regs_t::CLKOUT10_11_PD_NORMAL; +        _lmk04816_regs.CLKout10_11_DIV = +            static_cast<boost::uint16_t>(std::ceil(_vco_freq / _system_ref_rate)); + +        // Register 6 +        _lmk04816_regs.CLKout0_TYPE = lmk04816_regs_t::CLKOUT0_TYPE_LVDS; //FPGA +        _lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS +        _lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX +        _lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX +        // Delay the FPGA_CLK by 900ps to ensure a safe ADC_SSCLK -> RADIO_CLK crossing. +        // If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC +        // because the data interface clock is generated from FPGA_CLK. +        // NOTE: This delay value was verified at room temperature only. +        _lmk04816_regs.CLKout0_1_ADLY = 0x10; + +        // Register 7 +        _lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX +        _lmk04816_regs.CLKout5_TYPE = lmk04816_regs_t::CLKOUT5_TYPE_LVPECL_700MVPP; //DB_0_TX +        _lmk04816_regs.CLKout6_TYPE = lmk04816_regs_t::CLKOUT6_TYPE_LVPECL_700MVPP; //DB0_DAC +        _lmk04816_regs.CLKout7_TYPE = lmk04816_regs_t::CLKOUT7_TYPE_LVPECL_700MVPP; //DB1_DAC +        _lmk04816_regs.CLKout8_TYPE = lmk04816_regs_t::CLKOUT8_TYPE_LVPECL_700MVPP; //DB0_ADC +        _lmk04816_regs.CLKout6_7_ADLY = _lmk04816_regs.CLKout0_1_ADLY; + +        // Register 8 +        _lmk04816_regs.CLKout9_TYPE = lmk04816_regs_t::CLKOUT9_TYPE_LVPECL_700MVPP; //DB1_ADC +        _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_LVDS; //REF_CLKOUT +        _lmk04816_regs.CLKout11_TYPE = lmk04816_regs_t::CLKOUT11_TYPE_P_DOWN; //Debug header, use LVPECL + + +        // Register 10 +        _lmk04816_regs.EN_OSCout0 = lmk04816_regs_t::EN_OSCOUT0_DISABLED; //Debug header +        _lmk04816_regs.FEEDBACK_MUX = 5; //use output 10 (REF OUT) for feedback +        _lmk04816_regs.EN_FEEDBACK_MUX = lmk04816_regs_t::EN_FEEDBACK_MUX_ENABLED; + +        // Register 11 +        // MODE set in individual cases above +        _lmk04816_regs.SYNC_QUAL = lmk04816_regs_t::SYNC_QUAL_FB_MUX; +        _lmk04816_regs.EN_SYNC = lmk04816_regs_t::EN_SYNC_ENABLE; +        _lmk04816_regs.NO_SYNC_CLKout0_1 = lmk04816_regs_t::NO_SYNC_CLKOUT0_1_CLOCK_XY_SYNC; +        _lmk04816_regs.NO_SYNC_CLKout2_3 = lmk04816_regs_t::NO_SYNC_CLKOUT2_3_CLOCK_XY_SYNC; +        _lmk04816_regs.NO_SYNC_CLKout4_5 = lmk04816_regs_t::NO_SYNC_CLKOUT4_5_CLOCK_XY_SYNC; +        _lmk04816_regs.NO_SYNC_CLKout6_7 = lmk04816_regs_t::NO_SYNC_CLKOUT6_7_CLOCK_XY_SYNC; +        _lmk04816_regs.NO_SYNC_CLKout8_9 = lmk04816_regs_t::NO_SYNC_CLKOUT8_9_CLOCK_XY_SYNC; +        _lmk04816_regs.NO_SYNC_CLKout10_11 = lmk04816_regs_t::NO_SYNC_CLKOUT10_11_CLOCK_XY_SYNC; +        _lmk04816_regs.SYNC_TYPE = lmk04816_regs_t::SYNC_TYPE_INPUT; + +        // Register 12 +        _lmk04816_regs.LD_MUX = lmk04816_regs_t::LD_MUX_BOTH; + +        /* Input Clock Configurations */ +        // Register 13 +        _lmk04816_regs.EN_CLKin0 = lmk04816_regs_t::EN_CLKIN0_NO_VALID_USE;  // This is not connected +        _lmk04816_regs.EN_CLKin2 = lmk04816_regs_t::EN_CLKIN2_NO_VALID_USE;  // Used only for CPRI +        _lmk04816_regs.Status_CLKin1_MUX = lmk04816_regs_t::STATUS_CLKIN1_MUX_UWIRE_RB; +        _lmk04816_regs.CLKin_Select_MODE = lmk04816_regs_t::CLKIN_SELECT_MODE_CLKIN1_MAN; +        _lmk04816_regs.HOLDOVER_MUX = lmk04816_regs_t::HOLDOVER_MUX_PLL1_R; +        // Register 14 +        _lmk04816_regs.Status_CLKin1_TYPE = lmk04816_regs_t::STATUS_CLKIN1_TYPE_OUT_PUSH_PULL; +        _lmk04816_regs.Status_CLKin0_TYPE = lmk04816_regs_t::STATUS_CLKIN0_TYPE_OUT_PUSH_PULL; + +        // Register 26 +        // PLL2_CP_GAIN_26 set above in individual cases +        _lmk04816_regs.PLL2_CP_POL_26 = lmk04816_regs_t::PLL2_CP_POL_26_NEG_SLOPE; +        _lmk04816_regs.EN_PLL2_REF_2X = lmk04816_regs_t::EN_PLL2_REF_2X_DOUBLED_FREQ_REF; + +        // Register 27 +        // PLL1_CP_GAIN_27 set in individual cases above +        // PLL1_R_27 set in the individual cases above + +        // Register 28 +        // PLL1_N_28 and PLL2_R_28 are set in the individual cases above + +        // Register 29 +        _lmk04816_regs.PLL2_N_CAL_29 = _lmk04816_regs.PLL2_N_30;  // N_CAL should always match N +        _lmk04816_regs.OSCin_FREQ_29 = lmk04816_regs_t::OSCIN_FREQ_29_63_TO_127MHZ; + +        // Register 30 +        // PLL2_P_30 set in individual cases above +        // PLL2_N_30 set in individual cases above + +        /* Write the configuration values into the LMK */ +        for (size_t i = 1; i <= 16; ++i) { +            this->write_regs(i); +        } +        for (size_t i = 24; i <= 31; ++i) { +            this->write_regs(i); +        } -        default: -            UHD_THROW_INVALID_CODE_PATH(); -            break; -    }; - -    /* Reset the LMK clock controller. */ -    _lmk04816_regs.RESET = lmk04816_regs_t::RESET_RESET; -    this->write_regs(0); -    _lmk04816_regs.RESET = lmk04816_regs_t::RESET_NO_RESET; -    this->write_regs(0); - -    /* Initial power-up */ -    _lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP; -    this->write_regs(0); -    _lmk04816_regs.CLKout0_1_DIV = vco_div; -    _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_EV_X; -    _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH; -    _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH; -    this->write_regs(0); - -    // Register 1 -    _lmk04816_regs.CLKout2_3_PD = lmk04816_regs_t::CLKOUT2_3_PD_POWER_UP; -    _lmk04816_regs.CLKout2_3_DIV = vco_div; -    // Register 2 -    _lmk04816_regs.CLKout4_5_PD = lmk04816_regs_t::CLKOUT4_5_PD_POWER_UP; -    _lmk04816_regs.CLKout4_5_DIV = vco_div; -    // Register 3 -    _lmk04816_regs.CLKout6_7_DIV = vco_div; -    _lmk04816_regs.CLKout6_7_OSCin_Sel = lmk04816_regs_t::CLKOUT6_7_OSCIN_SEL_VCO; -    // Register 4 -    _lmk04816_regs.CLKout8_9_DIV = vco_div; -    // Register 5 -    _lmk04816_regs.CLKout10_11_PD = lmk04816_regs_t::CLKOUT10_11_PD_NORMAL; -    _lmk04816_regs.CLKout10_11_DIV = vco_div * static_cast<int>(clock_rate/X300_REF_CLK_OUT_RATE); - -    // Register 6 -    _lmk04816_regs.CLKout0_TYPE = lmk04816_regs_t::CLKOUT0_TYPE_LVDS; //FPGA -    _lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS -    _lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX -    _lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX -    // Delay the FPGA_CLK by 900ps to ensure a safe ADC_SSCLK -> RADIO_CLK crossing. -    // If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC -    // because the data interface clock is generated from FPGA_CLK. -    // This delay may need to vary due to temperature.  Tested and verified at room temperature only. -    _lmk04816_regs.CLKout0_1_ADLY = 0x10; -    _lmk04816_regs.CLKout6_7_ADLY = _lmk04816_regs.CLKout0_1_ADLY; - -    // Register 7 -    _lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX -    _lmk04816_regs.CLKout5_TYPE = lmk04816_regs_t::CLKOUT5_TYPE_LVPECL_700MVPP; //DB_0_TX -    _lmk04816_regs.CLKout6_TYPE = lmk04816_regs_t::CLKOUT6_TYPE_LVPECL_700MVPP; //DB0_DAC -    _lmk04816_regs.CLKout7_TYPE = lmk04816_regs_t::CLKOUT7_TYPE_LVPECL_700MVPP; //DB1_DAC -    _lmk04816_regs.CLKout8_TYPE = lmk04816_regs_t::CLKOUT8_TYPE_LVPECL_700MVPP; //DB0_ADC - -    // Register 8 -    _lmk04816_regs.CLKout9_TYPE = lmk04816_regs_t::CLKOUT9_TYPE_LVPECL_700MVPP; //DB1_ADC -    _lmk04816_regs.CLKout10_TYPE = lmk04816_regs_t::CLKOUT10_TYPE_LVDS; //REF_CLKOUT -    _lmk04816_regs.CLKout11_TYPE = lmk04816_regs_t::CLKOUT11_TYPE_P_DOWN; //Debug header, use LVPECL - - -    // Register 10 -    _lmk04816_regs.EN_OSCout0 = lmk04816_regs_t::EN_OSCOUT0_DISABLED; //Debug header -    _lmk04816_regs.FEEDBACK_MUX = 0; //use output 0 (FPGA clock) for feedback -    _lmk04816_regs.EN_FEEDBACK_MUX = lmk04816_regs_t::EN_FEEDBACK_MUX_ENABLED; - -    // Register 11 -    // MODE set in individual cases above -    _lmk04816_regs.SYNC_QUAL = lmk04816_regs_t::SYNC_QUAL_FB_MUX; -    _lmk04816_regs.EN_SYNC = lmk04816_regs_t::EN_SYNC_ENABLE; -    _lmk04816_regs.NO_SYNC_CLKout0_1 = lmk04816_regs_t::NO_SYNC_CLKOUT0_1_CLOCK_XY_SYNC; -    _lmk04816_regs.NO_SYNC_CLKout2_3 = lmk04816_regs_t::NO_SYNC_CLKOUT2_3_CLOCK_XY_SYNC; -    _lmk04816_regs.NO_SYNC_CLKout4_5 = lmk04816_regs_t::NO_SYNC_CLKOUT4_5_CLOCK_XY_SYNC; -    _lmk04816_regs.NO_SYNC_CLKout6_7 = lmk04816_regs_t::NO_SYNC_CLKOUT6_7_CLOCK_XY_SYNC; -    _lmk04816_regs.NO_SYNC_CLKout8_9 = lmk04816_regs_t::NO_SYNC_CLKOUT8_9_CLOCK_XY_SYNC; -    _lmk04816_regs.NO_SYNC_CLKout10_11 = lmk04816_regs_t::NO_SYNC_CLKOUT10_11_CLOCK_XY_SYNC; -    _lmk04816_regs.SYNC_EN_AUTO = lmk04816_regs_t::SYNC_EN_AUTO_SYNC_INT_GEN; -    _lmk04816_regs.SYNC_POL_INV = lmk04816_regs_t::SYNC_POL_INV_SYNC_LOW; -    _lmk04816_regs.SYNC_TYPE = lmk04816_regs_t::SYNC_TYPE_INPUT; - -    // Register 12 -    _lmk04816_regs.LD_MUX = lmk04816_regs_t::LD_MUX_BOTH; - -    /* Input Clock Configurations */ -    // Register 13 -    _lmk04816_regs.EN_CLKin0 = lmk04816_regs_t::EN_CLKIN0_NO_VALID_USE;  // This is not connected -    _lmk04816_regs.EN_CLKin2 = lmk04816_regs_t::EN_CLKIN2_NO_VALID_USE;  // Used only for CPRI -    _lmk04816_regs.Status_CLKin1_MUX = lmk04816_regs_t::STATUS_CLKIN1_MUX_UWIRE_RB; -    _lmk04816_regs.CLKin_Select_MODE = lmk04816_regs_t::CLKIN_SELECT_MODE_CLKIN1_MAN; -    _lmk04816_regs.HOLDOVER_MUX = lmk04816_regs_t::HOLDOVER_MUX_PLL1_R; -    // Register 14 -    _lmk04816_regs.Status_CLKin1_TYPE = lmk04816_regs_t::STATUS_CLKIN1_TYPE_OUT_PUSH_PULL; -    _lmk04816_regs.Status_CLKin0_TYPE = lmk04816_regs_t::STATUS_CLKIN0_TYPE_OUT_PUSH_PULL; - -    // Register 26 -    // PLL2_CP_GAIN_26 set above in individual cases -    _lmk04816_regs.PLL2_CP_POL_26 = lmk04816_regs_t::PLL2_CP_POL_26_NEG_SLOPE; -    _lmk04816_regs.EN_PLL2_REF_2X = lmk04816_regs_t::EN_PLL2_REF_2X_DOUBLED_FREQ_REF; - -    // Register 27 -    // PLL1_CP_GAIN_27 set in individual cases above -    // PLL1_R_27 set in the individual cases above - -    // Register 28 -    // PLL1_N_28 and PLL2_R_28 are set in the individual cases above - -    // Register 29 -    _lmk04816_regs.PLL2_N_CAL_29 = _lmk04816_regs.PLL2_N_30;  // N_CAL should always match N -    _lmk04816_regs.OSCin_FREQ_29 = lmk04816_regs_t::OSCIN_FREQ_29_63_TO_127MHZ; - -    // Register 30 -    // PLL2_P_30 set in individual cases above -    // PLL2_N_30 set in individual cases above - -    /* Write the configuration values into the LMK */ -    for (size_t i = 1; i <= 16; ++i) { -        this->write_regs(i); -    } -    for (size_t i = 24; i <= 31; ++i) { -        this->write_regs(i); +        this->sync_clocks();      } -    this->sync_clocks(); -} - -UHD_INLINE bool doubles_are_equal(double a, double b) { -    return  (std::fabs(a - b) < std::numeric_limits<double>::epsilon()); -} +    UHD_INLINE bool doubles_are_equal(double a, double b) { +        return  (std::fabs(a - b) < std::numeric_limits<double>::epsilon()); +    } -const spi_iface::sptr _spiface; -const size_t _slaveno; -const size_t _hw_rev; -const double _master_clock_rate; -const double _system_ref_rate; -lmk04816_regs_t _lmk04816_regs; +    const spi_iface::sptr _spiface; +    const size_t _slaveno; +    const size_t _hw_rev; +    const double _master_clock_rate; +    const double _system_ref_rate; +    lmk04816_regs_t _lmk04816_regs; +    double _vco_freq;  };  x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface, diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp index 40b62b09a..9c08aa356 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.hpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp @@ -71,11 +71,24 @@ public:       */      virtual void set_dboard_rate(const x300_clock_which_t which, double rate) = 0; +    /*! Get the clock rate on the given daughterboard clock. +     * \throw exception when rate invalid +     * \return the clock rate in Hz +     */ +    virtual double get_dboard_rate(const x300_clock_which_t which) = 0; +      /*! Get a list of possible daughterboard clock rates.       * \return a list of clock rates in Hz       */      virtual std::vector<double> get_dboard_rates(const x300_clock_which_t which) = 0; +    /*! Enable or disable daughterboard clock. +     * \param which which clock +     * \param enable true=enable, false=disable +     * \return a list of clock rates in Hz +     */ +    virtual void enable_dboard_clock(const x300_clock_which_t which, const bool enable) = 0; +      /*! Turn the reference output on/off       * \param true = on, false = off       */ diff --git a/host/lib/usrp/x300/x300_dboard_iface.cpp b/host/lib/usrp/x300/x300_dboard_iface.cpp index c286e805a..502630109 100644 --- a/host/lib/usrp/x300/x300_dboard_iface.cpp +++ b/host/lib/usrp/x300/x300_dboard_iface.cpp @@ -111,12 +111,12 @@ x300_dboard_iface::x300_dboard_iface(const x300_dboard_iface_config_t &config):          this->_write_aux_dac(unit);      } +    _clock_rates[UNIT_RX] = _config.clock->get_dboard_rate(_config.which_rx_clk); +    _clock_rates[UNIT_TX] = _config.clock->get_dboard_rate(_config.which_tx_clk); +      this->set_clock_enabled(UNIT_RX, false);      this->set_clock_enabled(UNIT_TX, false); -    this->set_clock_rate(UNIT_RX, _config.clock->get_master_clock_rate()); -    this->set_clock_rate(UNIT_TX, _config.clock->get_master_clock_rate()); -      //some test code      /* @@ -153,16 +153,20 @@ x300_dboard_iface::~x300_dboard_iface(void)   **********************************************************************/  void x300_dboard_iface::set_clock_rate(unit_t unit, double rate)  { -    _clock_rates[unit] = rate; //set to shadow +    // Just return if the requested rate is already set +    if (std::fabs(_clock_rates[unit] - rate) < std::numeric_limits<double>::epsilon()) +        return; +      switch(unit)      {          case UNIT_RX:              _config.clock->set_dboard_rate(_config.which_rx_clk, rate); -            return; +            break;          case UNIT_TX:              _config.clock->set_dboard_rate(_config.which_tx_clk, rate); -            return; +            break;      } +    _clock_rates[unit] = rate; //set to shadow  }  double x300_dboard_iface::get_clock_rate(unit_t unit) @@ -183,9 +187,17 @@ std::vector<double> x300_dboard_iface::get_clock_rates(unit_t unit)      }  } -void x300_dboard_iface::set_clock_enabled(UHD_UNUSED(unit_t unit), UHD_UNUSED(bool enb)) +void x300_dboard_iface::set_clock_enabled(unit_t unit, bool enb)  { -    // TODO Variable DBoard clock control needs to be implemented for X300. +    switch(unit) +    { +        case UNIT_RX: +            return _config.clock->enable_dboard_clock(_config.which_rx_clk, enb); +        case UNIT_TX: +            return _config.clock->enable_dboard_clock(_config.which_tx_clk, enb); +        default: +            UHD_THROW_INVALID_CODE_PATH(); +    }  }  double x300_dboard_iface::get_codec_rate(unit_t) diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 9042ad2ca..c27133745 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -70,13 +70,13 @@ static const size_t X300_PCIE_RX_DATA_FRAME_SIZE    = 8184;     //bytes  static const size_t X300_PCIE_TX_DATA_FRAME_SIZE    = 8192;     //bytes  static const size_t X300_PCIE_DATA_NUM_FRAMES       = 2048;  static const size_t X300_PCIE_MSG_FRAME_SIZE        = 256;      //bytes -static const size_t X300_PCIE_MSG_NUM_FRAMES        = 32; +static const size_t X300_PCIE_MSG_NUM_FRAMES        = 64;  static const size_t X300_10GE_DATA_FRAME_MAX_SIZE   = 8000;     //bytes  static const size_t X300_1GE_DATA_FRAME_MAX_SIZE    = 1472;     //bytes  static const size_t X300_ETH_MSG_FRAME_SIZE         = uhd::transport::udp_simple::mtu;  //bytes -static const size_t X300_ETH_MSG_NUM_FRAMES         = 32; +static const size_t X300_ETH_MSG_NUM_FRAMES         = 64;  static const size_t X300_ETH_DATA_NUM_FRAMES        = 32;  static const double X300_DEFAULT_SYSREF_RATE        = 10e6; | 
