diff options
Diffstat (limited to 'host/lib/usrp/x300/x300_dac_ctrl.cpp')
-rw-r--r-- | host/lib/usrp/x300/x300_dac_ctrl.cpp | 210 |
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: |