From b765df3b1976f30a8b95f5a1ea482517a8000a80 Mon Sep 17 00:00:00 2001 From: michael-west Date: Thu, 25 Sep 2014 15:46:52 -0700 Subject: x300: added reset and resync of ADCs and DACs when changing reference clock --- host/lib/usrp/x300/x300_adc_ctrl.cpp | 16 ++++- host/lib/usrp/x300/x300_adc_ctrl.hpp | 3 +- host/lib/usrp/x300/x300_dac_ctrl.cpp | 125 ++++++++++++++++++++++++----------- host/lib/usrp/x300/x300_dac_ctrl.hpp | 14 +++- host/lib/usrp/x300/x300_impl.cpp | 60 ++++++++++++++--- host/lib/usrp/x300/x300_impl.hpp | 2 + 6 files changed, 166 insertions(+), 54 deletions(-) diff --git a/host/lib/usrp/x300/x300_adc_ctrl.cpp b/host/lib/usrp/x300/x300_adc_ctrl.cpp index 75bfb048c..ab2bafef8 100644 --- a/host/lib/usrp/x300/x300_adc_ctrl.cpp +++ b/host/lib/usrp/x300/x300_adc_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -33,6 +33,11 @@ class x300_adc_ctrl_impl : public x300_adc_ctrl public: x300_adc_ctrl_impl(uhd::spi_iface::sptr iface, const size_t slaveno): _iface(iface), _slaveno(slaveno) + { + init(); + } + + void init() { //power-up adc _ads62p48_regs.reset = 1; @@ -46,8 +51,8 @@ public: _ads62p48_regs.lvds_cmos = ads62p48_regs_t::LVDS_CMOS_DDR_LVDS; _ads62p48_regs.channel_control = ads62p48_regs_t::CHANNEL_CONTROL_INDEPENDENT; _ads62p48_regs.data_format = ads62p48_regs_t::DATA_FORMAT_2S_COMPLIMENT; - _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26; - _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26; + _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26; + _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26; this->send_ads62p48_reg(0); @@ -72,6 +77,11 @@ public: } + void reset() + { + init(); + } + double set_gain(const double &gain) { const meta_range_t gain_range = meta_range_t(0, 6.0, 0.5); diff --git a/host/lib/usrp/x300/x300_adc_ctrl.hpp b/host/lib/usrp/x300/x300_adc_ctrl.hpp index fce40a434..d8c5e145b 100644 --- a/host/lib/usrp/x300/x300_adc_ctrl.hpp +++ b/host/lib/usrp/x300/x300_adc_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -39,6 +39,7 @@ public: virtual void set_test_word(const std::string &patterna, const std::string &patternb, const boost::uint32_t = 0) = 0; + virtual void reset(void) = 0; }; #endif /* INCLUDED_X300_ADC_CTRL_HPP */ diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp index 5eae9cc48..d2be70582 100644 --- a/host/lib/usrp/x300/x300_dac_ctrl.cpp +++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -39,7 +39,13 @@ class x300_dac_ctrl_impl : public x300_dac_ctrl { public: x300_dac_ctrl_impl(uhd::spi_iface::sptr iface, const size_t slaveno, const double refclk): - _iface(iface), _slaveno(slaveno) + _iface(iface), _slaveno(slaveno), _refclk(refclk) + { + init(); + check_pll(); + } + + void init() { write_ad9146_reg(0x00, 0x20); // Take DAC into reset. write_ad9146_reg(0x00, 0x80); // Enable SPI reads and come out of reset @@ -47,43 +53,31 @@ public: // Calculate N0 to be VCO friendly. // Aim for VCO between 1 and 2GHz, assert otherwise. - // const int N1 = 4; - const int N1 = 4; + // const int N1 = 4; + const int N1 = 4; int N0_val, N0; for (N0_val = 0; N0_val < 3; N0_val++) { N0 = (1 << N0_val); //1, 2, 4 - if ((refclk * N0 * N1) >= 1e9) break; + if ((_refclk * N0 * N1) >= 1e9) break; } - UHD_ASSERT_THROW((refclk * N0 * N1) >= 1e9); - UHD_ASSERT_THROW((refclk * N0 * N1) <= 2e9); + 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. 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(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. - // Verify PLL is Locked. 1 sec timeout. - // NOTE: Data sheet inconsistant 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) - { - const size_t reg_e = read_ad9146_reg(0x0E); /* Expect bit 7 = 1 */ - if ((exit_time < time_spec_t::get_system_time()) && ((reg_e & (1 << 7)) == 0)) - throw uhd::runtime_error("x300_dac_ctrl: timeout waiting for DAC PLL to lock"); - else if ((reg_e & ((1 << 7) | (1 << 6))) != 0) break; - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } - /* 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 - + write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface //fpga wants I,Q in the sample word: @@ -93,7 +87,14 @@ public: write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode write_ad9146_reg(0x10, 0x48); // Disable SYNC mode. - write_ad9146_reg(0x17, 0x04); // FIFO write pointer offset + + // 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 @@ -102,6 +103,8 @@ public: write_ad9146_reg(0x1C, 0x00); // Configure HB1 write_ad9146_reg(0x1D, 0x00); // Configure HB2 + // Clear event flags + write_ad9146_reg(0x06, 0xFF); } @@ -114,27 +117,71 @@ public: ) } - void arm_dac_sync(void) - { - // - // Attempt to synchronize AD9146's - // - write_ad9146_reg(0x10, 0xCF); // Enable SYNC mode. Sync Averaging set to 128. - - const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(1.0); - while (true) - { - const size_t reg_12 = read_ad9146_reg(0x12); /* Expect bit 7 = 0, bit 6 = 1 */ - if ((exit_time < time_spec_t::get_system_time()) && (((reg_12 & (1 << 6)) == 0) || ((reg_12 & (1 << 7)) != 0))) - throw uhd::runtime_error("x300_dac_ctrl: timeout waiting for backend synchronization"); - else if (((reg_12 & (1 << 6)) != 0) && ((reg_12 & (1 << 7)) == 0)) break; - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } - } + void arm_dac_sync(void) + { + // + // Attempt to synchronize AD9146's + // + write_ad9146_reg(0x10, 0x48); // Disable SYNC mode. + 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(); + } + + void check_pll() + { + // Verify PLL is Locked. 1 sec timeout. + // NOTE: Data sheet inconsistant 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) + { + const size_t reg_e = read_ad9146_reg(0x0E); // PLL Status (Expect bit 7 = 1) + const size_t reg_6 = read_ad9146_reg(0x06); // Event Flags (Expect bit 7 = 0 and bit 6 = 1) + if ((((reg_e >> 7) & 0x1) == 0x1) && (((reg_6 >> 6) & 0x3) == 0x1)) + 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? + write_ad9146_reg(0x06, 0xC0); // Clear PLL event flags + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + } + + void check_dac_sync() + { + const time_spec_t exit_time = time_spec_t::get_system_time() + time_spec_t(1.0); + while (true) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); // wait for sync to complete + const size_t reg_12 = read_ad9146_reg(0x12); // Sync Status (Expect bit 7 = 0, bit 6 = 1) + const size_t reg_6 = read_ad9146_reg(0x06); // Event Flags (Expect bit 5 = 0 and bit 4 = 1) + if ((((reg_12 >> 6) & 0x3) == 0x1) && (((reg_6 >> 4) & 0x3) == 0x1)) + 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)) + write_ad9146_reg(0x06, 0x30); // Clear Sync event flags + } + } + + void check_frontend_sync() + { + // 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; + } private: uhd::spi_iface::sptr _iface; const size_t _slaveno; + const double _refclk; }; /*********************************************************************** diff --git a/host/lib/usrp/x300/x300_dac_ctrl.hpp b/host/lib/usrp/x300/x300_dac_ctrl.hpp index 0db7e1e35..6fe515673 100644 --- a/host/lib/usrp/x300/x300_dac_ctrl.hpp +++ b/host/lib/usrp/x300/x300_dac_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2014 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -37,6 +37,18 @@ public: // ! 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; + + // ! Check for successful frontend sync + virtual void check_frontend_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 1b651065d..9af7bdbfb 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -735,6 +735,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) // setup time sources and properties //////////////////////////////////////////////////////////////////// _tree->create(mb_path / "time_source" / "value") + .set("internal") .subscribe(boost::bind(&x300_impl::update_time_source, this, boost::ref(mb), _1)); static const std::vector time_sources = boost::assign::list_of("internal")("external")("gpsdo"); _tree->create >(mb_path / "time_source" / "options").set(time_sources); @@ -748,7 +749,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) // setup clock sources and properties //////////////////////////////////////////////////////////////////// _tree->create(mb_path / "clock_source" / "value") - .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)); + .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 clock_source_options = boost::assign::list_of("internal")("external")("gpsdo"); _tree->create >(mb_path / "clock_source" / "options").set(clock_source_options); @@ -825,15 +829,6 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr) const time_t tp = time_t(mb.gps->get_sensor("gps_time").to_int()+1); _tree->access(mb_path / "time" / "pps").set(time_spec_t(tp)); } else { - _tree->access(mb_path / "clock_source" / "value").set("internal"); - try { - wait_for_ref_locked(mb.zpu_ctrl, 1.0); - } catch (uhd::exception::runtime_error &e) { - // Ignore for now - It can sometimes take longer than 1 second to lock and that is OK. - //UHD_MSG(warning) << "Clock reference failed to lock to internal source during device initialization. " << - // "Check for the lock before operation or ignore this warning if using another clock source." << std::endl; - } - _tree->access(mb_path / "time_source" / "value").set("internal"); UHD_MSG(status) << "References initialized to internal sources" << std::endl; } } @@ -1395,6 +1390,51 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou */ } +void x300_impl::reset_clocks(mboard_members_t &mb) +{ + 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()); + } + } +} + +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(); + } + + // check PLL locks + BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs) + { + perif.dac->check_pll(); + } + + // 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(); + } +} + void x300_impl::update_time_source(mboard_members_t &mb, const std::string &source) { if (source == "internal") { diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 578e96383..004f73f38 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -343,6 +343,8 @@ private: 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); -- cgit v1.2.3