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) |