From b75ca6cd9143a730d827c3b5e353a9086a79d210 Mon Sep 17 00:00:00 2001
From: Michael West <michael.west@ettus.com>
Date: Tue, 10 Apr 2018 12:25:29 -0700
Subject: UBX: Add support for phase synchronization at LTE clock rates

---
 host/lib/usrp/dboard/db_ubx.cpp  | 30 ++++++++++++++++++------------
 host/lib/usrp/x300/x300_impl.cpp | 17 ++++++++++++++++-
 host/lib/usrp/x300/x300_regs.hpp | 12 ++++++++++++
 3 files changed, 46 insertions(+), 13 deletions(-)

(limited to 'host/lib')

diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp
index c65b5f470..6f2670ec4 100644
--- a/host/lib/usrp/dboard/db_ubx.cpp
+++ b/host/lib/usrp/dboard/db_ubx.cpp
@@ -488,7 +488,7 @@ public:
             .set(freq_range_t(bw, bw));
         get_tx_subtree()->create<int64_t>("sync_delay")
             .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_sync_delay, this, true, _1))
-            .set(-8);
+            .set(0);
 
         ////////////////////////////////////////////////////////////////////
         // Register RX properties
@@ -524,7 +524,7 @@ public:
             .set(freq_range_t(bw, bw));
         get_rx_subtree()->create<int64_t>("sync_delay")
             .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_sync_delay, this, false, _1))
-            .set(-8);
+            .set(0);
     }
 
     virtual ~ubx_xcvr(void)
@@ -669,20 +669,26 @@ private:
             // Phase synchronization for MAX2871 requires that the sync signal
             // is at least 4/(N*PFD_freq) + 2.6ns before the rising edge of the
             // ref clock and 4/(N*PFD_freq) after the rising edge of the ref clock.
-            // Since the ref clock, the radio clock, and the VITA time are all
-            // synchronized to the 10 MHz clock, use the time spec to move
-            // the rising edge of the sync signal away from the 10 MHz edge,
-            // which will move it away from the ref clock edge by the same amount.
             // Since the MAX2871 requires the ref freq and PFD freq be the same
             // for phase synchronization, the dboard clock rate is used as the PFD
-            // freq and the worst case value of 20 is used for the N value to
-            // calculate the offset.
-            double pfd_freq = _iface->get_clock_rate(dir == TX_DIRECTION ? dboard_iface::UNIT_TX : dboard_iface::UNIT_RX);
-            double tick_rate = _iface->get_codec_rate(dir == TX_DIRECTION ? dboard_iface::UNIT_TX : dboard_iface::UNIT_RX);
+            // freq and the sync signal is aligned to the falling edge to meet
+            // the setup and hold requirements.  Since the command time ticks
+            // at the radio clock rate, this only works if the radio clock is
+            // an even multiple of the dboard clock, the dboard clock is a
+            // multiple of the system reference, and the device time has been
+            // set on a PPS edge sampled by the system reference clock.
+
+            const double pfd_freq = _iface->get_clock_rate(dir == TX_DIRECTION ? dboard_iface::UNIT_TX : dboard_iface::UNIT_RX);
+            const double tick_rate = _iface->get_codec_rate(dir == TX_DIRECTION ? dboard_iface::UNIT_TX : dboard_iface::UNIT_RX);
+            const int64_t ticks_per_pfd_cycle = (int64_t)(tick_rate / pfd_freq);
+
+            // Convert time to ticks
             int64_t ticks = cmd_time.to_ticks(tick_rate);
-            ticks -= ticks % (int64_t)(tick_rate / 10e6);            // align to 10 MHz clock
+            // Align time to next falling edge of dboard clock
+            ticks += ticks_per_pfd_cycle - (ticks % ticks_per_pfd_cycle) + (ticks_per_pfd_cycle / 2);
+            // Add any user specified delay
             ticks += dir == TX_DIRECTION ? _tx_sync_delay : _rx_sync_delay;
