aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/docs/uhd_find_devices.12
-rw-r--r--host/docs/uhd_usrp_probe.13
-rw-r--r--host/docs/usrp_b200.dox36
-rw-r--r--host/lib/usrp/common/max287x.hpp799
-rw-r--r--host/lib/usrp/dboard/db_cbx.cpp194
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.hpp7
-rw-r--r--host/lib/usrp/dboard/db_ubx.cpp811
-rw-r--r--host/lib/usrp/dboard/db_wbx_version4.cpp27
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.cpp2
-rw-r--r--host/lib/usrp/usrp2/fw_common.h2
-rw-r--r--host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp2
-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
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp4
15 files changed, 1590 insertions, 1097 deletions
diff --git a/host/docs/uhd_find_devices.1 b/host/docs/uhd_find_devices.1
index dfd5c0751..a29793872 100644
--- a/host/docs/uhd_find_devices.1
+++ b/host/docs/uhd_find_devices.1
@@ -43,6 +43,8 @@ type=b200 | USRP B200, USRP B210
type=e100 | USRP E100, USRP E110
+type=e3x0 | USRP E310
+
type=x300 | USRP X300, USRP X310
type=octoclock | OctoClock
diff --git a/host/docs/uhd_usrp_probe.1 b/host/docs/uhd_usrp_probe.1
index 178027aea..b5a48fc15 100644
--- a/host/docs/uhd_usrp_probe.1
+++ b/host/docs/uhd_usrp_probe.1
@@ -51,8 +51,11 @@ type=b200 | USRP B200, USRP B210
type=e100 | USRP E100, USRP E110
+type=e3x0 | USRP E310
+
type=x300 | USRP X300, USRP X310
+
.SS Identifying by serial number
All USRP devices are given a unique serial number, which can be used to identify a device as follows:
diff --git a/host/docs/usrp_b200.dox b/host/docs/usrp_b200.dox
index 9d3550b98..1da7f2aee 100644
--- a/host/docs/usrp_b200.dox
+++ b/host/docs/usrp_b200.dox
@@ -9,10 +9,12 @@
- External PPS reference input
- External 10 MHz reference input
- Configurable clock rate
+ - Variable analog bandwidth (200 kHz - 56 MHz)
- Internal GPSDO option (see \subpage page_gpsdo_b2x0 for details)
- B210 Only:
- MICTOR Debug Connector
- JTAG Connector
+ - Revision 6 with GPIO header
- FPGA Capabilities:
- Timed commands in FPGA
- Timed sampling in FPGA
@@ -90,6 +92,24 @@ frontends have 73 dB of available gain; and the transmit frontends have
it is recommended that users consider using at least half of the
available gain to get reasonable dynamic range.
+\subsection b200_fe_bw Frontend bandwidth
+
+The analog frontend has a seamlessly adjustable bandwidth of 200 kHz to 56 MHz.
+
+Generally, when requesting any possible master clock rate, UHD will
+automatically configure the analog filters to avoid any aliasing (RX) or
+out-of-band emissions whilst letting through the cleanest possible signal.
+
+If you, however, happen to have a very strong interferer within half the master
+clock rate of your RX LO frequency, you might want to reduce this analog
+bandwidth. You can do so by calling
+uhd::usrp::multi_usrp::set_rx_bandwidth(bw).
+
+The property to control the analog RX bandwidth is `bandwidth/value`.
+
+UHD will not allow you to set bandwidths larger than your current master clock
+rate.
+
\section Hardware Reference
\subsection LED Indicators
@@ -181,11 +201,17 @@ Below is a table showing the external connections and respective power informati
Below is a table showing the on-board connectors and switches:
-Component ID | Description | Details
---------------|----------------------------|----------------------------------------------------------
- J502* | Mictor Connector | Interface to FPGA for I/O and inspection.
- J503* | JTAG Header | Interface to FPGA for programming and debugging.
- S700 | FX3 Hard Reset Switch | -
+Component ID | Description | Details
+------------------------|----------------------------|---------------------------------------------------
+ J502<sup>1</sup> | Mictor Connector | Interface to FPGA for I/O and inspection.
+ J503<sup>1</sup> | JTAG Header | Interface to FPGA for programming and debugging.
+ J504<sup>2</sup> | GPIO Header | Header running to the FPGA for GPIO purposes.
+ S700 | FX3 Hard Reset Switch | Resets the USB controller / System reset
+ U100 | GPSDO socket | Interface to GPS disciplined reference oscillator
+
+<sup>1</sup> Only on the B210
+
+<sup>2</sup> Only since rev. 6 (green board)
*/
diff --git a/host/lib/usrp/common/max287x.hpp b/host/lib/usrp/common/max287x.hpp
new file mode 100644
index 000000000..ee1dbb946
--- /dev/null
+++ b/host/lib/usrp/common/max287x.hpp
@@ -0,0 +1,799 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef MAX287X_HPP_INCLUDED
+#define MAX287X_HPP_INCLUDED
+
+#include <uhd/exception.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/assign.hpp>
+#include <boost/function.hpp>
+#include <boost/thread.hpp>
+#include <vector>
+#include "max2870_regs.hpp"
+#include "max2871_regs.hpp"
+
+/**
+ * MAX287x interface
+ */
+class max287x_iface
+{
+public:
+ typedef boost::shared_ptr<max287x_iface> sptr;
+
+ typedef boost::function<void(std::vector<boost::uint32_t>)> write_fn;
+
+ /**
+ * LD Pin Modes
+ */
+ typedef enum{
+ LD_PIN_MODE_LOW,
+ LD_PIN_MODE_DLD,
+ LD_PIN_MODE_ALD,
+ LD_PIN_MODE_HIGH
+ } ld_pin_mode_t;
+
+ /**
+ * MUXOUT Modes
+ */
+ typedef enum{
+ MUXOUT_TRI_STATE,
+ MUXOUT_HIGH,
+ MUXOUT_LOW,
+ MUXOUT_RDIV,
+ MUXOUT_NDIV,
+ MUXOUT_ALD,
+ MUXOUT_DLD,
+ MUXOUT_SYNC,
+ MUXOUT_SPI
+ } muxout_mode_t;
+
+ /**
+ * Charge Pump Currents
+ */
+ typedef enum{
+ CHARGE_PUMP_CURRENT_0_32MA,
+ CHARGE_PUMP_CURRENT_0_64MA,
+ CHARGE_PUMP_CURRENT_0_96MA,
+ CHARGE_PUMP_CURRENT_1_28MA,
+ CHARGE_PUMP_CURRENT_1_60MA,
+ CHARGE_PUMP_CURRENT_1_92MA,
+ CHARGE_PUMP_CURRENT_2_24MA,
+ CHARGE_PUMP_CURRENT_2_56MA,
+ CHARGE_PUMP_CURRENT_2_88MA,
+ CHARGE_PUMP_CURRENT_3_20MA,
+ CHARGE_PUMP_CURRENT_3_52MA,
+ CHARGE_PUMP_CURRENT_3_84MA,
+ CHARGE_PUMP_CURRENT_4_16MA,
+ CHARGE_PUMP_CURRENT_4_48MA,
+ CHARGE_PUMP_CURRENT_4_80MA,
+ CHARGE_PUMP_CURRENT_5_12MA
+ } charge_pump_current_t;
+
+ /**
+ * Output Powers
+ */
+ typedef enum{
+ OUTPUT_POWER_M4DBM,
+ OUTPUT_POWER_M1DBM,
+ OUTPUT_POWER_2DBM,
+ OUTPUT_POWER_5DBM
+ } output_power_t;
+
+ typedef enum {
+ LOW_NOISE_AND_SPUR_LOW_NOISE,
+ LOW_NOISE_AND_SPUR_LOW_SPUR_1,
+ LOW_NOISE_AND_SPUR_LOW_SPUR_2
+ } low_noise_and_spur_t;
+
+ typedef enum {
+ CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF,
+ CLOCK_DIV_MODE_FAST_LOCK,
+ CLOCK_DIV_MODE_PHASE
+ } clock_divider_mode_t;
+
+ /**
+ * Make a synthesizer
+ * @param write write function
+ * @return shared pointer to object
+ */
+ template <typename max287X_t> static sptr make(write_fn write)
+ {
+ return sptr(new max287X_t(write));
+ }
+
+ /**
+ * Destructor
+ */
+ virtual ~max287x_iface() {};
+
+ /**
+ * Power up the synthesizer
+ */
+ virtual void power_up(void) = 0;
+
+ /**
+ * Shut down the synthesizer
+ */
+ virtual void shutdown(void) = 0;
+
+ /**
+ * Check if the synthesizer is shut down
+ */
+ virtual bool is_shutdown(void) = 0;
+
+ /**
+ * Set frequency
+ * @param target_freq target frequency
+ * @param ref_freq reference frequency
+ * @param target_pfd_freq target phase detector frequency
+ * @param is_int_n enable integer-N tuning
+ * @return actual frequency
+ */
+ virtual double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n) = 0;
+
+ /**
+ * Set output power
+ * @param power output power
+ */
+ virtual void set_output_power(output_power_t power) = 0;
+
+ /**
+ * Set lock detect pin mode
+ * @param mode lock detect pin mode
+ */
+ virtual void set_ld_pin_mode(ld_pin_mode_t mode) = 0;
+
+ /**
+ * Set muxout pin mode
+ * @param mode muxout mode
+ */
+ virtual void set_muxout_mode(muxout_mode_t mode) = 0;
+
+ /**
+ * Set charge pump current
+ * @param cp_current charge pump current
+ */
+ virtual void set_charge_pump_current(charge_pump_current_t cp_current) = 0;
+
+ /**
+ * Enable or disable auto retune
+ * @param enabled enable auto retune
+ */
+ virtual void set_auto_retune(bool enabled) = 0;
+
+ /**
+ * Set clock divider mode
+ * @param mode clock divider mode
+ */
+ virtual void set_clock_divider_mode(clock_divider_mode_t mode) = 0;
+
+ /**
+ * Enable or disable cycle slip mode
+ * @param enabled enable cycle slip mode
+ */
+ virtual void set_cycle_slip_mode(bool enabled) = 0;
+
+ /**
+ * Set low noise and spur mode
+ * @param mode low noise and spur mode
+ */
+ virtual void set_low_noise_and_spur(low_noise_and_spur_t mode) = 0;
+
+ /**
+ * Set phase
+ * @param phase the phase offset
+ */
+ virtual void set_phase(boost::uint16_t phase) = 0;
+
+ /**
+ * Write values configured by the set_* functions.
+ */
+ virtual void commit(void) = 0;
+
+ /**
+ * Check whether this is in a state where it can be synchronized
+ */
+ virtual bool can_sync(void) = 0;
+};
+
+/**
+ * MAX287x
+ * Base class for all MAX287x synthesizers
+ */
+template <typename max287x_regs_t>
+class max287x : public max287x_iface
+{
+public:
+ max287x(write_fn func);
+ virtual ~max287x();
+ virtual void power_up(void);
+ virtual void shutdown(void);
+ virtual bool is_shutdown(void);
+ virtual double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n);
+ virtual void set_output_power(output_power_t power);
+ virtual void set_ld_pin_mode(ld_pin_mode_t mode);
+ virtual void set_muxout_mode(muxout_mode_t mode);
+ virtual void set_charge_pump_current(charge_pump_current_t cp_current);
+ virtual void set_auto_retune(bool enabled);
+ virtual void set_clock_divider_mode(clock_divider_mode_t mode);
+ virtual void set_cycle_slip_mode(bool enabled);
+ virtual void set_low_noise_and_spur(low_noise_and_spur_t mode);
+ virtual void set_phase(boost::uint16_t phase);
+ virtual void commit();
+ virtual bool can_sync();
+
+protected:
+ max287x_regs_t _regs;
+ bool _can_sync;
+ bool _write_all_regs;
+
+private:
+ write_fn _write;
+ bool _delay_after_write;
+};
+
+/**
+ * MAX2870
+ */
+class max2870 : public max287x<max2870_regs_t>
+{
+public:
+ max2870(write_fn func) : max287x(func) {}
+ ~max2870() {}
+ double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+ {
+ _regs.cpoc = is_int_n ? max2870_regs_t::CPOC_ENABLED : max2870_regs_t::CPOC_DISABLED;
+ _regs.feedback_select = target_freq >= 3.0e9 ?
+ max2870_regs_t::FEEDBACK_SELECT_DIVIDED :
+ max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
+
+ return max287x::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n);
+ }
+ void commit(void)
+ {
+ // For MAX2870, we always need to write all registers.
+ _write_all_regs = true;
+ max287x::commit();
+ }
+};
+
+/**
+ * MAX2871
+ */
+class max2871 : public max287x<max2871_regs_t>
+{
+public:
+ max2871(write_fn func) : max287x(func) {}
+ ~max2871() {};
+ void set_muxout_mode(muxout_mode_t mode)
+ {
+ switch(mode)
+ {
+ case MUXOUT_SYNC:
+ _regs.muxout = max2871_regs_t::MUXOUT_SYNC;
+ break;
+ case MUXOUT_SPI:
+ _regs.muxout = max2871_regs_t::MUXOUT_SPI;
+ break;
+ default:
+ max287x::set_muxout_mode(mode);
+ }
+ }
+
+ double set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+ {
+ _regs.feedback_select = max2871_regs_t::FEEDBACK_SELECT_DIVIDED;
+ double freq = max287x::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n);
+
+ // According to Maxim support, the following factors must be true to allow for synchronization
+ if (_regs.r_counter_10_bit == 1 and
+ _regs.reference_divide_by_2 == max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED and
+ _regs.reference_doubler == max2871_regs_t::REFERENCE_DOUBLER_DISABLED and
+ _regs.rf_divider_select <= max2871_regs_t::RF_DIVIDER_SELECT_DIV16 and
+ _regs.low_noise_and_spur == max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE)
+ {
+ _can_sync = true;
+ }
+ return freq;
+ }
+};
+
+
+// Implementation of max287x template class
+// To avoid linker errors, it was either include
+// it here or put it in a .cpp file and include
+// that file in this header file. Decided to just
+// include it here.
+
+template <typename max287x_regs_t>
+max287x<max287x_regs_t>::max287x(write_fn func) :
+ _can_sync(false),
+ _write_all_regs(true),
+ _write(func),
+ _delay_after_write(true)
+{
+ power_up();
+}
+
+template <typename max287x_regs_t>
+max287x<max287x_regs_t>::~max287x()
+{
+ shutdown();
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::power_up(void)
+{
+ _regs.power_down = max287x_regs_t::POWER_DOWN_NORMAL;
+ _regs.double_buffer = max287x_regs_t::DOUBLE_BUFFER_ENABLED;
+
+ // According to MAX287x data sheets:
+ // "Upon power-up, the registers should be programmed twice with at
+ // least a 20ms pause between writes. The first write ensures that
+ // the device is enabled, and the second write starts the VCO
+ // selection process."
+ // The first write and the 20ms wait are done here. The second write
+ // is done when any other function that does a write to the registers
+ // is called (such as tuning).
+ _write_all_regs = true;
+ _delay_after_write = true;
+ commit();
+ _write_all_regs = true; // Next call to commit() writes all regs
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::shutdown(void)
+{
+ _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_DISABLED;
+ _regs.aux_output_enable = max287x_regs_t::AUX_OUTPUT_ENABLE_DISABLED;
+ _regs.power_down = max287x_regs_t::POWER_DOWN_SHUTDOWN;
+ commit();
+}
+
+template <typename max287x_regs_t>
+bool max287x<max287x_regs_t>::is_shutdown(void)
+{
+ return (_regs.power_down == max287x_regs_t::POWER_DOWN_SHUTDOWN);
+}
+
+template <typename max287x_regs_t>
+double max287x<max287x_regs_t>::set_frequency(
+ double target_freq,
+ double ref_freq,
+ double target_pfd_freq,
+ bool is_int_n)
+{
+ _can_sync = false;
+
+ //map rf divider select output dividers to enums
+ static const uhd::dict<int, typename max287x_regs_t::rf_divider_select_t> rfdivsel_to_enum =
+ boost::assign::map_list_of
+ (1, max287x_regs_t::RF_DIVIDER_SELECT_DIV1)
+ (2, max287x_regs_t::RF_DIVIDER_SELECT_DIV2)
+ (4, max287x_regs_t::RF_DIVIDER_SELECT_DIV4)
+ (8, max287x_regs_t::RF_DIVIDER_SELECT_DIV8)
+ (16, max287x_regs_t::RF_DIVIDER_SELECT_DIV16)
+ (32, max287x_regs_t::RF_DIVIDER_SELECT_DIV32)
+ (64, max287x_regs_t::RF_DIVIDER_SELECT_DIV64)
+ (128, max287x_regs_t::RF_DIVIDER_SELECT_DIV128);
+
+ //map mode setting to valid integer divider (N) values
+ static const uhd::range_t int_n_mode_div_range(16,65536,1);
+ static const uhd::range_t frac_n_mode_div_range(19,4091,1);
+
+ //other ranges and constants from MAX287X datasheets
+ static const uhd::range_t clock_div_range(1,4095,1);
+ static const uhd::range_t r_range(1,1023,1);
+ static const double MIN_VCO_FREQ = 3e9;
+ static const double BS_FREQ = 50e3;
+ static const int MAX_BS_VALUE = 1023;
+
+ int T = 0;
+ int D = ref_freq <= 10.0e6 ? 1 : 0;
+ int R = 0;
+ int BS = 0;
+ int N = 0;
+ int FRAC = 0;
+ int MOD = 4095;
+ int RFdiv = 1;
+ double pfd_freq = target_pfd_freq;
+ bool feedback_divided = (_regs.feedback_select == max287x_regs_t::FEEDBACK_SELECT_DIVIDED);
+
+ //increase RF divider until acceptable VCO frequency (MIN freq for MAX287x VCO is 3GHz)
+ double vco_freq = target_freq;
+ while (vco_freq < MIN_VCO_FREQ)
+ {
+ vco_freq *= 2;
+ RFdiv *= 2;
+ }
+
+ // The feedback frequency can be the fundamental VCO frequency or
+ // divided frequency. The output divider for MAX287x is actually
+ // 2 dividers, but only the first (1/2/4/8/16) is included in the
+ // feedback loop.
+ int fb_divisor = feedback_divided ? (RFdiv > 16 ? 16 : RFdiv) : 1;
+
+ /*
+ * The goal here is to loop though possible R dividers,
+ * band select clock dividers, N (int) dividers, and FRAC
+ * (frac) dividers.
+ *
+ * Calculate the N and F dividers for each set of values.
+ * The loop exits when it meets all of the constraints.
+ * The resulting loop values are loaded into the registers.
+ *
+ * f_pfd = f_ref*(1+D)/(R*(1+T))
+ * f_vco = (N + (FRAC/MOD))*f_pfd
+ * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
+ * f_rf = f_vco/RFdiv
+ */
+ for(R = int(ref_freq*(1+D)/(target_pfd_freq*(1+T))); R <= r_range.stop(); R++)
+ {
+ //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
+ pfd_freq = ref_freq*(1+D)/(R*(1+T));
+
+ //keep the PFD frequency at or below target
+ if (pfd_freq > target_pfd_freq)
+ continue;
+
+ //ignore fractional part of tuning
+ N = int((vco_freq/pfd_freq)/fb_divisor);
+
+ //Fractional-N calculation
+ FRAC = int(boost::math::round(((vco_freq/pfd_freq)/fb_divisor - N)*MOD));
+
+ if(is_int_n)
+ {
+ if (FRAC > (MOD / 2)) //Round integer such that actual freq is closest to target
+ N++;
+ FRAC = 0;
+ }
+
+ //keep N within int divider requirements
+ if(is_int_n)
+ {
+ if(N < int_n_mode_div_range.start()) continue;
+ if(N > int_n_mode_div_range.stop()) continue;
+ }
+ else
+ {
+ if(N < frac_n_mode_div_range.start()) continue;
+ if(N > frac_n_mode_div_range.stop()) continue;
+ }
+
+ //keep pfd freq low enough to achieve 50kHz BS clock
+ BS = std::ceil(pfd_freq / BS_FREQ);
+ if(BS <= MAX_BS_VALUE) break;
+ }
+ UHD_ASSERT_THROW(R <= r_range.stop());
+
+ //Reference divide-by-2 for 50% duty cycle
+ // if R even, move one divide by 2 to to regs.reference_divide_by_2
+ if(R % 2 == 0)
+ {
+ T = 1;
+ R /= 2;
+ }
+
+ //actual frequency calculation
+ double actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))) * fb_divisor / RFdiv;
+
+ UHD_LOGV(rarely)
+ << boost::format("MAX287x: Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f"
+ ) % ref_freq % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
+ << boost::format("MAX287x: tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") << std::endl
+ << boost::format("MAX287x: Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
+
+ //load the register values
+ _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_ENABLED;
+
+ if(is_int_n) {
+ _regs.cpl = max287x_regs_t::CPL_DISABLED;
+ _regs.ldf = max287x_regs_t::LDF_INT_N;
+ _regs.int_n_mode = max287x_regs_t::INT_N_MODE_INT_N;
+ } else {
+ _regs.cpl = max287x_regs_t::CPL_ENABLED;
+ _regs.ldf = max287x_regs_t::LDF_FRAC_N;
+ _regs.int_n_mode = max287x_regs_t::INT_N_MODE_FRAC_N;
+ }
+
+ _regs.lds = pfd_freq <= 32e6 ? max287x_regs_t::LDS_SLOW : max287x_regs_t::LDS_FAST;
+
+ _regs.frac_12_bit = FRAC;
+ _regs.int_16_bit = N;
+ _regs.mod_12_bit = MOD;
+ _regs.clock_divider_12_bit = std::max(int(clock_div_range.start()), int(std::ceil(400e-6*pfd_freq/MOD)));
+ UHD_ASSERT_THROW(_regs.clock_divider_12_bit <= clock_div_range.stop());
+ _regs.r_counter_10_bit = R;
+ _regs.reference_divide_by_2 = T ?
+ max287x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
+ max287x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
+ _regs.reference_doubler = D ?
+ max287x_regs_t::REFERENCE_DOUBLER_ENABLED :
+ max287x_regs_t::REFERENCE_DOUBLER_DISABLED;
+ _regs.band_select_clock_div = BS & 0xFF;
+ _regs.bs_msb = (BS & 0x300) >> 8;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ _regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+
+ if (_regs.clock_div_mode == max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK)
+ {
+ // Charge pump current needs to be set to lowest value in fast lock mode
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA;
+ // Make sure the register containing the charge pump current is written
+ _write_all_regs = true;
+ }
+
+ return actual_freq;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_output_power(output_power_t power)
+{
+ switch (power)
+ {
+ case OUTPUT_POWER_M4DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_M4DBM;
+ break;
+ case OUTPUT_POWER_M1DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_M1DBM;
+ break;
+ case OUTPUT_POWER_2DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_2DBM;
+ break;
+ case OUTPUT_POWER_5DBM:
+ _regs.output_power = max287x_regs_t::OUTPUT_POWER_5DBM;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_ld_pin_mode(ld_pin_mode_t mode)
+{
+ switch(mode)
+ {
+ case LD_PIN_MODE_LOW:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_LOW;
+ break;
+ case LD_PIN_MODE_DLD:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_DLD;
+ break;
+ case LD_PIN_MODE_ALD:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_ALD;
+ break;
+ case LD_PIN_MODE_HIGH:
+ _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_HIGH;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_muxout_mode(muxout_mode_t mode)
+{
+ switch(mode)
+ {
+ case MUXOUT_TRI_STATE:
+ _regs.muxout = max287x_regs_t::MUXOUT_TRI_STATE;
+ break;
+ case MUXOUT_HIGH:
+ _regs.muxout = max287x_regs_t::MUXOUT_HIGH;
+ break;
+ case MUXOUT_LOW:
+ _regs.muxout = max287x_regs_t::MUXOUT_LOW;
+ break;
+ case MUXOUT_RDIV:
+ _regs.muxout = max287x_regs_t::MUXOUT_RDIV;
+ break;
+ case MUXOUT_NDIV:
+ _regs.muxout = max287x_regs_t::MUXOUT_NDIV;
+ break;
+ case MUXOUT_ALD:
+ _regs.muxout = max287x_regs_t::MUXOUT_ALD;
+ break;
+ case MUXOUT_DLD:
+ _regs.muxout = max287x_regs_t::MUXOUT_DLD;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_charge_pump_current(charge_pump_current_t cp_current)
+{
+ switch(cp_current)
+ {
+ case CHARGE_PUMP_CURRENT_0_32MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA;
+ break;
+ case CHARGE_PUMP_CURRENT_0_64MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_64MA;
+ break;
+ case CHARGE_PUMP_CURRENT_0_96MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_96MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_28MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_28MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_60MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_60MA;
+ break;
+ case CHARGE_PUMP_CURRENT_1_92MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_92MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_24MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_24MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_56MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_56MA;
+ break;
+ case CHARGE_PUMP_CURRENT_2_88MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_88MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_20MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_20MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_52MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_52MA;
+ break;
+ case CHARGE_PUMP_CURRENT_3_84MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_84MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_16MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_16MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_48MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_48MA;
+ break;
+ case CHARGE_PUMP_CURRENT_4_80MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_80MA;
+ break;
+ case CHARGE_PUMP_CURRENT_5_12MA:
+ _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_5_12MA;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_auto_retune(bool enabled)
+{
+ _regs.retune = enabled ? max287x_regs_t::RETUNE_ENABLED : max287x_regs_t::RETUNE_DISABLED;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_clock_divider_mode(clock_divider_mode_t mode)
+{
+ switch(mode)
+ {
+ case CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF;
+ break;
+ case CLOCK_DIV_MODE_FAST_LOCK:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK;
+ break;
+ case CLOCK_DIV_MODE_PHASE:
+ _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_PHASE;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_cycle_slip_mode(bool enabled)
+{
+ if (enabled)
+ throw uhd::runtime_error("Cycle slip mode not supported on this MAX287x synthesizer.");
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_low_noise_and_spur(low_noise_and_spur_t mode)
+{
+ switch(mode)
+ {
+ case LOW_NOISE_AND_SPUR_LOW_NOISE:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE;
+ break;
+ case LOW_NOISE_AND_SPUR_LOW_SPUR_1:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_1;
+ break;
+ case LOW_NOISE_AND_SPUR_LOW_SPUR_2:
+ _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2;
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::set_phase(boost::uint16_t phase)
+{
+ _regs.phase_12_bit = phase & 0xFFF;
+}
+
+template <typename max287x_regs_t>
+void max287x<max287x_regs_t>::commit()
+{
+ std::vector<boost::uint32_t> regs;
+ std::set<boost::uint32_t> changed_regs;
+
+ // Get only regs with changes
+ if (_write_all_regs)
+ {
+ for (int addr = 5; addr >= 0; addr--)
+ regs.push_back(_regs.get_reg(boost::uint32_t(addr)));
+ } else {
+ try {
+ changed_regs = _regs.template get_changed_addrs<boost::uint32_t> ();
+ for (int addr = 5; addr >= 0; addr--)
+ {
+ if (changed_regs.find(boost::uint32_t(addr)) != changed_regs.end())
+ regs.push_back(_regs.get_reg(boost::uint32_t(addr)));
+ }
+ } catch (uhd::runtime_error& e) {
+ // No saved state - write all regs
+ for (int addr = 5; addr >= 0; addr--)
+ regs.push_back(_regs.get_reg(boost::uint32_t(addr)));
+ }
+ }
+
+ _write(regs);
+ _regs.save_state();
+ _write_all_regs = false;
+
+ if (_delay_after_write)
+ {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(20));
+ _delay_after_write = false;
+ }
+}
+
+
+template <typename max287x_regs_t>
+bool max287x<max287x_regs_t>::can_sync(void)
+{
+ return _can_sync;
+}
+
+#endif // MAX287X_HPP_INCLUDED
diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp
index ad255460e..8336117b8 100644
--- a/host/lib/usrp/dboard/db_cbx.cpp
+++ b/host/lib/usrp/dboard/db_cbx.cpp
@@ -15,8 +15,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
-
-#include "max2870_regs.hpp"
#include "db_sbx_common.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/math/special_functions/round.hpp>
@@ -31,6 +29,8 @@ using namespace boost::assign;
sbx_xcvr::cbx::cbx(sbx_xcvr *_self_sbx_xcvr) {
//register the handle to our base CBX class
self_base = _self_sbx_xcvr;
+ _txlo = max287x_iface::make<max2870>(boost::bind(&sbx_xcvr::cbx::write_lo_regs, this, dboard_iface::UNIT_TX, _1));
+ _rxlo = max287x_iface::make<max2870>(boost::bind(&sbx_xcvr::cbx::write_lo_regs, this, dboard_iface::UNIT_RX, _1));
}
@@ -38,6 +38,14 @@ sbx_xcvr::cbx::~cbx(void){
/* NOP */
}
+void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> &regs)
+{
+ BOOST_FOREACH(boost::uint32_t reg, regs)
+ {
+ self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32);
+ }
+}
+
/***********************************************************************
* Tuning
@@ -47,6 +55,13 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)
"CBX tune: target frequency %f MHz"
) % (target_freq/1e6) << std::endl;
+ //clip the input
+ target_freq = cbx_freq_range.clip(target_freq);
+
+ double ref_freq = self_base->get_iface()->get_clock_rate(unit);
+ double target_pfd_freq = 25e6;
+ double actual_freq = 0.0;
+
/*
* If the user sets 'mode_n=integer' in the tuning args, the user wishes to
* tune in Integer-N mode, which can result in better spur
@@ -57,174 +72,17 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq)
device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();
bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer");
- //clip the input
- target_freq = cbx_freq_range.clip(target_freq);
-
- //map mode setting to valid integer divider (N) values
- static const uhd::range_t int_n_mode_div_range(16,4095,1);
- static const uhd::range_t frac_n_mode_div_range(19,4091,1);
-
- //map rf divider select output dividers to enums
- static const uhd::dict<int, max2870_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of
- (1, max2870_regs_t::RF_DIVIDER_SELECT_DIV1)
- (2, max2870_regs_t::RF_DIVIDER_SELECT_DIV2)
- (4, max2870_regs_t::RF_DIVIDER_SELECT_DIV4)
- (8, max2870_regs_t::RF_DIVIDER_SELECT_DIV8)
- (16, max2870_regs_t::RF_DIVIDER_SELECT_DIV16)
- (32, max2870_regs_t::RF_DIVIDER_SELECT_DIV32)
- (64, max2870_regs_t::RF_DIVIDER_SELECT_DIV64)
- (128, max2870_regs_t::RF_DIVIDER_SELECT_DIV128)
- ;
-
- double actual_freq, pfd_freq;
- double ref_freq = self_base->get_iface()->get_clock_rate(unit);
- int R=0, BS=0, N=0, FRAC=0, MOD=4095;
- int RFdiv = 1;
- max2870_regs_t::reference_divide_by_2_t T = max2870_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
- max2870_regs_t::reference_doubler_t D = max2870_regs_t::REFERENCE_DOUBLER_DISABLED;
-
- //Reference doubler for 50% duty cycle
- // if ref_freq < 12.5MHz enable regs.reference_divide_by_2
- //NOTE: MAX2870 goes down to 10MHz ref vs. 12.5MHz on ADF4351
- if(ref_freq <= 10.0e6) D = max2870_regs_t::REFERENCE_DOUBLER_ENABLED;
-
- //increase RF divider until acceptable VCO frequency
- double vco_freq = target_freq;
- //NOTE: MIN freq for MAX2870 VCO is 3GHz vs. 2.2GHz on ADF4351
- while (vco_freq < 3e9) {
- vco_freq *= 2;
- RFdiv *= 2;
- }
-
- /*
- * The goal here is to loop though possible R dividers,
- * band select clock dividers, N (int) dividers, and FRAC
- * (frac) dividers.
- *
- * Calculate the N and F dividers for each set of values.
- * The loop exits when it meets all of the constraints.
- * The resulting loop values are loaded into the registers.
- *
- * from pg.21
- *
- * f_pfd = f_ref*(1+D)/(R*(1+T))
- * f_vco = (N + (FRAC/MOD))*f_pfd
- * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
- * f_rf = f_vco/RFdiv
- */
- for(R = 1; R <= 1023; R+=1){
- //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
- pfd_freq = ref_freq*(1+D)/(R*(1+T));
-
- //keep the PFD frequency at or below 25MHz
- if (pfd_freq > 25e6) continue;
-
- //ignore fractional part of tuning
- N = int(vco_freq/pfd_freq);
-
- //Fractional-N calculation
- FRAC = int(boost::math::round((vco_freq/pfd_freq - N)*MOD));
-
- if(is_int_n) {
- if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target
- N++;
- }
- FRAC = 0;
- }
-
- //keep N within int divider requirements
- if(is_int_n) {
- if(N < int_n_mode_div_range.start()) continue;
- if(N > int_n_mode_div_range.stop()) continue;
- } else {
- if(N < frac_n_mode_div_range.start()) continue;
- if(N > frac_n_mode_div_range.stop()) continue;
- }
-
- //keep pfd freq low enough to achieve 50kHz BS clock
- BS = int(std::ceil(pfd_freq / 50e3));
- if(BS <= 1023) break;
- }
-
- UHD_ASSERT_THROW(R <= 1023);
-
- //Reference divide-by-2 for 50% duty cycle
- // if R even, move one divide by 2 to to regs.reference_divide_by_2
- if(R % 2 == 0){
- T = max2870_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED;
- R /= 2;
- }
-
- //actual frequency calculation
- actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv);
-
- boost::uint16_t rx_id = self_base->get_rx_id().to_uint16();
- std::string board_name = (rx_id == 0x0085) ? "CBX-120" : "CBX";
- UHD_LOGV(often)
- << boost::format("%s Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f"
- ) % board_name.c_str() % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
- << boost::format("%s tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s"
- ) % board_name.c_str() % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") << std::endl
- << boost::format("%s Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
- ) % board_name.c_str() % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
-
- //load the register values
- max2870_regs_t regs;
-
- if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))
- regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM;
- else
- regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM;
-
- //set frac/int CPL mode
- max2870_regs_t::cpl_t cpl;
- max2870_regs_t::ldf_t ldf;
- max2870_regs_t::cpoc_t cpoc;
- if(is_int_n) {
- cpl = max2870_regs_t::CPL_DISABLED;
- cpoc = max2870_regs_t::CPOC_ENABLED;
- ldf = max2870_regs_t::LDF_INT_N;
+ if (unit == dboard_iface::UNIT_RX)
+ {
+ actual_freq = _rxlo->set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n);
+ _rxlo->commit();
} else {
- cpl = max2870_regs_t::CPL_ENABLED;
- ldf = max2870_regs_t::LDF_FRAC_N;
- cpoc = max2870_regs_t::CPOC_DISABLED;
- }
-
- regs.frac_12_bit = FRAC;
- regs.int_16_bit = N;
- regs.mod_12_bit = MOD;
- regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD)));
- regs.feedback_select = (target_freq >= 3.0e9) ? max2870_regs_t::FEEDBACK_SELECT_DIVIDED : max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
- regs.r_counter_10_bit = R;
- regs.reference_divide_by_2 = T;
- regs.reference_doubler = D;
- regs.band_select_clock_div = (BS & 0x0FF);
- regs.bs_msb = (BS & 0x300) >>8;
- UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
- regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
- regs.int_n_mode = (is_int_n) ? max2870_regs_t::INT_N_MODE_INT_N : max2870_regs_t::INT_N_MODE_FRAC_N;
- regs.cpl = cpl;
- regs.ldf = ldf;
- regs.cpoc = cpoc;
-
- //write the registers
- //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0)
- int addr;
-
- for(addr=5; addr>=0; addr--){
- UHD_LOGV(often) << boost::format(
- "%s SPI Reg (0x%02x): 0x%08x"
- ) % board_name.c_str() % addr % regs.get_reg(addr) << std::endl;
- self_base->get_iface()->write_spi(
- unit, spi_config_t::EDGE_RISE,
- regs.get_reg(addr), 32
- );
+ actual_freq = _txlo->set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n);
+ _txlo->set_output_power((actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))
+ ? max287x_iface::OUTPUT_POWER_2DBM
+ : max287x_iface::OUTPUT_POWER_5DBM);
+ _txlo->commit();
}
-
- //return the actual frequency
- UHD_LOGV(often) << boost::format(
- "%s tune: actual frequency %f MHz"
- ) % board_name.c_str() % (actual_freq/1e6) << std::endl;
return actual_freq;
}
diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp
index 58f79a606..a08d22537 100644
--- a/host/lib/usrp/dboard/db_sbx_common.hpp
+++ b/host/lib/usrp/dboard/db_sbx_common.hpp
@@ -17,7 +17,8 @@
#include <uhd/types/device_addr.hpp>
-#include "../common/adf435x_common.hpp"
+#include "adf435x_common.hpp"
+#include "max287x.hpp"
// Common IO Pins
#define LO_LPF_EN (1 << 15)
@@ -223,6 +224,10 @@ protected:
/*! This is the registered instance of the wrapper class, sbx_base. */
sbx_xcvr *self_base;
+ private:
+ void write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> &regs);
+ max287x_iface::sptr _txlo;
+ max287x_iface::sptr _rxlo;
};
/*!
diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp
index 06bfad7d3..1e79c14b0 100644
--- a/host/lib/usrp/dboard/db_ubx.cpp
+++ b/host/lib/usrp/dboard/db_ubx.cpp
@@ -34,522 +34,11 @@
#include <boost/algorithm/string.hpp>
#include <boost/thread/mutex.hpp>
#include <map>
+#include "max287x.hpp"
using namespace uhd;
using namespace uhd::usrp;
-#define fMHz (1000000.0)
-#define UBX_PROTO_V3_TX_ID 0x73
-#define UBX_PROTO_V3_RX_ID 0x74
-#define UBX_PROTO_V4_TX_ID 0x75
-#define UBX_PROTO_V4_RX_ID 0x76
-#define UBX_V1_40MHZ_TX_ID 0x77
-#define UBX_V1_40MHZ_RX_ID 0x78
-#define UBX_V1_160MHZ_TX_ID 0x79
-#define UBX_V1_160MHZ_RX_ID 0x7a
-
-/***********************************************************************
- * UBX Synthesizers
- **********************************************************************/
-#include "max2870_regs.hpp"
-#include "max2871_regs.hpp"
-
-typedef boost::function<void(std::vector<boost::uint32_t>)> max287x_write_fn;
-
-class max287x_synthesizer_iface
-{
-public:
- virtual bool is_shutdown(void) = 0;
- virtual void shutdown(void) = 0;
- virtual void power_up(void) = 0;
- virtual double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power) = 0;
-};
-
-class max287x : public max287x_synthesizer_iface
-{
-public:
- max287x(max287x_write_fn write_fn) : _write_fn(write_fn) {};
- virtual ~max287x() {};
-
-protected:
- virtual std::set<boost::uint32_t> get_changed_addrs(void) = 0;
- virtual boost::uint32_t get_reg(boost::uint32_t addr) = 0;
- virtual void save_state(void) = 0;
-
- void write_regs(void)
- {
- std::vector<boost::uint32_t> regs;
- std::set<boost::uint32_t> changed_regs;
-
- // Get only regs with changes
- try {
- changed_regs = get_changed_addrs();
- } catch (uhd::runtime_error&) {
- // No saved state - write all regs
- for (int addr = 5; addr >= 0; addr--)
- changed_regs.insert(boost::uint32_t(addr));
- }
-
- for (int addr = 5; addr >= 0; addr--)
- {
- if (changed_regs.find(boost::uint32_t(addr)) != changed_regs.end())
- regs.push_back(get_reg(boost::uint32_t(addr)));
- }
-
- // writing reg 0 initiates VCO auto select, so this makes sure it is written
- if (changed_regs.size() and changed_regs.find(0) == changed_regs.end())
- regs.push_back(get_reg(0));
-
- _write_fn(regs);
- save_state();
- }
-
- double calculate_freq_settings(
- double target_freq,
- double ref_freq,
- double target_pfd_freq,
- bool is_int_n,
- double &pfd_freq,
- int& T,
- int& D,
- int& R,
- int& BS,
- int& N,
- int& FRAC,
- int& MOD,
- int& RFdiv)
- {
- //map mode setting to valid integer divider (N) values
- static const uhd::range_t int_n_mode_div_range(16,4095,1);
- static const uhd::range_t frac_n_mode_div_range(19,4091,1);
-
- double actual_freq = 0.0;
-
- T = 0;
- D = ref_freq <= 10.0e6 ? 1 : 0;
- R = 0;
- BS = 0;
- N = 0;
- FRAC = 0;
- MOD = 4095;
- RFdiv = 1;
-
- //increase RF divider until acceptable VCO frequency (MIN freq for MAX287x VCO is 3GHz)
- double vco_freq = target_freq;
- while (vco_freq < 3e9)
- {
- vco_freq *= 2;
- RFdiv *= 2;
- }
-
- /*
- * The goal here is to loop though possible R dividers,
- * band select clock dividers, N (int) dividers, and FRAC
- * (frac) dividers.
- *
- * Calculate the N and F dividers for each set of values.
- * The loop exits when it meets all of the constraints.
- * The resulting loop values are loaded into the registers.
- *
- * f_pfd = f_ref*(1+D)/(R*(1+T))
- * f_vco = (N + (FRAC/MOD))*f_pfd
- * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD
- * f_rf = f_vco/RFdiv
- */
- for(R = int(ref_freq*(1+D)/(target_pfd_freq*(1+T))); R <= 1023; R++)
- {
- //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T)
- pfd_freq = ref_freq*(1+D)/(R*(1+T));
-
- //keep the PFD frequency at or below target
- if (pfd_freq > target_pfd_freq)
- continue;
-
- //ignore fractional part of tuning
- N = int(vco_freq/pfd_freq);
-
- //Fractional-N calculation
- FRAC = int(boost::math::round((vco_freq/pfd_freq - N)*MOD));
-
- if(is_int_n)
- {
- if (FRAC > (MOD / 2)) //Round integer such that actual freq is closest to target
- N++;
- FRAC = 0;
- }
-
- //keep N within int divider requirements
- if(is_int_n)
- {
- if(N < int_n_mode_div_range.start()) continue;
- if(N > int_n_mode_div_range.stop()) continue;
- }
- else
- {
- if(N < frac_n_mode_div_range.start()) continue;
- if(N > frac_n_mode_div_range.stop()) continue;
- }
-
- //keep pfd freq low enough to achieve 50kHz BS clock
- BS = int(std::ceil(pfd_freq / 50e3));
- if(BS <= 1023) break;
- }
- UHD_ASSERT_THROW(R <= 1023);
-
- //Reference divide-by-2 for 50% duty cycle
- // if R even, move one divide by 2 to to regs.reference_divide_by_2
- if(R % 2 == 0)
- {
- T = 1;
- R /= 2;
- }
-
- //actual frequency calculation
- actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv);
-
- UHD_LOGV(rarely)
- << boost::format("MAX287x: Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f"
- ) % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl
- << boost::format("MAX287x: tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s"
- ) % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") << std::endl
- << boost::format("MAX287x: Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
- ) % (pfd_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl;
-
- return actual_freq;
- }
-
- max287x_write_fn _write_fn;
-};
-
-class max2870 : public max287x
-{
-public:
- max2870(max287x_write_fn write_fn) : max287x(write_fn), _first_tune(true)
- {
- // initialize register values (override defaults)
- _regs.retune = max2870_regs_t::RETUNE_DISABLED;
- _regs.clock_div_mode = max2870_regs_t::CLOCK_DIV_MODE_FAST_LOCK;
-
- // MAX2870 data sheet says that all registers must be written twice
- // with at least a 20ms delay between writes upon power up. One
- // write and a 20ms wait are done in power_up(). The second write
- // is done when any other function that does a write to the registers
- // is called. To ensure all registers are written the second time, the
- // state of the registers is not saved during the first write.
- _save_state = false;
- power_up();
- _save_state = true;
- };
-
- ~max2870()
- {
- shutdown();
- };
-
- bool is_shutdown(void)
- {
- return (_regs.power_down == max2870_regs_t::POWER_DOWN_SHUTDOWN);
- };
-
- void shutdown(void)
- {
- _regs.rf_output_enable = max2870_regs_t::RF_OUTPUT_ENABLE_DISABLED;
- _regs.aux_output_enable = max2870_regs_t::AUX_OUTPUT_ENABLE_DISABLED;
- _regs.power_down = max2870_regs_t::POWER_DOWN_SHUTDOWN;
- _regs.muxout = max2870_regs_t::MUXOUT_LOW;
- _regs.ld_pin_mode = max2870_regs_t::LD_PIN_MODE_LOW;
- write_regs();
- };
-
- void power_up(void)
- {
- _regs.muxout = max2870_regs_t::MUXOUT_DLD;
- _regs.ld_pin_mode = max2870_regs_t::LD_PIN_MODE_DLD;
- _regs.power_down = max2870_regs_t::POWER_DOWN_NORMAL;
- write_regs();
-
- // MAX270 data sheet says to wait at least 20 ms after exiting low power mode
- // before programming final VCO frequency
- boost::this_thread::sleep(boost::posix_time::milliseconds(20));
-
- _first_tune = true;
- };
-
- double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power)
- {
- //map rf divider select output dividers to enums
- static const uhd::dict<int, max2870_regs_t::rf_divider_select_t> rfdivsel_to_enum =
- boost::assign::map_list_of
- (1, max2870_regs_t::RF_DIVIDER_SELECT_DIV1)
- (2, max2870_regs_t::RF_DIVIDER_SELECT_DIV2)
- (4, max2870_regs_t::RF_DIVIDER_SELECT_DIV4)
- (8, max2870_regs_t::RF_DIVIDER_SELECT_DIV8)
- (16, max2870_regs_t::RF_DIVIDER_SELECT_DIV16)
- (32, max2870_regs_t::RF_DIVIDER_SELECT_DIV32)
- (64, max2870_regs_t::RF_DIVIDER_SELECT_DIV64)
- (128, max2870_regs_t::RF_DIVIDER_SELECT_DIV128);
-
- int T = 0;
- int D = ref_freq <= 10.0e6 ? 1 : 0;
- int R, BS, N, FRAC, MOD, RFdiv;
- double pfd_freq = 25e6;
-
- double actual_freq = calculate_freq_settings(
- target_freq, ref_freq, 25e6, is_int_n, pfd_freq, T, D, R, BS, N, FRAC, MOD, RFdiv);
-
- //load the register values
- _regs.rf_output_enable = max2870_regs_t::RF_OUTPUT_ENABLE_ENABLED;
-
- if(is_int_n) {
- _regs.cpl = max2870_regs_t::CPL_DISABLED;
- _regs.ldf = max2870_regs_t::LDF_INT_N;
- _regs.cpoc = max2870_regs_t::CPOC_ENABLED;
- _regs.int_n_mode = max2870_regs_t::INT_N_MODE_INT_N;
- } else {
- _regs.cpl = max2870_regs_t::CPL_ENABLED;
- _regs.ldf = max2870_regs_t::LDF_FRAC_N;
- _regs.cpoc = max2870_regs_t::CPOC_DISABLED;
- _regs.int_n_mode = max2870_regs_t::INT_N_MODE_FRAC_N;
- }
-
- _regs.lds = pfd_freq <= 32e6 ? max2870_regs_t::LDS_SLOW : max2870_regs_t::LDS_FAST;
-
- _regs.frac_12_bit = FRAC;
- _regs.int_16_bit = N;
- _regs.mod_12_bit = MOD;
- _regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD)));
- _regs.feedback_select = (target_freq >= 3.0e9) ?
- max2870_regs_t::FEEDBACK_SELECT_DIVIDED :
- max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
- _regs.r_counter_10_bit = R;
- _regs.reference_divide_by_2 = T ?
- max2870_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
- max2870_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
- _regs.reference_doubler = D ?
- max2870_regs_t::REFERENCE_DOUBLER_ENABLED :
- max2870_regs_t::REFERENCE_DOUBLER_DISABLED;
- _regs.band_select_clock_div = BS;
- _regs.bs_msb = (BS & 0x300) >> 8;
- UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
- _regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
-
- switch (output_power)
- {
- case -4:
- _regs.output_power = max2870_regs_t::OUTPUT_POWER_M4DBM;
- break;
- case -1:
- _regs.output_power = max2870_regs_t::OUTPUT_POWER_M1DBM;
- break;
- case 2:
- _regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM;
- break;
- case 5:
- _regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM;
- break;
- }
-
- // Write the register values
- write_regs();
-
- // MAX2870 needs a 20ms delay after tuning for the first time
- // for the lock detect to be reliable.
- if (_first_tune)
- {
- boost::this_thread::sleep(boost::posix_time::milliseconds(20));
- _first_tune = false;
- }
-
- return actual_freq;
- };
-
-private:
- std::set<boost::uint32_t> get_changed_addrs()
- {
- return _regs.get_changed_addrs<boost::uint32_t>();
- };
-
- boost::uint32_t get_reg(boost::uint32_t addr)
- {
- return _regs.get_reg(addr);
- };
-
- void save_state()
- {
- if (_save_state)
- _regs.save_state();
- }
-
- max2870_regs_t _regs;
- bool _save_state;
- bool _first_tune;
-};
-
-class max2871 : public max287x
-{
-public:
- max2871(max287x_write_fn write_fn) : max287x(write_fn), _first_tune(true)
- {
- // initialize register values (override defaults)
- _regs.retune = max2871_regs_t::RETUNE_DISABLED;
- //_regs.csm = max2871_regs_t::CSM_ENABLED; // tried it - caused long lock times
- _regs.charge_pump_current = max2871_regs_t::CHARGE_PUMP_CURRENT_5_12MA;
-
- // MAX2871 data sheet says that all registers must be written twice
- // with at least a 20ms delay between writes upon power up. One
- // write and a 20ms wait are done in power_up(). The second write
- // is done when any other function that does a write to the registers
- // is called. To ensure all registers are written the second time, the
- // state of the registers is not saved during the first write.
- _save_state = false;
- power_up();
- _save_state = true;
- };
-
- ~max2871()
- {
- shutdown();
- };
-
- bool is_shutdown(void)
- {
- return (_regs.power_down == max2871_regs_t::POWER_DOWN_SHUTDOWN);
- };
-
- void shutdown(void)
- {
- _regs.rf_output_enable = max2871_regs_t::RF_OUTPUT_ENABLE_DISABLED;
- _regs.aux_output_enable = max2871_regs_t::AUX_OUTPUT_ENABLE_DISABLED;
- _regs.power_down = max2871_regs_t::POWER_DOWN_SHUTDOWN;
- _regs.ld_pin_mode = max2871_regs_t::LD_PIN_MODE_LOW;
- _regs.muxout = max2871_regs_t::MUXOUT_TRI_STATE;
- write_regs();
- };
-
- void power_up(void)
- {
- _regs.ld_pin_mode = max2871_regs_t::LD_PIN_MODE_DLD;
- _regs.power_down = max2871_regs_t::POWER_DOWN_NORMAL;
- _regs.muxout = max2871_regs_t::MUXOUT_TRI_STATE;
- write_regs();
-
- // MAX271 data sheet says to wait at least 20 ms after exiting low power mode
- // before programming final VCO frequency
- boost::this_thread::sleep(boost::posix_time::milliseconds(20));
-
- _first_tune = true;
- };
-
- double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power)
- {
- //map rf divider select output dividers to enums
- static const uhd::dict<int, max2871_regs_t::rf_divider_select_t> rfdivsel_to_enum =
- boost::assign::map_list_of
- (1, max2871_regs_t::RF_DIVIDER_SELECT_DIV1)
- (2, max2871_regs_t::RF_DIVIDER_SELECT_DIV2)
- (4, max2871_regs_t::RF_DIVIDER_SELECT_DIV4)
- (8, max2871_regs_t::RF_DIVIDER_SELECT_DIV8)
- (16, max2871_regs_t::RF_DIVIDER_SELECT_DIV16)
- (32, max2871_regs_t::RF_DIVIDER_SELECT_DIV32)
- (64, max2871_regs_t::RF_DIVIDER_SELECT_DIV64)
- (128, max2871_regs_t::RF_DIVIDER_SELECT_DIV128);
-
- int T = 0;
- int D = ref_freq <= 10.0e6 ? 1 : 0;
- int R, BS, N, FRAC, MOD, RFdiv;
- double pfd_freq = 50e6;
-
- double actual_freq = calculate_freq_settings(
- target_freq, ref_freq, 50e6, is_int_n, pfd_freq, T, D, R, BS, N, FRAC, MOD, RFdiv);
-
- //load the register values
- _regs.rf_output_enable = max2871_regs_t::RF_OUTPUT_ENABLE_ENABLED;
-
- if(is_int_n) {
- _regs.cpl = max2871_regs_t::CPL_DISABLED;
- _regs.ldf = max2871_regs_t::LDF_INT_N;
- _regs.int_n_mode = max2871_regs_t::INT_N_MODE_INT_N;
- } else {
- _regs.cpl = max2871_regs_t::CPL_ENABLED;
- _regs.ldf = max2871_regs_t::LDF_FRAC_N;
- _regs.int_n_mode = max2871_regs_t::INT_N_MODE_FRAC_N;
- }
-
- _regs.lds = pfd_freq <= 32e6 ? max2871_regs_t::LDS_SLOW : max2871_regs_t::LDS_FAST;
-
- _regs.frac_12_bit = FRAC;
- _regs.int_16_bit = N;
- _regs.mod_12_bit = MOD;
- _regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD)));
- _regs.feedback_select = (target_freq >= 3.0e9) ?
- max2871_regs_t::FEEDBACK_SELECT_DIVIDED :
- max2871_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
- _regs.r_counter_10_bit = R;
- _regs.reference_divide_by_2 = T ?
- max2871_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED :
- max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED;
- _regs.reference_doubler = D ?
- max2871_regs_t::REFERENCE_DOUBLER_ENABLED :
- max2871_regs_t::REFERENCE_DOUBLER_DISABLED;
- _regs.band_select_clock_div = BS;
- _regs.bs_msb = (BS & 0x300) >> 8;
- UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
- _regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
-
- switch (output_power)
- {
- case -4:
- _regs.output_power = max2871_regs_t::OUTPUT_POWER_M4DBM;
- break;
- case -1:
- _regs.output_power = max2871_regs_t::OUTPUT_POWER_M1DBM;
- break;
- case 2:
- _regs.output_power = max2871_regs_t::OUTPUT_POWER_2DBM;
- break;
- case 5:
- _regs.output_power = max2871_regs_t::OUTPUT_POWER_5DBM;
- break;
- default:
- UHD_THROW_INVALID_CODE_PATH();
- break;
- }
-
- write_regs();
-
- // MAX2871 needs a 20ms delay after tuning for the first time
- // for the lock detect to be reliable.
- if (_first_tune)
- {
- boost::this_thread::sleep(boost::posix_time::milliseconds(20));
- _first_tune = false;
- }
-
- return actual_freq;
- };
-
-private:
- std::set<boost::uint32_t> get_changed_addrs()
- {
- return _regs.get_changed_addrs<boost::uint32_t>();
- };
-
- boost::uint32_t get_reg(boost::uint32_t addr)
- {
- return _regs.get_reg(addr);
- };
-
- void save_state()
- {
- if (_save_state)
- _regs.save_state();
- }
-
- max2871_regs_t _regs;
- bool _save_state;
- bool _first_tune;
-};
-
/***********************************************************************
* UBX Data Structures
**********************************************************************/
@@ -606,7 +95,7 @@ struct ubx_gpio_field_info_t
boost::uint32_t offset;
boost::uint32_t mask;
boost::uint32_t width;
- enum direction_t {OUTPUT,INPUT} direction;
+ enum {OUTPUT,INPUT} direction;
bool is_atr_controlled;
boost::uint32_t atr_idle;
boost::uint32_t atr_tx;
@@ -653,7 +142,16 @@ enum spi_dest_t {
/***********************************************************************
* UBX Constants
**********************************************************************/
-static const freq_range_t ubx_freq_range(1.0e7, 6.0e9);
+#define fMHz (1000000.0)
+static const dboard_id_t UBX_PROTO_V3_TX_ID(0x73);
+static const dboard_id_t UBX_PROTO_V3_RX_ID(0x74);
+static const dboard_id_t UBX_PROTO_V4_TX_ID(0x75);
+static const dboard_id_t UBX_PROTO_V4_RX_ID(0x76);
+static const dboard_id_t UBX_V1_40MHZ_TX_ID(0x77);
+static const dboard_id_t UBX_V1_40MHZ_RX_ID(0x78);
+static const dboard_id_t UBX_V1_160MHZ_TX_ID(0x79);
+static const dboard_id_t UBX_V1_160MHZ_RX_ID(0x7A);
+static const freq_range_t ubx_freq_range(10e6, 6.0e9);
static const gain_range_t ubx_tx_gain_range(0, 31.5, double(0.5));
static const gain_range_t ubx_rx_gain_range(0, 31.5, double(0.5));
static const std::vector<std::string> ubx_pgas = boost::assign::list_of("PGA-TX")("PGA-RX");
@@ -664,6 +162,7 @@ static const std::vector<std::string> ubx_power_modes = boost::assign::list_of("
static const std::vector<std::string> ubx_xcvr_modes = boost::assign::list_of("FDX")("TX")("TX/RX")("RX");
static const ubx_gpio_field_info_t ubx_proto_gpio_info[] = {
+ //Field Unit Offset Mask Width Direction ATR IDLE,TX,RX,FDX
{SPI_ADDR, dboard_iface::UNIT_TX, 0, 0x7, 3, ubx_gpio_field_info_t::INPUT, false, 0, 0, 0, 0},
{TX_EN_N, dboard_iface::UNIT_TX, 3, 0x1<<3, 1, ubx_gpio_field_info_t::INPUT, true, 1, 0, 1, 0},
{RX_EN_N, dboard_iface::UNIT_TX, 4, 0x1<<4, 1, ubx_gpio_field_info_t::INPUT, true, 1, 1, 0, 0},
@@ -676,6 +175,7 @@ static const ubx_gpio_field_info_t ubx_proto_gpio_info[] = {
};
static const ubx_gpio_field_info_t ubx_v1_gpio_info[] = {
+ //Field Unit Offset Mask Width Direction ATR IDLE,TX,RX,FDX
{SPI_ADDR, dboard_iface::UNIT_TX, 0, 0x7, 3, ubx_gpio_field_info_t::INPUT, false, 0, 0, 0, 0},
{CPLD_RST_N, dboard_iface::UNIT_TX, 3, 0x1<<3, 1, ubx_gpio_field_info_t::INPUT, false, 0, 0, 0, 0},
{RX_ANT, dboard_iface::UNIT_TX, 4, 0x1<<4, 1, ubx_gpio_field_info_t::INPUT, false, 0, 0, 0, 0},
@@ -695,7 +195,8 @@ static const ubx_gpio_field_info_t ubx_v1_gpio_info[] = {
* Macros for routing and writing SPI registers
**********************************************************************/
#define ROUTE_SPI(iface, dest) \
- iface->set_gpio_out(dboard_iface::UNIT_TX, dest, 0x7);
+ set_gpio_field(SPI_ADDR, dest); \
+ write_gpio();
#define WRITE_SPI(iface, val) \
iface->write_spi(dboard_iface::UNIT_TX, spi_config_t::EDGE_RISE, val, 32);
@@ -708,6 +209,9 @@ class ubx_xcvr : public xcvr_dboard_base
public:
ubx_xcvr(ctor_args_t args) : xcvr_dboard_base(args)
{
+ double bw = 40e6;
+ double pfd_freq_max = 25e6;
+
////////////////////////////////////////////////////////////////////
// Setup GPIO hardware
////////////////////////////////////////////////////////////////////
@@ -716,12 +220,15 @@ public:
dboard_id_t tx_id = get_tx_id();
if (rx_id == UBX_PROTO_V3_RX_ID and tx_id == UBX_PROTO_V3_TX_ID)
_rev = 0;
- if (rx_id == UBX_PROTO_V4_RX_ID and tx_id == UBX_PROTO_V4_TX_ID)
+ else if (rx_id == UBX_PROTO_V4_RX_ID and tx_id == UBX_PROTO_V4_TX_ID)
_rev = 1;
else if (rx_id == UBX_V1_40MHZ_RX_ID and tx_id == UBX_V1_40MHZ_TX_ID)
_rev = 1;
else if (rx_id == UBX_V1_160MHZ_RX_ID and tx_id == UBX_V1_160MHZ_TX_ID)
+ {
+ bw = 160e6;
_rev = 1;
+ }
else
UHD_THROW_INVALID_CODE_PATH();
@@ -730,10 +237,12 @@ public:
case 0:
for (size_t i = 0; i < sizeof(ubx_proto_gpio_info) / sizeof(ubx_gpio_field_info_t); i++)
_gpio_map[ubx_proto_gpio_info[i].id] = ubx_proto_gpio_info[i];
+ pfd_freq_max = 25e6;
break;
case 1:
for (size_t i = 0; i < sizeof(ubx_v1_gpio_info) / sizeof(ubx_gpio_field_info_t); i++)
_gpio_map[ubx_v1_gpio_info[i].id] = ubx_v1_gpio_info[i];
+ pfd_freq_max = 50e6;
break;
}
@@ -757,6 +266,34 @@ public:
}
// Enable the reference clocks that we need
+ if (_rev >= 1)
+ {
+ // set dboard clock rates to as close to the max PFD freq as possible
+ if (_iface->get_clock_rate(dboard_iface::UNIT_RX) > pfd_freq_max)
+ {
+ std::vector<double> rates = _iface->get_clock_rates(dboard_iface::UNIT_RX);
+ double highest_rate = 0.0;
+ BOOST_FOREACH(double rate, rates)
+ {
+ if (rate <= pfd_freq_max and rate > highest_rate)
+ highest_rate = rate;
+ }
+ _iface->set_clock_rate(dboard_iface::UNIT_RX, highest_rate);
+ _rx_target_pfd_freq = highest_rate;
+ }
+ if (_iface->get_clock_rate(dboard_iface::UNIT_TX) > pfd_freq_max)
+ {
+ std::vector<double> rates = _iface->get_clock_rates(dboard_iface::UNIT_TX);
+ double highest_rate = 0.0;
+ BOOST_FOREACH(double rate, rates)
+ {
+ if (rate <= pfd_freq_max and rate > highest_rate)
+ highest_rate = rate;
+ }
+ _iface->set_clock_rate(dboard_iface::UNIT_TX, highest_rate);
+ _tx_target_pfd_freq = highest_rate;
+ }
+ }
_iface->set_clock_enabled(dboard_iface::UNIT_TX, true);
_iface->set_clock_enabled(dboard_iface::UNIT_RX, true);
@@ -801,17 +338,37 @@ public:
// Initialize LOs
if (_rev == 0)
{
- _txlo1.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1)));
- _txlo2.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1)));
- _rxlo1.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1)));
- _rxlo2.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1)));
+ _txlo1 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1));
+ _txlo2 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1));
+ _rxlo1 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1));
+ _rxlo2 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1));
+ std::vector<max287x_iface::sptr> los = boost::assign::list_of(_txlo1)(_txlo2)(_rxlo1)(_rxlo2);
+ BOOST_FOREACH(max287x_iface::sptr lo, los)
+ {
+ lo->set_auto_retune(false);
+ lo->set_clock_divider_mode(max287x_iface::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF);
+ lo->set_muxout_mode(max287x_iface::MUXOUT_DLD);
+ lo->set_ld_pin_mode(max287x_iface::LD_PIN_MODE_DLD);
+ }
}
else if (_rev == 1)
{
- _txlo1.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1)));
- _txlo2.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1)));
- _rxlo1.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1)));
- _rxlo2.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1)));
+ _txlo1 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1));
+ _txlo2 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1));
+ _rxlo1 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1));
+ _rxlo2 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1));
+ std::vector<max287x_iface::sptr> los = boost::assign::list_of(_txlo1)(_txlo2)(_rxlo1)(_rxlo2);
+ BOOST_FOREACH(max287x_iface::sptr lo, los)
+ {
+ lo->set_auto_retune(false);
+ lo->set_clock_divider_mode(max287x_iface::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF);
+ //lo->set_cycle_slip_mode(true); // tried it - caused longer lock times
+ lo->set_charge_pump_current(max287x_iface::CHARGE_PUMP_CURRENT_5_12MA);
+ lo->set_muxout_mode(max287x_iface::MUXOUT_SYNC);
+ lo->set_ld_pin_mode(max287x_iface::LD_PIN_MODE_DLD);
+ lo->set_low_noise_and_spur(max287x_iface::LOW_NOISE_AND_SPUR_LOW_NOISE);
+ lo->set_phase(0);
+ }
}
else
{
@@ -819,6 +376,7 @@ public:
}
// Initialize CPLD register
+ _prev_cpld_value = 0xFFFF;
_cpld_reg.value = 0;
write_cpld_reg();
@@ -865,9 +423,9 @@ public:
get_tx_subtree()->create<bool>("use_lo_offset")
.set(false);
get_tx_subtree()->create<double>("bandwidth/value")
- .set(2*20.0e6); //20MHz low-pass, complex double-sided, so it should be 2x20MHz=40MHz
+ .set(bw);
get_tx_subtree()->create<meta_range_t>("bandwidth/range")
- .set(freq_range_t(2*20.0e6, 2*20.0e6));
+ .set(freq_range_t(bw, bw));
////////////////////////////////////////////////////////////////////
// Register RX properties
@@ -898,9 +456,9 @@ public:
get_rx_subtree()->create<bool>("use_lo_offset")
.set(false);
get_rx_subtree()->create<double>("bandwidth/value")
- .set(2*20.0e6); //20MHz low-pass, complex double-sided, so it should be 2x20MHz=40MHz
+ .set(bw);
get_rx_subtree()->create<meta_range_t>("bandwidth/range")
- .set(freq_range_t(2*20.0e6, 2*20.0e6));
+ .set(freq_range_t(bw, bw));
}
~ubx_xcvr(void)
@@ -938,14 +496,14 @@ private:
**********************************************************************/
void write_spi_reg(spi_dest_t dest, boost::uint32_t value)
{
- boost::mutex::scoped_lock lock(_spi_lock);
+ boost::mutex::scoped_lock lock(_spi_mutex);
ROUTE_SPI(_iface, dest);
WRITE_SPI(_iface, value);
}
void write_spi_regs(spi_dest_t dest, std::vector<boost::uint32_t> values)
{
- boost::mutex::scoped_lock lock(_spi_lock);
+ boost::mutex::scoped_lock lock(_spi_mutex);
ROUTE_SPI(_iface, dest);
BOOST_FOREACH(boost::uint32_t value, values)
WRITE_SPI(_iface, value);
@@ -958,7 +516,11 @@ private:
void write_cpld_reg()
{
- write_spi_reg(CPLD, _cpld_reg.value);
+ if (_cpld_reg.value != _prev_cpld_value)
+ {
+ write_spi_reg(CPLD, _cpld_reg.value);
+ _prev_cpld_value = _cpld_reg.value;
+ }
}
void set_gpio_field(ubx_gpio_field_id_t id, boost::uint32_t value)
@@ -996,7 +558,10 @@ private:
return 0;
ubx_gpio_field_info_t field_info = entry->second;
if (field_info.direction == ubx_gpio_field_info_t::INPUT)
- return 0;
+ {
+ ubx_gpio_reg_t *reg = (field_info.unit == dboard_iface::UNIT_TX ? &_tx_gpio_reg : &_rx_gpio_reg);
+ return (reg->value >> field_info.offset) & field_info.mask;
+ }
// Read register
boost::uint32_t value = _iface->read_gpio(field_info.unit);
@@ -1028,6 +593,7 @@ private:
**********************************************************************/
sensor_value_t get_locked(const std::string &pll_name)
{
+ boost::mutex::scoped_lock lock(_mutex);
assert_has(ubx_plls, pll_name, "ubx pll name");
if(pll_name == "TXLO")
@@ -1053,6 +619,7 @@ private:
// Set RX antennas
void set_rx_ant(const std::string &ant)
{
+ boost::mutex::scoped_lock lock(_mutex);
//validate input
assert_has(ubx_rx_antennas, ant, "ubx rx antenna name");
@@ -1070,6 +637,7 @@ private:
**********************************************************************/
double set_tx_gain(double gain)
{
+ boost::mutex::scoped_lock lock(_mutex);
gain = ubx_tx_gain_range.clip(gain);
int attn_code = int(std::floor(gain * 2));
_ubx_tx_atten_val = ((attn_code & 0x3F) << 10);
@@ -1082,6 +650,7 @@ private:
double set_rx_gain(double gain)
{
+ boost::mutex::scoped_lock lock(_mutex);
gain = ubx_rx_gain_range.clip(gain);
int attn_code = int(std::floor(gain * 2));
_ubx_rx_atten_val = ((attn_code & 0x3F) << 10);
@@ -1097,6 +666,7 @@ private:
**********************************************************************/
double set_tx_freq(double freq)
{
+ boost::mutex::scoped_lock lock(_mutex);
double freq_lo1 = 0.0;
double freq_lo2 = 0.0;
double ref_freq = _iface->get_clock_rate(dboard_iface::UNIT_TX);
@@ -1131,11 +701,12 @@ private:
set_cpld_field(TXLO1_FSEL1, 0);
set_cpld_field(TXLB_SEL, 1);
set_cpld_field(TXHB_SEL, 0);
- write_cpld_reg();
// Set LO1 to IF of 2100 MHz (offset from RX IF to reduce leakage)
- freq_lo1 = _txlo1->set_freq_and_power(2100*fMHz, ref_freq, is_int_n, 5);
+ freq_lo1 = _txlo1->set_frequency(2100*fMHz, ref_freq, _tx_target_pfd_freq, is_int_n);
+ _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);
// Set LO2 to IF minus desired frequency
- freq_lo2 = _txlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2);
+ freq_lo2 = _txlo2->set_frequency(freq_lo1 - freq, ref_freq, _tx_target_pfd_freq, is_int_n);
+ _txlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq >= (500*fMHz)) && (freq <= (800*fMHz)))
{
@@ -1144,8 +715,8 @@ private:
set_cpld_field(TXLO1_FSEL1, 1);
set_cpld_field(TXLB_SEL, 0);
set_cpld_field(TXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2);
+ freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n);
+ _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq > (800*fMHz)) && (freq <= (1000*fMHz)))
{
@@ -1154,8 +725,8 @@ private:
set_cpld_field(TXLO1_FSEL1, 1);
set_cpld_field(TXLB_SEL, 0);
set_cpld_field(TXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5);
+ freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n);
+ _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);
}
else if ((freq > (1000*fMHz)) && (freq <= (2200*fMHz)))
{
@@ -1164,8 +735,8 @@ private:
set_cpld_field(TXLO1_FSEL1, 0);
set_cpld_field(TXLB_SEL, 0);
set_cpld_field(TXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2);
+ freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n);
+ _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq > (2200*fMHz)) && (freq <= (2500*fMHz)))
{
@@ -1174,8 +745,8 @@ private:
set_cpld_field(TXLO1_FSEL1, 0);
set_cpld_field(TXLB_SEL, 0);
set_cpld_field(TXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2);
+ freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n);
+ _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq > (2500*fMHz)) && (freq <= (6000*fMHz)))
{
@@ -1184,8 +755,50 @@ private:
set_cpld_field(TXLO1_FSEL1, 0);
set_cpld_field(TXLB_SEL, 0);
set_cpld_field(TXHB_SEL, 1);
+ freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n);
+ _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);
+ }
+
+ // To reduce the number of commands issued to the device, write to the
+ // SPI destination already addressed first. This avoids the writes to
+ // the GPIO registers to route the SPI to the same destination.
+ switch (get_gpio_field(SPI_ADDR))
+ {
+ case TXLO1:
+ _txlo1->commit();
+ if (freq < (500*fMHz)) _txlo2->commit();
+ write_cpld_reg();
+ break;
+ case TXLO2:
+ if (freq < (500*fMHz)) _txlo2->commit();
+ _txlo1->commit();
write_cpld_reg();
- freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5);
+ break;
+ default:
+ write_cpld_reg();
+ _txlo1->commit();
+ if (freq < (500*fMHz)) _txlo2->commit();
+ break;
+ }
+
+ if (_txlo1->can_sync())
+ {
+ // Send phase sync signal only if the command time is set
+ uhd::time_spec_t cmd_time = _iface->get_command_time();
+ if (cmd_time != uhd::time_spec_t(0.0))
+ {
+ // Delay 400 microseconds to allow LOs to lock
+ cmd_time += uhd::time_spec_t(0.0004);
+ _iface->set_command_time(cmd_time);
+ set_gpio_field(TXLO1_SYNC, 1);
+ set_gpio_field(TXLO2_SYNC, 1);
+ write_gpio();
+ // De-assert SYNC
+ // Head of line blocking means the time does not need to be set.
+ set_gpio_field(TXLO1_SYNC, 0);
+ set_gpio_field(TXLO2_SYNC, 0);
+ write_gpio();
+ }
}
_tx_freq = freq_lo1 - freq_lo2;
@@ -1199,6 +812,7 @@ private:
double set_rx_freq(double freq)
{
+ boost::mutex::scoped_lock lock(_mutex);
double freq_lo1 = 0.0;
double freq_lo2 = 0.0;
double ref_freq = _iface->get_clock_rate(dboard_iface::UNIT_RX);
@@ -1231,11 +845,12 @@ private:
set_cpld_field(RXLO1_FSEL1, 0);
set_cpld_field(RXLB_SEL, 1);
set_cpld_field(RXHB_SEL, 0);
- write_cpld_reg();
// Set LO1 to IF of 2380 MHz (2440 MHz filter center minus 60 MHz offset to minimize LO leakage)
- freq_lo1 = _rxlo1->set_freq_and_power(2380*fMHz, ref_freq, is_int_n, 5);
+ freq_lo1 = _rxlo1->set_frequency(2380*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);
// Set LO2 to IF minus desired frequency
- freq_lo2 = _rxlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2);
+ freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq >= 100*fMHz) && (freq < 500*fMHz))
{
@@ -1246,11 +861,12 @@ private:
set_cpld_field(RXLO1_FSEL1, 0);
set_cpld_field(RXLB_SEL, 1);
set_cpld_field(RXHB_SEL, 0);
- write_cpld_reg();
// Set LO1 to IF of 2440 (center of filter)
- freq_lo1 = _rxlo1->set_freq_and_power(2440*fMHz, ref_freq, is_int_n, 5);
+ freq_lo1 = _rxlo1->set_frequency(2440*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);
// Set LO2 to IF minus desired frequency
- freq_lo2 = _rxlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2);
+ freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq >= 500*fMHz) && (freq < 800*fMHz))
{
@@ -1261,8 +877,8 @@ private:
set_cpld_field(RXLO1_FSEL1, 1);
set_cpld_field(RXLB_SEL, 0);
set_cpld_field(RXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2);
+ freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq >= 800*fMHz) && (freq < 1000*fMHz))
{
@@ -1273,8 +889,8 @@ private:
set_cpld_field(RXLO1_FSEL1, 1);
set_cpld_field(RXLB_SEL, 0);
set_cpld_field(RXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5);
+ freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);
}
else if ((freq >= 1000*fMHz) && (freq < 1500*fMHz))
{
@@ -1285,8 +901,8 @@ private:
set_cpld_field(RXLO1_FSEL1, 0);
set_cpld_field(RXLB_SEL, 0);
set_cpld_field(RXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2);
+ freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq >= 1500*fMHz) && (freq < 2200*fMHz))
{
@@ -1297,8 +913,8 @@ private:
set_cpld_field(RXLO1_FSEL1, 0);
set_cpld_field(RXLB_SEL, 0);
set_cpld_field(RXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2);
+ freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq >= 2200*fMHz) && (freq < 2500*fMHz))
{
@@ -1309,8 +925,8 @@ private:
set_cpld_field(RXLO1_FSEL1, 0);
set_cpld_field(RXLB_SEL, 0);
set_cpld_field(RXHB_SEL, 1);
- write_cpld_reg();
- freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2);
+ freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);
}
else if ((freq >= 2500*fMHz) && (freq <= 6000*fMHz))
{
@@ -1321,15 +937,59 @@ private:
set_cpld_field(RXLO1_FSEL1, 0);
set_cpld_field(RXLB_SEL, 0);
set_cpld_field(RXHB_SEL, 1);
+ freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n);
+ _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);
+ }
+
+ // To reduce the number of commands issued to the device, write to the
+ // SPI destination already addressed first. This avoids the writes to
+ // the GPIO registers to route the SPI to the same destination.
+ switch (get_gpio_field(SPI_ADDR))
+ {
+ case RXLO1:
+ _rxlo1->commit();
+ if (freq < (500*fMHz)) _rxlo2->commit();
+ write_cpld_reg();
+ break;
+ case RXLO2:
+ if (freq < (500*fMHz)) _rxlo2->commit();
+ _rxlo1->commit();
+ write_cpld_reg();
+ break;
+ default:
write_cpld_reg();
- freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5);
+ _rxlo1->commit();
+ if (freq < (500*fMHz)) _rxlo2->commit();
+ break;
+ }
+
+ if (_rxlo1->can_sync())
+ {
+ // Send phase sync signal only if the command time is set
+ uhd::time_spec_t cmd_time = _iface->get_command_time();
+ if (cmd_time != uhd::time_spec_t(0.0))
+ {
+ // Delay 400 microseconds to allow LOs to lock
+ cmd_time += uhd::time_spec_t(0.0004);
+ _iface->set_command_time(cmd_time);
+ set_gpio_field(RXLO1_SYNC, 1);
+ set_gpio_field(RXLO2_SYNC, 1);
+ write_gpio();
+ // De-assert SYNC
+ // Head of line blocking means the time does not need to be set.
+ set_gpio_field(RXLO1_SYNC, 0);
+ set_gpio_field(RXLO2_SYNC, 0);
+ write_gpio();
+ }
}
- freq = freq_lo1 - freq_lo2;
+ _rx_freq = freq_lo1 - freq_lo2;
+ _rxlo1_freq = freq_lo1;
+ _rxlo2_freq = freq_lo2;
- UHD_LOGV(rarely) << boost::format("UBX RX: the actual frequency is %f MHz") % (freq/1e6) << std::endl;
+ UHD_LOGV(rarely) << boost::format("UBX RX: the actual frequency is %f MHz") % (_rx_freq/1e6) << std::endl;
- return freq;
+ return _rx_freq;
}
/***********************************************************************
@@ -1337,6 +997,7 @@ private:
**********************************************************************/
void set_power_mode(std::string mode)
{
+ boost::mutex::scoped_lock lock(_mutex);
if (mode == "performance")
{
// FIXME: Response to ATR change is too slow for some components,
@@ -1392,12 +1053,16 @@ private:
* Variables
**********************************************************************/
dboard_iface::sptr _iface;
- boost::mutex _spi_lock;
+ boost::mutex _spi_mutex;
+ boost::mutex _mutex;
ubx_cpld_reg_t _cpld_reg;
- boost::shared_ptr<max287x_synthesizer_iface> _txlo1;
- boost::shared_ptr<max287x_synthesizer_iface> _txlo2;
- boost::shared_ptr<max287x_synthesizer_iface> _rxlo1;
- boost::shared_ptr<max287x_synthesizer_iface> _rxlo2;
+ boost::uint32_t _prev_cpld_value;
+ boost::shared_ptr<max287x_iface> _txlo1;
+ boost::shared_ptr<max287x_iface> _txlo2;
+ boost::shared_ptr<max287x_iface> _rxlo1;
+ boost::shared_ptr<max287x_iface> _rxlo2;
+ double _tx_target_pfd_freq;
+ double _rx_target_pfd_freq;
double _tx_gain;
double _rx_gain;
double _tx_freq;
@@ -1431,8 +1096,8 @@ static dboard_base::sptr make_ubx(dboard_base::ctor_args_t args)
UHD_STATIC_BLOCK(reg_ubx_dboards)
{
- dboard_manager::register_dboard(0x0074, 0x0073, &make_ubx, "UBX v0.3");
- dboard_manager::register_dboard(0x0076, 0x0075, &make_ubx, "UBX v0.4");
- dboard_manager::register_dboard(0x0078, 0x0077, &make_ubx, "UBX-40 v1");
- dboard_manager::register_dboard(0x007a, 0x0079, &make_ubx, "UBX-160 v1");
+ dboard_manager::register_dboard(UBX_PROTO_V3_RX_ID, UBX_PROTO_V3_TX_ID, &make_ubx, "UBX v0.3");
+ dboard_manager::register_dboard(UBX_PROTO_V4_RX_ID, UBX_PROTO_V4_TX_ID, &make_ubx, "UBX v0.4");
+ dboard_manager::register_dboard(UBX_V1_40MHZ_RX_ID, UBX_V1_40MHZ_TX_ID, &make_ubx, "UBX-40 v1");
+ dboard_manager::register_dboard(UBX_V1_160MHZ_RX_ID, UBX_V1_160MHZ_TX_ID, &make_ubx, "UBX-160 v1");
}
diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp
index f80aeda77..81cdaefac 100644
--- a/host/lib/usrp/dboard/db_wbx_version4.cpp
+++ b/host/lib/usrp/dboard/db_wbx_version4.cpp
@@ -63,7 +63,7 @@ static int tx_pga0_gain_to_iobits(double &gain){
(attn_code & 8 ? 0 : TX_ATTN_8) |
(attn_code & 4 ? 0 : TX_ATTN_4) |
(attn_code & 2 ? 0 : TX_ATTN_2) |
- (attn_code & 1 ? 0 : TX_ATTN_1)
+ (attn_code & 1 ? 0 : TX_ATTN_1)
) & TX_ATTN_MASK;
UHD_LOGV(often) << boost::format(
@@ -220,8 +220,8 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar
//map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler)
static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of
- (0,23) //adf4351_regs_t::PRESCALER_4_5
- (1,75) //adf4351_regs_t::PRESCALER_8_9
+ (adf4351_regs_t::PRESCALER_4_5, 23)
+ (adf4351_regs_t::PRESCALER_8_9, 75)
;
//map rf divider select output dividers to enums
@@ -237,14 +237,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar
double reference_freq = self_base->get_iface()->get_clock_rate(unit);
//The mixer has a divide-by-2 stage on the LO port so the synthesizer
- //frequency must 2x the target frequency
+ //frequency must 2x the target frequency. This introduces a 180 degree phase
+ //ambiguity when trying to synchronize the phase of multiple boards.
double synth_target_freq = target_freq * 2;
- //TODO: Document why the following has to be true
- bool div_resync_enabled = (target_freq > reference_freq);
adf4351_regs_t::prescaler_t prescaler =
- synth_target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5;
-
+ synth_target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5;
+
adf435x_tuning_constraints tuning_constraints;
tuning_constraints.force_frac0 = is_int_n;
tuning_constraints.band_sel_freq_max = 100e3;
@@ -252,9 +251,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar
tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095);
tuning_constraints.pfd_freq_max = 25e6;
tuning_constraints.rf_divider_range = uhd::range_t(1, 64);
- //When divider resync is enabled, a 180 deg phase error is introduced when syncing
- //multiple WBX boards. Switching to fundamental mode works arounds this issue.
- tuning_constraints.feedback_after_divider = div_resync_enabled;
+ //The feedback of the divided frequency must be disabled whenever the target frequency
+ //divided by the minimum PFD frequency cannot meet the minimum integer divider (N) value.
+ //If it is disabled, additional phase ambiguity will be introduced. With a minimum PFD
+ //frequency of 10 MHz, synthesizer frequencies below 230 MHz (LO frequencies below 115 MHz)
+ //will have too much ambiguity to synchronize.
+ tuning_constraints.feedback_after_divider =
+ (int(synth_target_freq / 10e6) >= prescaler_to_min_int_div[prescaler]);
double synth_actual_freq = 0;
adf435x_tuning_settings tuning_settings = tune_adf435x_synth(
@@ -281,7 +284,7 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar
regs.feedback_select = tuning_constraints.feedback_after_divider ?
adf4351_regs_t::FEEDBACK_SELECT_DIVIDED :
adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL;
- regs.clock_div_mode = div_resync_enabled ?
+ regs.clock_div_mode = tuning_constraints.feedback_after_divider ?
adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE :
adf4351_regs_t::CLOCK_DIV_MODE_FAST_LOCK;
regs.prescaler = prescaler;
diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp
index 2e922a3e2..8ba7b54ca 100644
--- a/host/lib/usrp/usrp1/codec_ctrl.cpp
+++ b/host/lib/usrp/usrp1/codec_ctrl.cpp
@@ -357,7 +357,7 @@ double usrp1_codec_ctrl_impl::fine_tune(double codec_rate, double target_freq)
static const double scale_factor = std::pow(2.0, 24);
boost::uint32_t freq_word = boost::uint32_t(
- boost::math::round(abs((target_freq / codec_rate) * scale_factor)));
+ boost::math::round(std::abs((target_freq / codec_rate) * scale_factor)));
double actual_freq = freq_word * codec_rate / scale_factor;
diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h
index 337a1ad6f..cfaee0ddc 100644
--- a/host/lib/usrp/usrp2/fw_common.h
+++ b/host/lib/usrp/usrp2/fw_common.h
@@ -30,7 +30,7 @@ extern "C" {
#endif
//fpga and firmware compatibility numbers
-#define USRP2_FPGA_COMPAT_NUM 10
+#define USRP2_FPGA_COMPAT_NUM 11
#define USRP2_FW_COMPAT_NUM 12
#define USRP2_FW_VER_MINOR 4
diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp
index 0276a7a66..e0544862d 100644
--- a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp
+++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp
@@ -33,7 +33,7 @@ static const size_t POKE32_CMD = (1 << 8);
static const size_t PEEK32_CMD = 0;
static const double ACK_TIMEOUT = 0.5;
static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command
-static const boost::uint32_t MAX_SEQS_OUT = 15;
+static const boost::uint32_t MAX_SEQS_OUT = 63;
#define SPI_DIV SR_SPI_CORE + 0
#define SPI_CTRL SR_SPI_CORE + 1
diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp
index 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)
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index 9042ad2ca..c27133745 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -70,13 +70,13 @@ static const size_t X300_PCIE_RX_DATA_FRAME_SIZE = 8184; //bytes
static const size_t X300_PCIE_TX_DATA_FRAME_SIZE = 8192; //bytes
static const size_t X300_PCIE_DATA_NUM_FRAMES = 2048;
static const size_t X300_PCIE_MSG_FRAME_SIZE = 256; //bytes
-static const size_t X300_PCIE_MSG_NUM_FRAMES = 32;
+static const size_t X300_PCIE_MSG_NUM_FRAMES = 64;
static const size_t X300_10GE_DATA_FRAME_MAX_SIZE = 8000; //bytes
static const size_t X300_1GE_DATA_FRAME_MAX_SIZE = 1472; //bytes
static const size_t X300_ETH_MSG_FRAME_SIZE = uhd::transport::udp_simple::mtu; //bytes
-static const size_t X300_ETH_MSG_NUM_FRAMES = 32;
+static const size_t X300_ETH_MSG_NUM_FRAMES = 64;
static const size_t X300_ETH_DATA_NUM_FRAMES = 32;
static const double X300_DEFAULT_SYSREF_RATE = 10e6;