diff options
| author | michael-west <michael.west@ettus.com> | 2015-04-10 18:06:54 -0700 | 
|---|---|---|
| committer | michael-west <michael.west@ettus.com> | 2015-04-10 18:32:59 -0700 | 
| commit | 7a3fb4a527d9bc5861093cf8caac75e5a5195313 (patch) | |
| tree | 5ac565a370b8d21f7362e54913065ee5622a5beb /host/lib | |
| parent | e5855d2b26d65e02172ce4d238da5c816dbee288 (diff) | |
| download | uhd-7a3fb4a527d9bc5861093cf8caac75e5a5195313.tar.gz uhd-7a3fb4a527d9bc5861093cf8caac75e5a5195313.tar.bz2 uhd-7a3fb4a527d9bc5861093cf8caac75e5a5195313.zip | |
X300: Change dboard clock rate to 50 MHz
Diffstat (limited to 'host/lib')
| -rw-r--r-- | host/lib/usrp/dboard/db_wbx_version4.cpp | 27 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_clock_ctrl.cpp | 757 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_clock_ctrl.hpp | 13 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_dboard_iface.cpp | 28 | 
4 files changed, 480 insertions, 345 deletions
| 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/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index b59247d53..9bea4a4b4 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,370 +38,475 @@ 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; +        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 +        // Analog delay of 900ps to synchronize the radio clock with the source synchronous ADC clocks. +        // This delay may need to vary due to temperature.  Tested and 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 + +        // 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; -    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 -    // Analog delay of 900ps to synchronize the radio clock with the source synchronous ADC clocks. -    // This delay may need to vary due to temperature.  Tested and 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 - -    // 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) | 
