aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
authormichael-west <michael.west@ettus.com>2014-09-25 15:46:52 -0700
committerMartin Braun <martin.braun@ettus.com>2014-09-25 17:12:14 -0700
commitb765df3b1976f30a8b95f5a1ea482517a8000a80 (patch)
tree140547343209e14d49ed5aa88369e7d44c3f2298 /host/lib
parentd4f487af3fc04a4cd3685f454988f86650e2ef46 (diff)
downloaduhd-b765df3b1976f30a8b95f5a1ea482517a8000a80.tar.gz
uhd-b765df3b1976f30a8b95f5a1ea482517a8000a80.tar.bz2
uhd-b765df3b1976f30a8b95f5a1ea482517a8000a80.zip
x300: added reset and resync of ADCs and DACs when changing reference clock
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/usrp/x300/x300_adc_ctrl.cpp16
-rw-r--r--host/lib/usrp/x300/x300_adc_ctrl.hpp3
-rw-r--r--host/lib/usrp/x300/x300_dac_ctrl.cpp125
-rw-r--r--host/lib/usrp/x300/x300_dac_ctrl.hpp14
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp60
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp2
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
@@ -34,6 +34,11 @@ 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;
this->send_ads62p48_reg(0x00); //issue a reset to the ADC
@@ -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<std::string>(mb_path / "time_source" / "value")
+ .set("internal")
.subscribe(boost::bind(&x300_impl::update_time_source, this, boost::ref(mb), _1));
static const std::vector<std::string> time_sources = boost::assign::list_of("internal")("external")("gpsdo");
_tree->create<std::vector<std::string> >(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<std::string>(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<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo");
_tree->create<std::vector<std::string> >(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<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp));
} else {
- _tree->access<std::string>(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<std::string>(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);