From 95aa5da3baf236cf5f2ead03d55710e8699a87b2 Mon Sep 17 00:00:00 2001
From: Martin Braun <martin.braun@ettus.com>
Date: Tue, 4 Nov 2014 15:17:43 +0100
Subject: x300: Fixed typo in io_impl

---
 host/lib/usrp/x300/x300_io_impl.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'host/lib/usrp/x300/x300_io_impl.cpp')

diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp
index 500a24f7f..c5b8f49e3 100644
--- a/host/lib/usrp/x300/x300_io_impl.cpp
+++ b/host/lib/usrp/x300/x300_io_impl.cpp
@@ -532,7 +532,7 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)
     //setup defaults for unspecified values
     if (not args.otw_format.empty() and args.otw_format != "sc16")
     {
-        throw uhd::value_error("x300_impl::get_rx_stream only supports otw_format sc16");
+        throw uhd::value_error("x300_impl::get_tx_stream only supports otw_format sc16");
     }
     args.otw_format = "sc16";
     args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
-- 
cgit v1.2.3


From 265db795c26cf9609f8f1288484f85788cbf7b3d Mon Sep 17 00:00:00 2001
From: Ashish Chaudhari <ashish@ettus.com>
Date: Thu, 6 Nov 2014 14:21:39 -0800
Subject: x300: Cleaned up DAC ctrl and clock init logic

- DAC: Squashed configuration into 2 main operations: reset and reset_and_resync
- DAC: Put in sleep mode during configuration
- DAC: Synchronize only if streaming to more than one DAC
- DAC: Use falling edge sync mode
- DAC: Fixed power up/down settings
- DAC: Frontend sync failure is fatal
- Clocks: Refactored clock source change logic
- Clocks: Cleaned up init and lock-check sequence
---
 host/lib/usrp/x300/x300_clock_ctrl.cpp |   1 -
 host/lib/usrp/x300/x300_dac_ctrl.cpp   | 210 +++++++++++++++++--------
 host/lib/usrp/x300/x300_dac_ctrl.hpp   |  14 +-
 host/lib/usrp/x300/x300_impl.cpp       | 273 ++++++++++++++++++---------------
 host/lib/usrp/x300/x300_impl.hpp       |  12 +-
 host/lib/usrp/x300/x300_io_impl.cpp    |   9 +-
 6 files changed, 318 insertions(+), 201 deletions(-)

(limited to 'host/lib/usrp/x300/x300_io_impl.cpp')

diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp
index 21411e651..247c10ac4 100644
--- a/host/lib/usrp/x300/x300_clock_ctrl.cpp
+++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp
@@ -49,7 +49,6 @@ x300_clock_ctrl_impl(uhd::spi_iface::sptr spiface,
     _master_clock_rate(master_clock_rate),
     _system_ref_rate(system_ref_rate)
 {
-    set_master_clock_rate(master_clock_rate);
 }
 
 void reset_clocks() {
diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp
index 62fe55c35..d3bcb8644 100644
--- a/host/lib/usrp/x300/x300_dac_ctrl.cpp
+++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp
@@ -25,6 +25,8 @@
 #include <boost/foreach.hpp>
 #include <boost/thread/thread.hpp> //sleep
 
+#define X300_DAC_FRONTEND_SYNC_FAILURE_FATAL
+
 using namespace uhd;
 
 #define write_ad9146_reg(addr, data) \
@@ -45,19 +47,68 @@ public:
     x300_dac_ctrl_impl(uhd::spi_iface::sptr iface, const size_t slaveno, const double refclk):
         _iface(iface), _slaveno(slaveno), _refclk(refclk)
     {
-        init();
-        check_pll();
+        //Power up all DAC subsystems
+        write_ad9146_reg(0x01, 0x10); //Up: I DAC, Q DAC, Receiver, Voltage Ref, Clocks
+        write_ad9146_reg(0x02, 0x00); //No extended delays. Up: Voltage Ref, PLL, DAC, FIFO, Filters
+
+        reset();
     }
 
-    void init()
+    ~x300_dac_ctrl_impl(void)
     {
-        write_ad9146_reg(0x00, 0x20); // Take DAC into reset.
-        write_ad9146_reg(0x00, 0x80); // Enable SPI reads and come out of reset
-        write_ad9146_reg(0x1e, 0x01); // Data path config - set for proper operation
+        UHD_SAFE_CALL
+        (
+            //Power down all DAC subsystems
+            write_ad9146_reg(0x01, 0xEF); //Down: I DAC, Q DAC, Receiver, Voltage Ref, Clocks
+            write_ad9146_reg(0x02, 0x1F); //No extended delays. Down: Voltage Ref, PLL, DAC, FIFO, Filters
+        )
+    }
+
+    void reset()
+    {
+        //ADI recommendations:
+        //- soft reset the chip before configuration
+        //- put the chip in sleep mode during configuration and wake it up when done
+        _soft_reset();
+        _sleep_mode(true);
+        _init();
+        _sleep_mode(false);
+    }
+
+    void reset_and_resync()
+    {
+        //ADI recommendations:
+        //- soft reset the chip before configuration
+        //- put the chip in sleep mode during configuration and wake it up when done
+        //- configure synchronization settings when sleeping
+        _soft_reset();
+        _sleep_mode(true);
+        _init();
+        _backend_sync();
+        _sleep_mode(false);
+    }
+
+    void verify_sync()
+    {
+        _check_pll();
+        _check_dac_sync();
+#ifdef X300_DAC_FRONTEND_SYNC_FAILURE_FATAL
+        _check_frontend_sync(true);
+#else
+        _check_frontend_sync(false);
+#endif
+    }
+
+    //
+    // Setup all non-synchronization related DAC parameters
+    //
+    void _init()
+    {
+        write_ad9146_reg(0x1e, 0x01);   //Datasheet: "Set 1 for proper operation"
+        write_ad9146_reg(0x06, 0xFF);   //Clear all event flags
 
         // Calculate N0 to be VCO friendly.
         // Aim for VCO between 1 and 2GHz, assert otherwise.
-        //  const int N1 = 4;
         const int N1 = 4;
         int N0_val, N0;
         for (N0_val = 0; N0_val < 3; N0_val++)
@@ -68,78 +119,83 @@ public:
         UHD_ASSERT_THROW((_refclk * N0 * N1) >= 1e9);
         UHD_ASSERT_THROW((_refclk * N0 * N1) <= 2e9);
 
-        /* Start PLL */
-        //write_ad9146_reg(0x0C, 0xD1); // Narrow PLL loop filter, Midrange charge pump.
+        // Start PLL
+        write_ad9146_reg(0x06, 0xC0);   //Clear PLL event flags
+        write_ad9146_reg(0x0C, 0xD1); // Narrow PLL loop filter, Midrange charge pump.
         write_ad9146_reg(0x0D, 0xD1 | (N0_val << 2)); // N1=4, N2=16, N0 as calculated
-        //write_ad9146_reg(0x0D, 0x90 | (N0_val << 2)); // N1=2, N2=8, N0 as calculated
         write_ad9146_reg(0x0A, 0xCF); // Auto init VCO band training as per datasheet
         write_ad9146_reg(0x0A, 0xA0); // See above.
 
-        /* Skew DCI signal to find stable data eye */
-        //write_ad9146_reg(0x16, 0x04); //Disable delay in DCI
-        //write_ad9146_reg(0x16, 0x00); //165ps delay in DCI
-        //write_ad9146_reg(0x16, 0x01); //375ps delay in DCI
-        write_ad9146_reg(0x16, 0x02); //615ps delay in DCI
-        //write_ad9146_reg(0x16, 0x03); //720ps delay in DCI
+        _check_pll();
 
+        // Configure digital interface settings
+        write_ad9146_reg(0x16, 0x02); // Skew DCI signal by 615ps to find stable data eye
         write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface
-
         //fpga wants I,Q in the sample word:
         //first transaction goes into low bits
         //second transaction goes into high bits
         //therefore, we want Q to go first (bit 6 == 1)
         write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode
 
-        write_ad9146_reg(0x10, 0x48); // Disable SYNC mode.
-
-        // FIFO write pointer offset
-        // It was found that the read was happening before the write
-        // so the FIFO was maintainining a depth of 3 during operation.
-        // Setting it to 5 to ensure it maintains the ideal depth of 4.
-        // TODO:  Investigate RefClk -> DCI clock timing.
-        write_ad9146_reg(0x17, 0x05);
-
-        write_ad9146_reg(0x18, 0x02); // Request soft FIFO align
-        write_ad9146_reg(0x18, 0x00); // (See above)
-        write_ad9146_reg(0x1B, 0xE4); // Bypass: Modulator, InvSinc, IQ Bal
-
-        /* Configure interpolation filters */
+        // Configure interpolation filters
         write_ad9146_reg(0x1C, 0x00); // Configure HB1
         write_ad9146_reg(0x1D, 0x00); // Configure HB2
+        write_ad9146_reg(0x1B, 0xE4); // Bypass: Modulator, InvSinc, IQ Bal
 
-        // Clear event flags
-        write_ad9146_reg(0x06, 0xFF);
-    }
-
-
-    ~x300_dac_ctrl_impl(void)
-    {
-        UHD_SAFE_CALL
-        (
-            write_ad9146_reg(0x1, 0xf); //total power down
-            write_ad9146_reg(0x2, 0xf); //total power down
-        )
+        // Disable sync mode by default (may get turned on later)
+        write_ad9146_reg(0x10, 0x40); // Disable SYNC mode.
     }
 
-    void arm_dac_sync(void)
+    //
+    // Attempt to synchronize AD9146's
+    //
+    void _backend_sync(void)
     {
-        //
-        // Attempt to synchronize AD9146's
-        //
-        write_ad9146_reg(0x10, 0x48);   // Disable SYNC mode.
+        write_ad9146_reg(0x10, 0x40);   // Disable SYNC mode to reset state machines.
         write_ad9146_reg(0x06, 0x30);   // Clear Sync event flags
-        write_ad9146_reg(0x10, 0xCF);   // Enable SYNC mode. Sync Averaging set to 128.
-    }
 
-    void reset()
-    {
-        init();
+        //SYNC Settings:
+        //- SYNC = Enabled
+        //- Data Rate Mode: Synchronize at the rate at which data is consumed and not at
+        //                  the granularity of the FIFO
+        //- Falling edge sync: For the X300, DACCLK is generated using RefClk. Within the
+        //                     DAC, the RefClk is sampled by DACCLK to sync interpolation
+        //                     stages across multiple DACs. To ensure that we capture the
+        //                     RefClk when it is not transitioning, we sample on the falling
+        //                     edge of DACCLK
+        //- Averaging = MAX
+        write_ad9146_reg(0x10, 0xC7);   // Enable SYNC mode. Falling edge sync. Averaging set to 128.
+
+        //Wait for backend SYNC state machine to lock before proceeding. This guarantees that the
+        //inputs and output of the FIFO have synchronized clocks
+        _check_dac_sync();
+
+        //FIFO write pointer offset
+        //One of ADI's requirements to use data-rate synchronization in PLL mode is to meet
+        //setup and hold times for RefClk -> DCI clock which we *do not* currently meet in
+        //the FPGA. The DCI clock reaches a full RefClk cycle later which results in the
+        //FIFO popping before the first push. This results in a steady-state FIFO fullness
+        //of pointer - 1. To reach the optimal FIFO fullness of 4 we set the pointer to 5.
+        //FIXME: At some point we should meet timing on this interface
+        write_ad9146_reg(0x17, 0x05);
+
+        // We are requesting a soft FIFO align just to put the FIFO
+        // in a known state. The FRAME will actually do sync the
+        // FIFO correctly when a stream is created
+        write_ad9146_reg(0x18, 0x02); // Request soft FIFO align
+        write_ad9146_reg(0x18, 0x00); // (See above)
+
+        //Verify the FIFO thermometer
+        _check_frontend_sync(false); //FIFO sanity check
     }
 
-    void check_pll()
+    //
+    // Check for PLL lock. Fatal if not locked within timeout
+    //
+    void _check_pll()
     {
         // Verify PLL is Locked. 1 sec timeout.
-        // NOTE: Data sheet inconsistant about which pins give PLL lock status. FIXME!
+        // NOTE: Data sheet inconsistent about which pins give PLL lock status. FIXME!
         const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(1.0);
         while (true)
         {
@@ -149,13 +205,16 @@ public:
                 break;
             if (exit_time < time_spec_t::get_system_time())
                 throw uhd::runtime_error("x300_dac_ctrl: timeout waiting for DAC PLL to lock");
-            if (reg_6 & (1 << 7))               // Sync lost?
+            if (reg_6 & (1 << 7))               // Lock lost?
                 write_ad9146_reg(0x06, 0xC0);   // Clear PLL event flags
             boost::this_thread::sleep(boost::posix_time::milliseconds(10));
         }
     }
 
-    void check_dac_sync()
+    //
+    // Check for DAC sync. Fatal if not synced within timeout
+    //
+    void _check_dac_sync()
     {
         const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(1.0);
         while (true)
@@ -167,19 +226,44 @@ public:
                 break;
             if (exit_time < time_spec_t::get_system_time())
                 throw uhd::runtime_error("x300_dac_ctrl: timeout waiting for backend synchronization");
-            if (reg_12 & (1 << 7))  // Sync acquired and lost?
-                arm_dac_sync();     // Re-arm and try again
-            else if (reg_6 & (1 << 5))
+            if (reg_6 & (1 << 5))
                 write_ad9146_reg(0x06, 0x30);   // Clear Sync event flags
+#ifdef X300_DAC_RETRY_BACKEND_SYNC
+            if (reg_12 & (1 << 7))              // Sync acquired and lost?
+                write_ad9146_reg(0x10, 0xC7);   // Enable SYNC mode. Falling edge sync. Averaging set to 128.
+#endif
         }
     }
 
-    void check_frontend_sync()
+    //
+    // Check FIFO thermometer.
+    //
+    void _check_frontend_sync(bool failure_is_fatal)
     {
         // Register 0x19 has a thermometer indicator of the FIFO depth
         const size_t reg_19 = read_ad9146_reg(0x19);
-        if ((reg_19 & 0xFF) != 0xF)
-            UHD_MSG(warning) << "x300_dac_ctrl: unexpected FIFO depth [0x" << std::hex << (reg_19 & 0xFF) << std::dec << "]" << std::endl;
+        if ((reg_19 & 0xFF) != 0xF) {
+            std::string msg((boost::format("x300_dac_ctrl: front-end sync failed. unexpected FIFO depth [0x%x]\n") % (reg_19 & 0xFF)).str());
+            if (failure_is_fatal) {
+                throw uhd::runtime_error(msg);
+            } else {
+                UHD_MSG(warning) << msg;
+            }
+        }
+    }
+
+    void _sleep_mode(bool sleep)
+    {
+        boost::uint8_t sleep_val = sleep ? (1<<7) : 0x00;
+        //Set sleep word and default fullscale value
+        write_ad9146_reg(0x41, sleep_val | 0x01);    //I DAC
+        write_ad9146_reg(0x45, sleep_val | 0x01);    //Q DAC
+    }
+
+    void _soft_reset()
+    {
+        write_ad9146_reg(0x00, 0x20); // Take DAC into reset.
+        write_ad9146_reg(0x00, 0x80); // Enable SPI reads and come out of reset
     }
 
 private:
diff --git a/host/lib/usrp/x300/x300_dac_ctrl.hpp b/host/lib/usrp/x300/x300_dac_ctrl.hpp
index 5fd7e13d8..c2e509b54 100644
--- a/host/lib/usrp/x300/x300_dac_ctrl.hpp
+++ b/host/lib/usrp/x300/x300_dac_ctrl.hpp
@@ -37,20 +37,14 @@ public:
      */
     static sptr make(uhd::spi_iface::sptr iface, const size_t slaveno, const double clock_rate);
 
-    // ! Arm the sync feature in DAC
-    virtual void arm_dac_sync(void) = 0;
-
-    // ! Check for successful backend sync
-    virtual void check_dac_sync(void) = 0;
-
     // ! Reset the DAC
     virtual void reset(void) = 0;
 
-    // ! Check for PLL lock
-    virtual void check_pll(void) = 0;
+    // ! Reset the DAC and resync
+    virtual void reset_and_resync(void) = 0;
 
-    // ! Check for successful frontend sync
-    virtual void check_frontend_sync(void) = 0;
+    // ! Check for successful backend and frontend sync
+    virtual void verify_sync(void) = 0;
 };
 
 #endif /* INCLUDED_X300_DAC_CTRL_HPP */
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index b4286ee79..5520cc82e 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -372,6 +372,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
 {
     const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i);
     mboard_members_t &mb = _mb[mb_i];
