aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
authormichael-west <michael.west@ettus.com>2015-04-10 18:06:54 -0700
committermichael-west <michael.west@ettus.com>2015-04-10 18:32:59 -0700
commit7a3fb4a527d9bc5861093cf8caac75e5a5195313 (patch)
tree5ac565a370b8d21f7362e54913065ee5622a5beb /host/lib
parente5855d2b26d65e02172ce4d238da5c816dbee288 (diff)
downloaduhd-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.cpp27
-rw-r--r--host/lib/usrp/x300/x300_clock_ctrl.cpp757
-rw-r--r--host/lib/usrp/x300/x300_clock_ctrl.hpp13
-rw-r--r--host/lib/usrp/x300/x300_dboard_iface.cpp28
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)