aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/x300/x300_dac_ctrl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/x300/x300_dac_ctrl.cpp')
-rw-r--r--host/lib/usrp/x300/x300_dac_ctrl.cpp210
1 files changed, 147 insertions, 63 deletions
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: