aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/lib/usrp/dboard/db_ubx.cpp18
-rw-r--r--host/lib/usrp/dboard/db_ubx.hpp57
-rw-r--r--host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp51
-rw-r--r--host/lib/usrp/x300/x300_device_args.hpp7
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp62
5 files changed, 153 insertions, 42 deletions
diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp
index 8ac96a2fe..733a4b8e2 100644
--- a/host/lib/usrp/dboard/db_ubx.cpp
+++ b/host/lib/usrp/dboard/db_ubx.cpp
@@ -7,6 +7,7 @@
/***********************************************************************
* Included Files and Libraries
**********************************************************************/
+#include "db_ubx.hpp"
#include <uhd/types/device_addr.hpp>
#include <uhd/types/dict.hpp>
#include <uhd/types/direction.hpp>
@@ -30,6 +31,7 @@
using namespace uhd;
using namespace uhd::usrp;
+using namespace uhd::usrp::dboard::ubx;
/***********************************************************************
* UBX Data Structures
@@ -134,22 +136,6 @@ enum spi_dest_t {
* UBX Constants
**********************************************************************/
#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 dboard_id_t UBX_V2_40MHZ_TX_ID(0x7B);
-static const dboard_id_t UBX_V2_40MHZ_RX_ID(0x7C);
-static const dboard_id_t UBX_V2_160MHZ_TX_ID(0x7D);
-static const dboard_id_t UBX_V2_160MHZ_RX_ID(0x7E);
-static const dboard_id_t UBX_LP_160MHZ_TX_ID(0x0200);
-static const dboard_id_t UBX_LP_160MHZ_RX_ID(0x0201);
-static const dboard_id_t UBX_TDD_160MHZ_TX_ID(0x0202);
-static const dboard_id_t UBX_TDD_160MHZ_RX_ID(0x0203);
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));
diff --git a/host/lib/usrp/dboard/db_ubx.hpp b/host/lib/usrp/dboard/db_ubx.hpp
new file mode 100644
index 000000000..246a89eb0
--- /dev/null
+++ b/host/lib/usrp/dboard/db_ubx.hpp
@@ -0,0 +1,57 @@
+//
+// Copyright 2020 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#pragma once
+
+#include <uhd/usrp/dboard_base.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+namespace uhd { namespace usrp { namespace dboard { namespace ubx {
+
+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 dboard_id_t UBX_V2_40MHZ_TX_ID(0x7B);
+static const dboard_id_t UBX_V2_40MHZ_RX_ID(0x7C);
+static const dboard_id_t UBX_V2_160MHZ_TX_ID(0x7D);
+static const dboard_id_t UBX_V2_160MHZ_RX_ID(0x7E);
+static const dboard_id_t UBX_LP_160MHZ_TX_ID(0x0200);
+static const dboard_id_t UBX_LP_160MHZ_RX_ID(0x0201);
+static const dboard_id_t UBX_TDD_160MHZ_TX_ID(0x0202);
+static const dboard_id_t UBX_TDD_160MHZ_RX_ID(0x0203);
+static const std::vector<dboard_id_t> ubx_ids{UBX_PROTO_V3_TX_ID,
+ UBX_PROTO_V4_TX_ID,
+ UBX_V1_40MHZ_TX_ID,
+ UBX_V1_160MHZ_TX_ID,
+ UBX_V2_40MHZ_TX_ID,
+ UBX_V2_160MHZ_TX_ID,
+ UBX_LP_160MHZ_TX_ID,
+ UBX_TDD_160MHZ_TX_ID,
+ UBX_PROTO_V3_RX_ID,
+ UBX_PROTO_V4_RX_ID,
+ UBX_V1_40MHZ_RX_ID,
+ UBX_V1_160MHZ_RX_ID,
+ UBX_V2_40MHZ_RX_ID,
+ UBX_V2_160MHZ_RX_ID,
+ UBX_LP_160MHZ_RX_ID,
+ UBX_TDD_160MHZ_RX_ID};
+
+static UHD_INLINE double get_max_pfd_freq(dboard_id_t dboard_id)
+{
+ if ((dboard_id == UBX_PROTO_V3_TX_ID) || (dboard_id == UBX_PROTO_V3_RX_ID)) {
+ return 25e6;
+ }
+ return 50e6;
+}
+
+}}}}; // namespace uhd::usrp::dboard::ubx
diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp
index 3752a2e70..3686f4348 100644
--- a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp
+++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp
@@ -13,6 +13,7 @@
#include <uhdlib/usrp/common/adf535x.hpp>
#include <uhdlib/utils/narrow.hpp>
#include <chrono>
+#include <cmath>
#include <thread>
using namespace uhd;
@@ -29,10 +30,9 @@ inline uint32_t bool2bin(bool x)
return x ? 1 : 0;
}
-const double TWINRX_DESIRED_REFERENCE_FREQ = 50e6;
-const double TWINRX_REV_AB_PFD_FREQ = 6.25e6;
-const double TWINRX_REV_C_PFD_FREQ = 12.5e6;
-const double TWINRX_SPI_CLOCK_FREQ = 3e6;
+const double TWINRX_REV_AB_PFD_FREQ = 6.25e6;
+const double TWINRX_REV_C_PFD_FREQ = 12.5e6;
+const double TWINRX_SPI_CLOCK_FREQ = 3e6;
} // namespace
class twinrx_ctrl_impl : public twinrx_ctrl
@@ -49,22 +49,29 @@ public:
_spi_config.divider = uhd::narrow_cast<size_t>(std::ceil(
_db_iface->get_codec_rate(dboard_iface::UNIT_TX) / TWINRX_SPI_CLOCK_FREQ));
- // Initialize dboard clocks
- bool found_rate = false;
- for (double rate : _db_iface->get_clock_rates(dboard_iface::UNIT_TX)) {
- found_rate |=
- uhd::math::frequencies_are_equal(rate, TWINRX_DESIRED_REFERENCE_FREQ);
- }
- for (double rate : _db_iface->get_clock_rates(dboard_iface::UNIT_RX)) {
- found_rate |=
- uhd::math::frequencies_are_equal(rate, TWINRX_DESIRED_REFERENCE_FREQ);
- }
- if (not found_rate) {
- throw uhd::runtime_error("TwinRX not supported on this motherboard");
+ // Daughterboard clock rates must be a multiple of the pfd frequency
+ if (rx_id == twinrx::TWINRX_REV_C_ID) {
+ if (fmod(_db_iface->get_clock_rate(dboard_iface::UNIT_RX),
+ TWINRX_REV_C_PFD_FREQ)
+ != 0) {
+ throw uhd::value_error(
+ str(boost::format(
+ "TwinRX clock rate %f is not a multiple of the pfd freq %f.")
+ % _db_iface->get_clock_rate(dboard_iface::UNIT_RX)
+ % TWINRX_REV_C_PFD_FREQ));
+ }
+ } else {
+ if (fmod(_db_iface->get_clock_rate(dboard_iface::UNIT_RX),
+ TWINRX_REV_AB_PFD_FREQ)
+ != 0) {
+ throw uhd::value_error(
+ str(boost::format(
+ "TwinRX clock rate %f is not a multiple of the pfd freq %f.")
+ % _db_iface->get_clock_rate(dboard_iface::UNIT_RX)
+ % TWINRX_REV_AB_PFD_FREQ));
+ }
}
- _db_iface->set_clock_rate(dboard_iface::UNIT_TX, TWINRX_DESIRED_REFERENCE_FREQ);
- _db_iface->set_clock_rate(dboard_iface::UNIT_RX, TWINRX_DESIRED_REFERENCE_FREQ);
-
+ // Initialize dboard clocks
_db_iface->set_clock_enabled(dboard_iface::UNIT_TX, true);
_db_iface->set_clock_enabled(dboard_iface::UNIT_RX, true);
@@ -126,7 +133,8 @@ public:
_lo1_iface[i]->set_pfd_freq(TWINRX_REV_AB_PFD_FREQ);
}
_lo1_iface[i]->set_output_power(adf535x_iface::OUTPUT_POWER_5DBM);
- _lo1_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ);
+ _lo1_iface[i]->set_reference_freq(
+ _db_iface->get_clock_rate(dboard_iface::UNIT_TX));
_lo1_iface[i]->set_muxout_mode(adf535x_iface::MUXOUT_DLD);
_lo1_iface[i]->set_frequency(3e9, 1.0e3);
@@ -137,7 +145,8 @@ public:
});
_lo2_iface[i]->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED);
_lo2_iface[i]->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM);
- _lo2_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ);
+ _lo2_iface[i]->set_reference_freq(
+ _db_iface->get_clock_rate(dboard_iface::UNIT_RX));
_lo2_iface[i]->set_muxout_mode(adf435x_iface::MUXOUT_DLD);
_lo2_iface[i]->set_tuning_mode(adf435x_iface::TUNING_MODE_LOW_SPUR);
_lo2_iface[i]->set_prescaler(adf435x_iface::PRESCALER_8_9);
diff --git a/host/lib/usrp/x300/x300_device_args.hpp b/host/lib/usrp/x300/x300_device_args.hpp
index 6c6680c4f..7e17c36ce 100644
--- a/host/lib/usrp/x300/x300_device_args.hpp
+++ b/host/lib/usrp/x300/x300_device_args.hpp
@@ -197,12 +197,11 @@ private:
if (dev_args.has_key(_dboard_clock_rate.key())) {
_dboard_clock_rate.parse(dev_args[_dboard_clock_rate.key()]);
} else {
- // Some daughterboards may require other rates, but this default
- // works best for all newer daughterboards (i.e. CBX, WBX, SBX,
- // UBX, and TwinRX).
+ // This default clock rate works best for most daughterboards (i.e. DBSRX2,
+ // WBX, SBX, CBX, and TwinRX).
if (_master_clock_rate.get() >= MIN_TICK_RATE
&& _master_clock_rate.get() <= MAX_TICK_RATE) {
- _dboard_clock_rate.set(_master_clock_rate.get() / 4);
+ _dboard_clock_rate.set(_master_clock_rate.get() / 2);
} else {
throw uhd::value_error("Can't infer daughterboard clock rate. Specify "
"dboard_clk_rate in the device args.");
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index a414fd0f1..fa8425fac 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -7,6 +7,7 @@
//
#include "x300_impl.hpp"
+#include "../dboard/db_ubx.hpp"
#include "x300_claim.hpp"
#include "x300_eth_mgr.hpp"
#include "x300_mb_controller.hpp"
@@ -15,6 +16,8 @@
#include "x300_mboard_type.hpp"
#include "x300_pcie_mgr.hpp"
#include <uhd/transport/if_addrs.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/usrp/dboard_id.hpp>
#include <uhd/utils/log.hpp>
#include <uhd/utils/paths.hpp>
#include <uhd/utils/safe_call.hpp>
@@ -141,6 +144,31 @@ device_addrs_t x300_find(const device_addr_t& hint_)
}
/***********************************************************************
+ * Daughterboard detection before initialization in software
+ **********************************************************************/
+static std::vector<dboard_id_t> get_dboard_ids(uhd::i2c_iface& zpu_i2c)
+{
+ std::vector<dboard_id_t> dboard_ids;
+ // Read dboard ids from the EEPROM
+ constexpr size_t BASE_ADDR = 0x50;
+ constexpr size_t RX_EEPROM_ADDR = 0x5;
+ constexpr size_t TX_EEPROM_ADDR = 0x4;
+ static const std::vector<size_t> DB_OFFSETS{0x0, 0x2};
+ static const std::vector<size_t> EEPROM_ADDRS{RX_EEPROM_ADDR, TX_EEPROM_ADDR};
+ for (size_t eeprom_addr : EEPROM_ADDRS) {
+ for (size_t db_offset : DB_OFFSETS) {
+ const size_t addr = eeprom_addr + db_offset;
+ // Load EEPROM
+ std::unordered_map<size_t, usrp::dboard_eeprom_t> db_eeproms;
+ db_eeproms[addr].load(zpu_i2c, BASE_ADDR | addr);
+ uint16_t dboard_id = db_eeproms[addr].id.to_uint16();
+ dboard_ids.push_back(static_cast<dboard_id_t>(dboard_id));
+ }
+ }
+ return dboard_ids;
+}
+
+/***********************************************************************
* Make
**********************************************************************/
static device::sptr x300_make(const device_addr_t& device_addr)
@@ -331,13 +359,45 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
// create clock control objects
////////////////////////////////////////////////////////////////////
UHD_LOGGER_DEBUG("X300") << "Setting up RF frontend clocking...";
+
+ // The default daughterboard clock rate may have to be overridden. This is due to the
+ // limitation on X300 devices where both daughterboards must use the same clock rate.
+ // The daughterboards that require specific clock rates are UBX and TwinRX. TwinRX
+ // requires a clock rate of 100 MHz for the best RF performance. UBX daughterboards
+ // require a clock rate of no more than the max pfd frequency to maintain phase
+ // synchronization. If there is no UBX, the default daughterboard clock rate is half
+ // of the master clock rate for X300.
+ const double x300_dboard_clock_rate = [this, dev_addr, mb]() -> double {
+ // Do not override use-specified dboard clock rates
+ if (dev_addr.has_key("dboard_clock_rate")) {
+ return mb.args.get_dboard_clock_rate();
+ }
+ const double mcr = mb.args.get_master_clock_rate();
+ double dboard_clock_rate = mb.args.get_dboard_clock_rate();
+ // Check for UBX daughterboards
+ std::vector<dboard_id_t> dboard_ids = get_dboard_ids(*mb.zpu_i2c);
+ for (dboard_id_t dboard_id : dboard_ids) {
+ if (std::find(
+ dboard::ubx::ubx_ids.begin(), dboard::ubx::ubx_ids.end(), dboard_id)
+ != dboard::ubx::ubx_ids.end()) {
+ double ubx_clock_rate = mcr;
+ for (int i = 2; ubx_clock_rate > dboard::ubx::get_max_pfd_freq(dboard_id);
+ i++) {
+ ubx_clock_rate = mcr / i;
+ }
+ dboard_clock_rate = std::min(dboard_clock_rate, ubx_clock_rate);
+ }
+ }
+ return dboard_clock_rate;
+ }();
+
// Initialize clock control registers.
// NOTE: This does not configure the LMK yet.
mb.clock = x300_clock_ctrl::make(mb.zpu_spi,
1 /*slaveno*/,
mb.hw_rev,
mb.args.get_master_clock_rate(),
- mb.args.get_dboard_clock_rate(),
+ x300_dboard_clock_rate,
mb.args.get_system_ref_rate());
////////////////////////////////////////////////////////////////////