+    mb.initialization_done = false;
 
     mb.addr = dev_addr.has_key("resource") ? dev_addr["resource"] : dev_addr["addr"];
     mb.xport_path = dev_addr.has_key("resource") ? "nirio" : "eth";
@@ -621,32 +622,18 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
         mb.hw_rev = X300_REV("D");
     }
 
-    //Initialize clock control with internal references and GPSDO power on.
-    mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL;
-    mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL;
-    mb.clock_control_regs_pps_out_enb = 0;
-    mb.clock_control_regs_tcxo_enb = 1;
-    mb.clock_control_regs_gpsdo_pwr = 1;
-    this->update_clock_control(mb);
-
-    //Create clock control
+    //Create clock control. NOTE: This does not configure the LMK yet.
+    initialize_clock_control(mb);
     mb.clock = x300_clock_ctrl::make(mb.zpu_spi,
         1 /*slaveno*/,
         mb.hw_rev,
         dev_addr.cast<double>("master_clock_rate", X300_DEFAULT_TICK_RATE),
         dev_addr.cast<double>("system_ref_rate", X300_DEFAULT_SYSREF_RATE));
 
-    //wait for reference clock to lock
-    if(mb.hw_rev > 4)
-    {
-        try {
-            //FIXME:  Need to verify timeout value to make sure lock can be achieved in < 1.0 seconds
-            wait_for_ref_locked(mb.zpu_ctrl, 1.0);
-        } catch (uhd::runtime_error &e) {
-            //Silently fail for now, but fix after we have the correct timeout value
-            //UHD_MSG(warning) << "Clock failed to lock to internal source during initialization." << std::endl;
-        }
-    }
+    //Initialize clock source to use internal reference and generate
+    //a valid radio clock. This may change after configuration is done.
+    //This will configure the LMK and wait for lock
+    update_clock_source(mb, "internal");
 
     ////////////////////////////////////////////////////////////////////
     // create clock properties