-            ticks += std::ceil(tick_rate*4/(20*pfd_freq));  // add required offset (using worst case N value of 20)
+            // Set the command time
             cmd_time = uhd::time_spec_t::from_ticks(ticks, tick_rate);
             _iface->set_command_time(cmd_time);
 
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index 4a8c12e04..051242e0a 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -888,12 +888,27 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
 
     //Initialize clock control registers. NOTE: This does not configure the LMK yet.
     const double requested_mcr = dev_addr.cast<double>("master_clock_rate", X300_DEFAULT_TICK_RATE);
+    //Some daughterboards may require other rates, but these defaults
+    //work best for all newer daughterboards (i.e. CBX, WBX, SBX, UBX,
+    //and TwinRX).
+    double default_dboard_clk_rate = requested_mcr;
+    if (math::frequencies_are_equal(requested_mcr, 200e6)) {
+        default_dboard_clk_rate = 50e6;
+    } else if (math::frequencies_are_equal(requested_mcr, 184.32e6)) {
+        default_dboard_clk_rate = 46.08e6;
+    } else if (math::frequencies_are_equal(requested_mcr, 120e6)) {
+        default_dboard_clk_rate = 40e6;
+    }
     mb.clock = x300_clock_ctrl::make(mb.zpu_spi,
         1 /*slaveno*/,
         mb.hw_rev,
         requested_mcr,
-        dev_addr.cast<double>("dboard_clock_rate", requested_mcr / 4),
+        dev_addr.cast<double>("dboard_clock_rate", default_dboard_clk_rate),
         dev_addr.cast<double>("system_ref_rate", X300_DEFAULT_SYSREF_RATE));
+    mb.fw_regmap->ref_freq_reg.write(
+        fw_regmap_t::ref_freq_reg_t::REF_FREQ,
+        uint32_t(dev_addr.cast<double>("system_ref_rate",
+        X300_DEFAULT_SYSREF_RATE)));
 
     //Initialize clock source to use internal reference and generate
     //a valid radio clock. This may change after configuration is done.
diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp
index 68b405dfe..b54c5c2bb 100644
--- a/host/lib/usrp/x300/x300_regs.hpp
+++ b/host/lib/usrp/x300/x300_regs.hpp
@@ -30,6 +30,7 @@ static const int ZPU_SR_LEDS       = 00;
 static const int ZPU_SR_SW_RST     = 01;
 static const int ZPU_SR_CLOCK_CTRL = 02;
 static const int ZPU_SR_XB_LOCAL   = 03;
+static const int ZPU_SR_REF_FREQ   = 04;
 static const int ZPU_SR_SPI        = 32;
 static const int ZPU_SR_ETHINT0    = 40;
 static const int ZPU_SR_ETHINT1    = 56;
@@ -204,9 +205,20 @@ namespace uhd { namespace usrp { namespace x300 {
             clk_status_reg_t(): uhd::soft_reg32_ro_t(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) {}
         } clock_status_reg;
 
+        class ref_freq_reg_t : public uhd::soft_reg32_wo_t {
+        public:
+            UHD_DEFINE_SOFT_REG_FIELD(REF_FREQ,   /*width*/ 32, /*shift*/ 0);
+
+            ref_freq_reg_t(): uhd::soft_reg32_wo_t(SR_ADDR(SET0_BASE, ZPU_SR_REF_FREQ)) {
+                //Initial values
+                set(REF_FREQ, 10000000);
+            }
+        } ref_freq_reg;
+
         fw_regmap_t() : soft_regmap_t("fw_regmap") {
             add_to_map(clock_ctrl_reg, "clock_ctrl_reg", PUBLIC);
             add_to_map(clock_status_reg, "clock_status_reg", PUBLIC);
+            add_to_map(ref_freq_reg, "ref_freq_reg", PUBLIC);
         }
     };
 
-- 
cgit v1.2.3