aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/x300/x300_dac_ctrl.cpp
diff options
context:
space:
mode:
authormichael-west <michael.west@ettus.com>2014-03-25 15:59:03 -0700
committermichael-west <michael.west@ettus.com>2014-03-25 15:59:03 -0700
commit04292f9b109479b639add31f83fd240a6387f488 (patch)
tree4b8723a4ae63626029704f901ee0083bb23bc1e9 /host/lib/usrp/x300/x300_dac_ctrl.cpp
parent09915aa57bc88099cbcbbe925946ae65bc0ad8f0 (diff)
parentff8a1252f3a51369abe0a165d963b781089ec66c (diff)
downloaduhd-04292f9b109479b639add31f83fd240a6387f488.tar.gz
uhd-04292f9b109479b639add31f83fd240a6387f488.tar.bz2
uhd-04292f9b109479b639add31f83fd240a6387f488.zip
Merge branch 'master' into mwest/b200_docs
Diffstat (limited to 'host/lib/usrp/x300/x300_dac_ctrl.cpp')
-rw-r--r--host/lib/usrp/x300/x300_dac_ctrl.cpp146
1 files changed, 146 insertions, 0 deletions
diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp
new file mode 100644
index 000000000..5eae9cc48
--- /dev/null
+++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp
@@ -0,0 +1,146 @@
+//
+// Copyright 2010-2013 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
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "x300_dac_ctrl.hpp"
+#include "x300_regs.hpp"
+#include <uhd/types/time_spec.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <uhd/exception.hpp>
+#include <boost/foreach.hpp>
+#include <boost/thread/thread.hpp> //sleep
+
+using namespace uhd;
+
+#define write_ad9146_reg(addr, data) \
+ _iface->write_spi(_slaveno, spi_config_t::EDGE_RISE, ((addr) << 8) | (data), 16)
+#define read_ad9146_reg(addr) \
+ (_iface->read_spi(_slaveno, spi_config_t::EDGE_RISE, ((addr) << 8) | (1 << 15), 16) & 0xff)
+
+/*!
+ * A X300 codec control specific to the ad9146 ic.
+ */
+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)
+ {
+ 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
+
+ // 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++)
+ {
+ N0 = (1 << N0_val); //1, 2, 4
+ if ((refclk * N0 * N1) >= 1e9) break;
+ }
+ 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(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:
+ //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.
+ write_ad9146_reg(0x17, 0x04); // FIFO write pointer offset
+ 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 */
+ write_ad9146_reg(0x1C, 0x00); // Configure HB1
+ write_ad9146_reg(0x1D, 0x00); // Configure HB2
+
+ }
+
+
+ ~x300_dac_ctrl_impl(void)
+ {
+ UHD_SAFE_CALL
+ (
+ write_ad9146_reg(0x1, 0xf); //total power down
+ write_ad9146_reg(0x2, 0xf); //total power down
+ )
+ }
+
+ 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));
+ }
+ }
+
+private:
+ uhd::spi_iface::sptr _iface;
+ const size_t _slaveno;
+};
+
+/***********************************************************************
+ * Public make function for the DAC control
+ **********************************************************************/
+x300_dac_ctrl::sptr x300_dac_ctrl::make(uhd::spi_iface::sptr iface, const size_t slaveno, const double clock_rate)
+{
+ return sptr(new x300_dac_ctrl_impl(iface, slaveno, clock_rate));
+}