@@ -750,7 +737,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
     _tree->create<std::string>(mb_path / "clock_source" / "value")
         .set("internal")
         .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1))
-        .subscribe(boost::bind(&x300_impl::reset_clocks, this, boost::ref(mb)))
         .subscribe(boost::bind(&x300_impl::reset_radios, this, boost::ref(mb)));
 
     static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo");
@@ -769,6 +755,22 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
     _tree->create<bool>(mb_path / "clock_source" / "output")
         .subscribe(boost::bind(&x300_clock_ctrl::set_ref_out, mb.clock, _1));
 
+    ////////////////////////////////////////////////////////////////////
+    // initialize clock and time sources
+    ////////////////////////////////////////////////////////////////////
+    if (mb.gps and mb.gps->gps_detected())
+    {
+        UHD_MSG(status) << "Initializing clock and time using GPSDO... " << std::flush;
+        _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo");
+        _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo");
+        const time_t tp = time_t(mb.gps->get_sensor("gps_time").to_int() + 1);
+        _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp));
+    } else {
+        UHD_MSG(status) << "Initializing clock and time using internal sources... " << std::flush;
+        _tree->access<std::string>(mb_path / "clock_source" / "value").set("internal");
+        _tree->access<std::string>(mb_path / "time_source" / "value").set("internal");
+    }
+    UHD_MSG(status) << "done"  << std::endl;
 
     ////////////////////////////////////////////////////////////////////
     // create frontend mapping
@@ -811,25 +813,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
     _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_fe_spec);
     _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_fe_spec);
 
-    UHD_MSG(status) << "Initializing clock and PPS references..." << std::endl;
-    //Set to the GPSDO if installed
-    if (mb.gps and mb.gps->gps_detected())
-    {
-        _tree->access<std::string>(mb_path / "clock_source" / "value").set("gpsdo");
-        try {
-            wait_for_ref_locked(mb.zpu_ctrl, 1.0);
-        } catch (uhd::exception::runtime_error &e) {
-            UHD_MSG(warning) << "Clock reference failed to lock to GPSDO during device initialization.  " <<
-                "Check for the lock before operation or ignore this warning if using another clock source." << std::endl;
-        }
-        _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo");
-        UHD_MSG(status) << "References initialized to GPSDO sources" << std::endl;
-        UHD_MSG(status) << "Initializing time to the GPSDO time" << std::endl;
-        const time_t tp = time_t(mb.gps->get_sensor("gps_time").to_int()+1);
-        _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp));
-    } else {
-        UHD_MSG(status) << "References initialized to internal sources" << std::endl;
-    }
+    mb.initialization_done = true;
 }
 
 x300_impl::~x300_impl(void)
@@ -916,14 +900,6 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
     }
     perif.adc->set_test_word("normal", "normal");
 
-    ////////////////////////////////////////////////////////////////
-    // Sync DAC's for MIMO
-    ////////////////////////////////////////////////////////////////
-    UHD_MSG(status) << "Sync DAC's." << std::endl;
-    perif.dac->arm_dac_sync();               // Put DAC into data Sync mode
-    perif.ctrl->poke32(TOREG(SR_DACSYNC), 0x1);  // Arm FRAMEP/N sync pulse
-
-
     ////////////////////////////////////////////////////////////////
     // create codec control objects
     ////////////////////////////////////////////////////////////////
@@ -960,8 +936,6 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
         .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1))
         .set(std::complex<double>(0.0, 0.0));
 
-
-
     ////////////////////////////////////////////////////////////////////
     // create rx dsp control objects
     ////////////////////////////////////////////////////////////////////
@@ -1200,10 +1174,10 @@ x300_impl::both_xports_t x300_impl::make_transport(
                 << std::endl;
         }
 
-    size_t system_max_send_frame_size = (size_t) _max_frame_sizes.send_frame_size;
-    size_t system_max_recv_frame_size = (size_t) _max_frame_sizes.recv_frame_size;
+        size_t system_max_send_frame_size = (size_t) _max_frame_sizes.send_frame_size;
+        size_t system_max_recv_frame_size = (size_t) _max_frame_sizes.recv_frame_size;
 
-    // Make sure frame sizes do not exceed the max available value supported by UHD
+        // Make sure frame sizes do not exceed the max available value supported by UHD
         default_buff_args.send_frame_size =
             (prefix == X300_RADIO_DEST_PREFIX_TX)
             ? std::min(system_max_send_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE)
@@ -1346,11 +1320,9 @@ void x300_impl::register_loopback_self_test(wb_iface::sptr iface)
     UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl;
 }
 
-void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb)
-{
-    mb.clock_control_regs_pps_out_enb = enb? 1 : 0;
-    this->update_clock_control(mb);
-}
+/***********************************************************************
+ * clock and time control logic
+ **********************************************************************/
 
 void x300_impl::update_clock_control(mboard_members_t &mb)
 {
@@ -1363,79 +1335,71 @@ void x300_impl::update_clock_control(mboard_members_t &mb)
     mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_CLOCK_CTRL), reg);
 }
 
-void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &source)
+void x300_impl::initialize_clock_control(mboard_members_t &mb)
 {
-    mb.clock_control_regs_clock_source = 0;
-    mb.clock_control_regs_tcxo_enb = 0;
-    if (source == "internal") {
-        mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL;
-        mb.clock_control_regs_tcxo_enb = 1;
-    } else if (source == "external") {
-        mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL;
-    } else if (source == "gpsdo") {
-        mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO;
-    } else {
-        throw uhd::key_error("update_clock_source: unknown source: " + source);
-    }
-
+    //Initialize clock control register soft copies
+    mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL;
+    mb.clock_control_regs_pps_select = ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL;
+    mb.clock_control_regs_pps_out_enb = 0;
+    mb.clock_control_regs_tcxo_enb = 1;
+    mb.clock_control_regs_gpsdo_pwr = 1;    //GPSDO power always ON
     this->update_clock_control(mb);
-
-    /* FIXME:  implement when we know the correct timeouts
-     * //wait for lock
-     * double timeout = 1.0;
-     * try {
-     *     if (mb.hw_rev > 4)
-     *         wait_for_ref_locked(mb.zpu_ctrl, timeout);
-     * } catch (uhd::runtime_error &e) {
-     *     //failed to lock on reference
-     *     throw uhd::runtime_error((boost::format("Clock failed to lock to %s source.") % source).str());
-     * }
-     */
 }
 
-void x300_impl::reset_clocks(mboard_members_t &mb)
+void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb)
 {
-    mb.clock->reset_clocks();
-
-    if (mb.hw_rev > 4)
-    {
-        try {
-            wait_for_ref_locked(mb.zpu_ctrl, 30.0);
-        } catch (uhd::runtime_error &e) {
-            //failed to lock on reference
-            throw uhd::runtime_error((boost::format("PLL failed to lock to reference clock.")).str());
-        }
-    }
+    mb.clock_control_regs_pps_out_enb = enb? 1 : 0;
+    this->update_clock_control(mb);
 }
 
-void x300_impl::reset_radios(mboard_members_t &mb)
+void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &source)
 {
-    // reset ADCs and DACs
-    BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs)
-    {
-        perif.adc->reset();
-        perif.dac->reset();
-    }
+    //Optimize for the case when the current source is internal and we are trying
+    //to set it to internal. This is the only case where we are guaranteed that 
+    //the clock has not gone away so we can skip setting the MUX and reseting the LMK.
+    if (not (mb.current_refclk_src == "internal" and source == "internal")) {
+        //Update the clock MUX on the motherboard to select the requested source
+        mb.clock_control_regs_clock_source = 0;
+        mb.clock_control_regs_tcxo_enb = 0;
+        if (source == "internal") {
+            mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL;
+            mb.clock_control_regs_tcxo_enb = 1;
+        } else if (source == "external") {
+            mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL;
+        } else if (source == "gpsdo") {
+            mb.clock_control_regs_clock_source = ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO;
+        } else {
+            throw uhd::key_error("update_clock_source: unknown source: " + source);
+        }
+        this->update_clock_control(mb);
 
-    // check PLL locks
-    BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs)
-    {
-        perif.dac->check_pll();
+        //Reset the LMK to make sure it re-locks to the new reference
+        mb.clock->reset_clocks();
     }
 
-    // Sync DACs
-    BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs)
-    {
-        perif.dac->arm_dac_sync();
-    }
-    BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs)
-    {
-        perif.dac->check_dac_sync();
-        // Arm FRAMEP/N sync pulse
-        // TODO:  Investigate timing of the sync frame pulse.
-        perif.ctrl->poke32(TOREG(SR_DACSYNC), 0x1);
-        perif.dac->check_frontend_sync();
+    //Wait for the LMK to lock (always, as a sanity check that the clock is useable)
+    //* Currently the LMK can take as long as 30 seconds to lock to a reference but we don't
+    //* want to wait that long during initialization.
+    //TODO: Need to verify timeout and settings to make sure lock can be achieved in < 1.0 seconds
+    double timeout = mb.initialization_done ? 30.0 : 1.0;
+
+    //The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may
+    //lead to locking issues. So, disable the ref-locked check for older (unsupported) boards.
+    if (mb.hw_rev > 4) {
+        if (not wait_for_ref_locked(mb.zpu_ctrl, timeout)) {
+            //failed to lock on reference
+            if (mb.initialization_done) {
+                throw uhd::runtime_error((boost::format("Reference Clock failed to lock to %s source.") % source).str());
+            } else {
+                //TODO: Re-enable this warning when we figure out a reliable lock time
+                //UHD_MSG(warning) << "Reference clock failed to lock to " + source + " during device initialization.  " <<
+                //    "Check for the lock before operation or ignore this warning if using another clock source." << std::endl;
+            }
+        }
     }
+
+    //Update cache value
+    mb.current_refclk_src = source;
 }
 
 void x300_impl::update_time_source(mboard_members_t &mb, const std::string &source)
@@ -1460,18 +1424,18 @@ void x300_impl::update_time_source(mboard_members_t &mb, const std::string &sour
     }
 }
 
-void x300_impl::wait_for_ref_locked(wb_iface::sptr ctrl, double timeout)
+bool x300_impl::wait_for_ref_locked(wb_iface::sptr ctrl, double timeout)
 {
     boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::milliseconds(timeout * 1000.0);
     do
     {
         if (get_ref_locked(ctrl).to_bool())
-            return;
+            return true;
         boost::this_thread::sleep(boost::posix_time::milliseconds(1));
     } while (boost::get_system_time() < timeout_time);
 
     //failed to lock on reference
-    throw uhd::runtime_error("The reference clock failed to lock.");
+    return false;
 }
 
 sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl)
@@ -1496,6 +1460,67 @@ bool x300_impl::is_pps_present(wb_iface::sptr ctrl)
     return false;
 }
 
+/***********************************************************************
+ * reset and synchronization logic
+ **********************************************************************/
+
+void x300_impl::reset_radios(mboard_members_t &mb)
+{
+    // Reset ADCs and DACs
+    BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs)
+    {
+        perif.adc->reset();
+        perif.dac->reset();
+    }
+}
+
+void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios)
+{
+    if (radios.size() < 2) return;  //Nothing to synchronize
+
+    //**PRECONDITION**
+    //This function assumes that all the VITA times in "radios" are synchronized
+    //to a common reference. Currently, this function is called in get_tx_stream
+    //which also has the same precondition.
+
+    //Reinitialize and resync all DACs
+    for (size_t i = 0; i < radios.size(); i++) {
+        radios[i]->dac->reset_and_resync();
+    }
+
+    //Get a rough estimate of the cumulative command latency
+    boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time();
+    for (size_t i = 0; i < radios.size(); i++) {
+        radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call
+    }
+    boost::posix_time::time_duration t_elapsed =
+        boost::posix_time::microsec_clock::local_time() - t_start;
+
+    //Add 100% of headroom + uncertaintly to the command time
+    boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/;
+
+    //Pick radios[0] as the time reference.
+    uhd::time_spec_t sync_time =
+        radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6);
+
+    //Send the sync command
+    for (size_t i = 0; i < radios.size(); i++) {
+        radios[i]->ctrl->set_time(sync_time);
+        radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1);    //Arm FRAMEP/N sync pulse
+        radios[i]->ctrl->set_time(uhd::time_spec_t(0.0));   //Clear command time
+    }
+
+    //Wait and check status
+    boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us));
+    for (size_t i = 0; i < radios.size(); i++) {
+        radios[i]->dac->verify_sync();
+    }
+}
+
+/***********************************************************************
+ * eeprom
+ **********************************************************************/
+
 void x300_impl::set_db_eeprom(i2c_iface::sptr i2c, const size_t addr, const uhd::usrp::dboard_eeprom_t &db_eeprom)
 {
     db_eeprom.store(*i2c, addr);
@@ -1507,6 +1532,10 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep
     mb_eeprom.commit(*eeprom16, "X300");
 }
 
+/***********************************************************************
+ * front-panel GPIO
+ **********************************************************************/
+
 boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &)
 {
     return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX));
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index 924cb61a4..70c5dccb4 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -194,6 +194,7 @@ private:
         uhd::dict<size_t, boost::weak_ptr<uhd::rx_streamer> > rx_streamers;
         uhd::dict<size_t, boost::weak_ptr<uhd::tx_streamer> > tx_streamers;
 
+        bool initialization_done;
         uhd::task::sptr claimer_task;
         std::string addr;
         std::string xport_path;
@@ -233,6 +234,7 @@ private:
         std::string loaded_fpga_image;
 
         size_t hw_rev;
+        std::string current_refclk_src;
     };
     std::vector<mboard_members_t> _mb;
 
@@ -341,14 +343,14 @@ private:
     void update_tx_samp_rate(mboard_members_t&, const size_t, const double);
 
     void update_clock_control(mboard_members_t&);
+    void initialize_clock_control(mboard_members_t &mb);
     void set_time_source_out(mboard_members_t&, const bool);
     void update_clock_source(mboard_members_t&, const std::string &);
     void update_time_source(mboard_members_t&, const std::string &);
-    void reset_clocks(mboard_members_t&);
     void reset_radios(mboard_members_t&);
 
     uhd::sensor_value_t get_ref_locked(uhd::wb_iface::sptr);
-    void wait_for_ref_locked(uhd::wb_iface::sptr, double timeout = 0.0);
+    bool wait_for_ref_locked(uhd::wb_iface::sptr, double timeout = 0.0);
     bool is_pps_present(uhd::wb_iface::sptr);
 
     void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &);
@@ -360,6 +362,12 @@ private:
     void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant);
     boost::uint32_t get_fp_gpio(gpio_core_200::sptr, const std::string &);
     void set_fp_gpio(gpio_core_200::sptr, const std::string &, const boost::uint32_t);
+
+    //**PRECONDITION**
+    //This function assumes that all the VITA times in "radios" are synchronized
+    //to a common reference. Currently, this function is called in get_tx_stream
+    //which also has the same precondition.
+    static void synchronize_dacs(const std::vector<radio_perifs_t*>& mboards);
 };
 
 #endif /* INCLUDED_X300_IMPL_HPP */
diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp
index c5b8f49e3..04042049d 100644
--- a/host/lib/usrp/x300/x300_io_impl.cpp
+++ b/host/lib/usrp/x300/x300_io_impl.cpp
@@ -540,6 +540,7 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)
     //shared async queue for all channels in streamer
     boost::shared_ptr<async_md_type> async_md(new async_md_type(1000/*messages deep*/));
 
+    std::vector<radio_perifs_t*> radios_list;
     boost::shared_ptr<sph::send_packet_streamer> my_streamer;
     for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
     {
@@ -557,9 +558,10 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)
         }
         // Find the DSP that corresponds to this mainboard and subdev
         mboard_members_t &mb = _mb[mb_index];
