diff options
Diffstat (limited to 'host/lib/usrp/x300')
| -rw-r--r-- | host/lib/usrp/x300/x300_clock_ctrl.cpp | 1 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_dac_ctrl.cpp | 210 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_dac_ctrl.hpp | 14 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 273 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.hpp | 12 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_io_impl.cpp | 9 | 
6 files changed, 318 insertions, 201 deletions
| 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) @@ -917,14 +901,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      ////////////////////////////////////////////////////////////////      _tree->create<int>(mb_path / "rx_codecs" / slot_name / "gains"); //phony property so this dir exists @@ -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;  } | 