-	const size_t radio_index = _tree->access<std::vector<size_t> >("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "tx_chan_dsp_mapping")
+        const size_t radio_index = _tree->access<std::vector<size_t> >("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "tx_chan_dsp_mapping")
                                             .get().at(mb_chan);
         radio_perifs_t &perif = mb.radio_perifs[radio_index];
+        radios_list.push_back(&perif);
 
         //setup the dsp transport hints (TODO)
         device_addr_t device_addr = mb.send_args;
@@ -571,8 +573,8 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)
         both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_TX, device_addr, data_sid);
         UHD_LOG << boost::format("data_sid = 0x%08x\n") % data_sid << std::endl;
 
-	// To calculate the max number of samples per packet, we assume the maximum header length
-	// to avoid fragmentation should the entire header be used.
+        // To calculate the max number of samples per packet, we assume the maximum header length
+        // to avoid fragmentation should the entire header be used.
         const size_t bpp = xport.send->get_send_frame_size() - X300_TX_MAX_HDR_LEN;
         const size_t bpi = convert::get_bytes_per_item(args.otw_format);
         const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
@@ -640,5 +642,6 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)
         _tree->access<double>(mb_path / "tx_dsps" / boost::lexical_cast<std::string>(radio_index) / "rate" / "value").update();
     }
 
+    synchronize_dacs(radios_list);
     return my_streamer;
 }
-- 
cgit v1.2.3


From 2068af70eee6b6ca831d8bd71764d783e182f913 Mon Sep 17 00:00:00 2001
From: Martin Braun <martin.braun@ettus.com>
Date: Tue, 4 Nov 2014 22:36:26 +0100
Subject: x300: Made use of new CHDR packing routines

---
 host/lib/usrp/x300/x300_io_impl.cpp | 52 +++++++------------------------------
 1 file changed, 9 insertions(+), 43 deletions(-)

(limited to 'host/lib/usrp/x300/x300_io_impl.cpp')

diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp
index 334ae8168..e3515af0c 100644
--- a/host/lib/usrp/x300/x300_io_impl.cpp
+++ b/host/lib/usrp/x300/x300_io_impl.cpp
@@ -23,6 +23,7 @@
 #include <uhd/transport/nirio_zero_copy.hpp>
 #include "async_packet_handler.hpp"
 #include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/chdr.hpp>
 #include <boost/bind.hpp>
 #include <uhd/utils/tasks.hpp>
 #include <uhd/utils/log.hpp>
@@ -123,41 +124,6 @@ void x300_impl::update_subdev_spec(const std::string &tx_rx, const size_t mb_i,
 }
 
 
-/***********************************************************************
- * VITA stuff
- **********************************************************************/
-static void x300_if_hdr_unpack_be(
-    const boost::uint32_t *packet_buff,
-    vrt::if_packet_info_t &if_packet_info
-){
-    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
-    return vrt::if_hdr_unpack_be(packet_buff, if_packet_info);
-}
-
-static void x300_if_hdr_pack_be(
-    boost::uint32_t *packet_buff,
-    vrt::if_packet_info_t &if_packet_info
-){
-    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
-    return vrt::if_hdr_pack_be(packet_buff, if_packet_info);
-}
-
-static void x300_if_hdr_unpack_le(
-    const boost::uint32_t *packet_buff,
-    vrt::if_packet_info_t &if_packet_info
-){
-    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
-    return vrt::if_hdr_unpack_le(packet_buff, if_packet_info);
-}
-
-static void x300_if_hdr_pack_le(
-    boost::uint32_t *packet_buff,
-    vrt::if_packet_info_t &if_packet_info
-){
-    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
-    return vrt::if_hdr_pack_le(packet_buff, if_packet_info);
-}
-
 /***********************************************************************
  * RX flow control handler
  **********************************************************************/
@@ -209,9 +175,9 @@ static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xpo
 
     //load header
     if (big_endian)
-        x300_if_hdr_pack_be(pkt, packet_info);
+        vrt::chdr::if_hdr_pack_be(pkt, packet_info);
     else
-        x300_if_hdr_pack_le(pkt, packet_info);
+        vrt::chdr::if_hdr_pack_le(pkt, packet_info);
 
     //load payload
     pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0);
@@ -276,12 +242,12 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero
     {
         if (big_endian)
         {
-            x300_if_hdr_unpack_be(packet_buff, if_packet_info);
+            vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info);
             endian_conv = uhd::ntohx;
         }
         else
         {
-            x300_if_hdr_unpack_le(packet_buff, if_packet_info);
+            vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info);
             endian_conv = uhd::wtohx;
         }
     }
@@ -430,10 +396,10 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_)
         //init some streamer stuff
         std::string conv_endianness;
         if (mb.if_pkt_is_big_endian) {
-            my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_be);
+            my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be);
             conv_endianness = "be";
         } else {
-            my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_le);
+            my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le);
             conv_endianness = "le";
         }
 
@@ -594,10 +560,10 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)
 
         std::string conv_endianness;
         if (mb.if_pkt_is_big_endian) {
-            my_streamer->set_vrt_packer(&x300_if_hdr_pack_be);
+            my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be);
             conv_endianness = "be";
         } else {
-            my_streamer->set_vrt_packer(&x300_if_hdr_pack_le);
+            my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le);
             conv_endianness = "le";
         }
 
-- 
cgit v1.2.3