diff options
Diffstat (limited to 'host/lib')
| -rw-r--r-- | host/lib/usrp/x300/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_adc_dac_utils.cpp | 412 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 386 | 
3 files changed, 413 insertions, 386 deletions
| diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt index 15af44721..9a8601452 100644 --- a/host/lib/usrp/x300/CMakeLists.txt +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -35,6 +35,7 @@ IF(ENABLE_X300)          ${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_dac_utils.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c      )  ENDIF(ENABLE_X300) diff --git a/host/lib/usrp/x300/x300_adc_dac_utils.cpp b/host/lib/usrp/x300/x300_adc_dac_utils.cpp new file mode 100644 index 000000000..2dadea26e --- /dev/null +++ b/host/lib/usrp/x300/x300_adc_dac_utils.cpp @@ -0,0 +1,412 @@ +// +// Copyright 2015 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_impl.hpp" +#include <boost/date_time/posix_time/posix_time_io.hpp> + +/*********************************************************************** + * DAC: Reset and synchronization operations + **********************************************************************/ + +void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) +{ +    if (radios.size() < 2) return;  //Nothing to synchronize + +    //**PRECONDITION** +    //This function assumes that all the VITA times in "radios" are synchronized +    //to a common reference. Currently, this function is called in get_tx_stream +    //which also has the same precondition. + +    //Reinitialize and resync all DACs +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->dac->reset_and_resync(); +    } + +    //Get a rough estimate of the cumulative command latency +    boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call +    } +    boost::posix_time::time_duration t_elapsed = +        boost::posix_time::microsec_clock::local_time() - t_start; + +    //Add 100% of headroom + uncertaintly to the command time +    boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; + +    //Pick radios[0] as the time reference. +    uhd::time_spec_t sync_time = +        radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); + +    //Send the sync command +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->ctrl->set_time(sync_time); +        radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1);    //Arm FRAMEP/N sync pulse +        radios[i]->ctrl->set_time(uhd::time_spec_t(0.0));   //Clear command time +    } + +    //Wait and check status +    boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->dac->verify_sync(); +    } +} + +/*********************************************************************** + * ADC: Self-test operations + **********************************************************************/ + +static void check_adc(uhd::wb_iface::sptr iface, const boost::uint32_t val, const boost::uint32_t i) +{ +    boost::uint32_t adc_rb = iface->peek32(RB32_RX); +    adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA +    if (val != adc_rb) { +        throw uhd::runtime_error( +            (boost::format("ADC self-test failed for Radio%d. (Exp=0x%x, Got=0x%x)")%i%val%adc_rb).str()); +    } +} + +void x300_impl::self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms) { +    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +        radio_perifs_t &perif = mb.radio_perifs[r]; + +        //First test basic patterns +        perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc,r); +        perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000,r); +        perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000,r); +        perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc,r); +        for (size_t k = 0; k < 14; k++) +        { +            perif.adc->set_test_word("zeros", "custom", 1 << k); +            check_adc(perif.ctrl, 1 << (k+2),r); +        } +        for (size_t k = 0; k < 14; k++) +        { +            perif.adc->set_test_word("custom", "zeros", 1 << k); +            check_adc(perif.ctrl, 1 << (k+18),r); +        } + +        //Turn on ramp pattern test +        perif.adc->set_test_word("ramp", "ramp"); +        perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +        perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +    } +    boost::this_thread::sleep(boost::posix_time::milliseconds(ramp_time_ms)); + +    bool passed = true; +    std::string status_str; +    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +        radio_perifs_t &perif = mb.radio_perifs[r]; +        perif.misc_ins->refresh(); + +        std::string i_status, q_status; +        if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) +            if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR)) +                i_status = "Bit Errors!"; +            else +                i_status = "Good"; +        else +            i_status = "Not Locked!"; + +        if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) +            if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR)) +                q_status = "Bit Errors!"; +            else +                q_status = "Good"; +        else +            q_status = "Not Locked!"; + +        passed = passed && (i_status == "Good") && (q_status == "Good"); +        status_str += (boost::format(", ADC%d_I=%s, ADC%d_Q=%s")%r%i_status%r%q_status).str(); + +        //Return to normal mode +        perif.adc->set_test_word("normal", "normal"); +    } + +    if (not passed) { +        throw uhd::runtime_error( +            (boost::format("ADC self-test failed! Ramp checker status: {%s}")%status_str.substr(2)).str()); +    } +} + +void x300_impl::extended_adc_test(mboard_members_t& mb, double duration_s) +{ +    static const size_t SECS_PER_ITER = 5; +    UHD_MSG(status) << boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...\n") +        % duration_s % SECS_PER_ITER; + +    size_t num_iters = static_cast<size_t>(ceil(duration_s/SECS_PER_ITER)); +    size_t num_failures = 0; +    for (size_t iter = 0; iter < num_iters; iter++) { +        //Print date and time +        boost::posix_time::time_facet *facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S"); +        std::ostringstream time_strm; +        time_strm.imbue(std::locale(std::locale::classic(), facet)); +        time_strm << boost::posix_time::second_clock::local_time(); +        //Run self-test +        UHD_MSG(status) << boost::format("-- [%s] Iteration %06d... ") % time_strm.str() % (iter+1); +        try { +            self_test_adcs(mb, SECS_PER_ITER*1000); +            UHD_MSG(status) << "passed" << std::endl; +        } catch(std::exception &e) { +            num_failures++; +            UHD_MSG(status) << e.what() << std::endl; +        } + +    } +    if (num_failures == 0) { +        UHD_MSG(status) << "Extended ADC Self-Test PASSED\n"; +    } else { +        throw uhd::runtime_error( +                (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)\n") % num_failures % num_iters).str()); +    } +} + +/*********************************************************************** + * ADC: Self-calibration operations + **********************************************************************/ + +void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status) +{ +    radio_perifs_t& perif = mb.radio_perifs[radio_i]; +    if (print_status) UHD_MSG(status) << "Running ADC capture delay self-cal..." << std::flush; + +    static const boost::uint32_t NUM_DELAY_STEPS = 32;   //The IDELAYE2 element has 32 steps +    static const boost::uint32_t NUM_RETRIES     = 2;    //Retry self-cal if it fails in warmup situations +    static const boost::int32_t  MIN_WINDOW_LEN  = 4; + +    boost::int32_t win_start = -1, win_stop = -1; +    boost::uint32_t iter = 0; +    while (iter++ < NUM_RETRIES) { +        for (boost::uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) { +            //Apply delay +            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, dly_tap); +            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1); +            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0); + +            boost::uint32_t err_code = 0; + +            // -- Test I Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            perif.adc->set_test_word("ramp", "ones"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            //10ms @ 200MHz = 2 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +            if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_I_LOCKED)) { +                err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_I_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            // -- Test Q Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            perif.adc->set_test_word("ones", "ramp"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            //10ms @ 200MHz = 2 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +            if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_Q_LOCKED)) { +                err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_Q_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            if (err_code == 0) { +                if (win_start == -1) {      //This is the first window +                    win_start = dly_tap; +                    win_stop = dly_tap; +                } else {                    //We are extending the window +                    win_stop = dly_tap; +                } +            } else { +                if (win_start != -1) {      //A valid window turned invalid +                    if (win_stop - win_start >= MIN_WINDOW_LEN) { +                        break;              //Valid window found +                    } else { +                        win_start = -1;     //Reset window +                    } +                } +            } +            //UHD_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % dly_tap % err_code); +        } + +        //Retry the self-cal if it fails +        if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN) && iter < NUM_RETRIES /*not last iteration*/) { +            win_start = -1; +            win_stop = -1; +            boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); +        } else { +            break; +        } +    } +    perif.adc->set_test_word("normal", "normal"); +    perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); + +    if (win_start == -1) { +        throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Convergence error."); +    } + +    if (win_stop-win_start < MIN_WINDOW_LEN) { +        throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Valid window too narrow."); +    } + +    boost::uint32_t ideal_tap = (win_stop + win_start) / 2; +    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, ideal_tap); +    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1); +    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0); + +    if (print_status) { +        double tap_delay = (1.0e12 / mb.clock->get_master_clock_rate()) / (2*32); //in ps +        UHD_MSG(status) << boost::format(" done (Tap=%d, Window=%d, TapDelay=%.3fps, Iter=%d)\n") % ideal_tap % (win_stop-win_start) % tap_delay % iter; +    } +} + +double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay) +{ +    UHD_MSG(status) << "Running ADC transfer delay self-cal: " << std::flush; + +    //Effective resolution of the self-cal. +    static const size_t NUM_DELAY_STEPS = 100; + +    double master_clk_period = (1.0e9 / mb.clock->get_master_clock_rate()); //in ns +    double delay_start = 0.0; +    double delay_range = 2 * master_clk_period; +    double delay_incr = delay_range / NUM_DELAY_STEPS; + +    UHD_MSG(status) << "Measuring..." << std::flush; +    double cached_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_ADC0); +    double fpga_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_FPGA); + +    //Iterate through several values of delays and measure ADC data integrity +    std::vector< std::pair<double,bool> > results; +    for (size_t i = 0; i < NUM_DELAY_STEPS; i++) { +        //Delay the ADC clock (will set both Ch0 and Ch1 delays) +        double delay = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr*i + delay_start); +        wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1); + +        boost::uint32_t err_code = 0; +        for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +            //Test each channel (I and Q) individually so as to not accidentally trigger +            //on the data from the other channel if there is a swap + +            // -- Test I Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            mb.radio_perifs[r].adc->set_test_word("ramp", "ones"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            //50ms @ 200MHz = 10 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(50)); +            if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) { +                err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            // -- Test Q Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            mb.radio_perifs[r].adc->set_test_word("ones", "ramp"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            //50ms @ 200MHz = 10 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(50)); +            if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) { +                err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } +        } +        //UHD_MSG(status) << (boost::format("XferDelay=%fns, Error=%d\n") % delay % err_code); +        results.push_back(std::pair<double,bool>(delay, err_code==0)); +    } + +    //Calculate the valid window +    int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1; +    for (size_t i = 0; i < results.size(); i++) { +        std::pair<double,bool>& item = results[i]; +        if (item.second) {  //If data is stable +            if (cur_start_idx == -1) {  //This is the first window +                cur_start_idx = i; +                cur_stop_idx = i; +            } else {                    //We are extending the window +                cur_stop_idx = i; +            } +        } else { +            if (cur_start_idx == -1) {  //We haven't yet seen valid data +                //Do nothing +            } else if (win_start_idx == -1) {   //We passed the first valid window +                win_start_idx = cur_start_idx; +                win_stop_idx = cur_stop_idx; +            } else {                    //Update cached window if current window is larger +                double cur_win_len = results[cur_stop_idx].first - results[cur_start_idx].first; +                double cached_win_len = results[win_stop_idx].first - results[win_start_idx].first; +                if (cur_win_len > cached_win_len) { +                    win_start_idx = cur_start_idx; +                    win_stop_idx = cur_stop_idx; +                } +            } +            //Reset current window +            cur_start_idx = -1; +            cur_stop_idx = -1; +        } +    } +    if (win_start_idx == -1) { +        throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Convergence error."); +    } + +    double win_center = (results[win_stop_idx].first + results[win_start_idx].first) / 2.0; +    double win_length = results[win_stop_idx].first - results[win_start_idx].first; +    if (win_length < master_clk_period/4) { +        throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Valid window too narrow."); +    } + +    //Cycle slip the relative delay by a clock cycle to prevent sample misalignment +    //fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need +    bool cycle_slip = (win_center-fpga_clk_delay >= master_clk_period); +    if (cycle_slip) { +        win_center -= master_clk_period; +    } + +    if (apply_delay) { +        UHD_MSG(status) << "Validating..." << std::flush; +        //Apply delay +        win_center = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, win_center);  //Sets ADC0 and ADC1 +        wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1); +        //Validate +        self_test_adcs(mb, 2000); +    } else { +        //Restore delay +        mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, cached_clk_delay);  //Sets ADC0 and ADC1 +    } + +    //Teardown +    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +        mb.radio_perifs[r].adc->set_test_word("normal", "normal"); +        mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +    } +    UHD_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") % +        (win_center-fpga_clk_delay) % (cycle_slip?" +cyc":"") % win_length); + +    return win_center; +} diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index ef6e41a83..82ed5bfe2 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -37,7 +37,6 @@  #include <uhd/transport/nirio_zero_copy.hpp>  #include <uhd/transport/nirio/niusrprio_session.h>  #include <uhd/utils/platform.hpp> -#include <boost/date_time/posix_time/posix_time_io.hpp>  #define NIUSRPRIO_DEFAULT_RPC_PORT "5444" @@ -1549,53 +1548,6 @@ bool x300_impl::is_pps_present(wb_iface::sptr ctrl)  }  /*********************************************************************** - * reset and synchronization logic - **********************************************************************/ - -void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) -{ -    if (radios.size() < 2) return;  //Nothing to synchronize - -    //**PRECONDITION** -    //This function assumes that all the VITA times in "radios" are synchronized -    //to a common reference. Currently, this function is called in get_tx_stream -    //which also has the same precondition. - -    //Reinitialize and resync all DACs -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->dac->reset_and_resync(); -    } - -    //Get a rough estimate of the cumulative command latency -    boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call -    } -    boost::posix_time::time_duration t_elapsed = -        boost::posix_time::microsec_clock::local_time() - t_start; - -    //Add 100% of headroom + uncertaintly to the command time -    boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; - -    //Pick radios[0] as the time reference. -    uhd::time_spec_t sync_time = -        radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); - -    //Send the sync command -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->ctrl->set_time(sync_time); -        radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1);    //Arm FRAMEP/N sync pulse -        radios[i]->ctrl->set_time(uhd::time_spec_t(0.0));   //Clear command time -    } - -    //Wait and check status -    boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->dac->verify_sync(); -    } -} - -/***********************************************************************   * eeprom   **********************************************************************/ @@ -1907,341 +1859,3 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo      }      return mb_type;  } - -void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status) -{ -    radio_perifs_t& perif = mb.radio_perifs[radio_i]; -    if (print_status) UHD_MSG(status) << "Running ADC capture delay self-cal..." << std::flush; - -    static const boost::uint32_t NUM_DELAY_STEPS = 32;   //The IDELAYE2 element has 32 steps -    static const boost::uint32_t NUM_RETRIES     = 2;    //Retry self-cal if it fails in warmup situations -    static const boost::int32_t  MIN_WINDOW_LEN  = 4; - -    boost::int32_t win_start = -1, win_stop = -1; -    boost::uint32_t iter = 0; -    while (iter++ < NUM_RETRIES) { -        for (boost::uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) { -            //Apply delay -            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, dly_tap); -            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1); -            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0); - -            boost::uint32_t err_code = 0; - -            // -- Test I Channel -- -            //Put ADC in ramp test mode. Tie the other channel to all ones. -            perif.adc->set_test_word("ramp", "ones"); -            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero -            //and count deviations from the expected value -            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); -            //10ms @ 200MHz = 2 million samples -            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); -            if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_I_LOCKED)) { -                err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_I_ERROR); -            } else { -                err_code += 100;    //Increment error code by 100 to indicate no lock -            } - -            // -- Test Q Channel -- -            //Put ADC in ramp test mode. Tie the other channel to all ones. -            perif.adc->set_test_word("ones", "ramp"); -            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero -            //and count deviations from the expected value -            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); -            //10ms @ 200MHz = 2 million samples -            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); -            if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_Q_LOCKED)) { -                err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_Q_ERROR); -            } else { -                err_code += 100;    //Increment error code by 100 to indicate no lock -            } - -            if (err_code == 0) { -                if (win_start == -1) {      //This is the first window -                    win_start = dly_tap; -                    win_stop = dly_tap; -                } else {                    //We are extending the window -                    win_stop = dly_tap; -                } -            } else { -                if (win_start != -1) {      //A valid window turned invalid -                    if (win_stop - win_start >= MIN_WINDOW_LEN) { -                        break;              //Valid window found -                    } else { -                        win_start = -1;     //Reset window -                    } -                } -            } -            //UHD_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % dly_tap % err_code); -        } - -        //Retry the self-cal if it fails -        if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN) && iter < NUM_RETRIES /*not last iteration*/) { -            win_start = -1; -            win_stop = -1; -            boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); -        } else { -            break; -        } -    } -    perif.adc->set_test_word("normal", "normal"); -    perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); - -    if (win_start == -1) { -        throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Convergence error."); -    } - -    if (win_stop-win_start < MIN_WINDOW_LEN) { -        throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Valid window too narrow."); -    } - -    boost::uint32_t ideal_tap = (win_stop + win_start) / 2; -    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, ideal_tap); -    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1); -    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0); - -    if (print_status) { -        double tap_delay = (1.0e12 / mb.clock->get_master_clock_rate()) / (2*32); //in ps -        UHD_MSG(status) << boost::format(" done (Tap=%d, Window=%d, TapDelay=%.3fps, Iter=%d)\n") % ideal_tap % (win_stop-win_start) % tap_delay % iter; -    } -} - -double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay) -{ -    UHD_MSG(status) << "Running ADC transfer delay self-cal: " << std::flush; - -    //Effective resolution of the self-cal. -    static const size_t NUM_DELAY_STEPS = 100; - -    double master_clk_period = (1.0e9 / mb.clock->get_master_clock_rate()); //in ns -    double delay_start = 0.0; -    double delay_range = 2 * master_clk_period; -    double delay_incr = delay_range / NUM_DELAY_STEPS; - -    UHD_MSG(status) << "Measuring..." << std::flush; -    double cached_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_ADC0); -    double fpga_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_FPGA); - -    //Iterate through several values of delays and measure ADC data integrity -    std::vector< std::pair<double,bool> > results; -    for (size_t i = 0; i < NUM_DELAY_STEPS; i++) { -        //Delay the ADC clock (will set both Ch0 and Ch1 delays) -        double delay = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr*i + delay_start); -        wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1); - -        boost::uint32_t err_code = 0; -        for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { -            //Test each channel (I and Q) individually so as to not accidentally trigger -            //on the data from the other channel if there is a swap - -            // -- Test I Channel -- -            //Put ADC in ramp test mode. Tie the other channel to all ones. -            mb.radio_perifs[r].adc->set_test_word("ramp", "ones"); -            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero -            //and count deviations from the expected value -            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); -            //50ms @ 200MHz = 10 million samples -            boost::this_thread::sleep(boost::posix_time::milliseconds(50)); -            if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) { -                err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR); -            } else { -                err_code += 100;    //Increment error code by 100 to indicate no lock -            } - -            // -- Test Q Channel -- -            //Put ADC in ramp test mode. Tie the other channel to all ones. -            mb.radio_perifs[r].adc->set_test_word("ones", "ramp"); -            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero -            //and count deviations from the expected value -            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); -            //50ms @ 200MHz = 10 million samples -            boost::this_thread::sleep(boost::posix_time::milliseconds(50)); -            if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) { -                err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR); -            } else { -                err_code += 100;    //Increment error code by 100 to indicate no lock -            } -        } -        //UHD_MSG(status) << (boost::format("XferDelay=%fns, Error=%d\n") % delay % err_code); -        results.push_back(std::pair<double,bool>(delay, err_code==0)); -    } - -    //Calculate the valid window -    int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1; -    for (size_t i = 0; i < results.size(); i++) { -        std::pair<double,bool>& item = results[i]; -        if (item.second) {  //If data is stable -            if (cur_start_idx == -1) {  //This is the first window -                cur_start_idx = i; -                cur_stop_idx = i; -            } else {                    //We are extending the window -                cur_stop_idx = i; -            } -        } else { -            if (cur_start_idx == -1) {  //We haven't yet seen valid data -                //Do nothing -            } else if (win_start_idx == -1) {   //We passed the first valid window -                win_start_idx = cur_start_idx; -                win_stop_idx = cur_stop_idx; -            } else {                    //Update cached window if current window is larger -                double cur_win_len = results[cur_stop_idx].first - results[cur_start_idx].first; -                double cached_win_len = results[win_stop_idx].first - results[win_start_idx].first; -                if (cur_win_len > cached_win_len) { -                    win_start_idx = cur_start_idx; -                    win_stop_idx = cur_stop_idx; -                } -            } -            //Reset current window -            cur_start_idx = -1; -            cur_stop_idx = -1; -        } -    } -    if (win_start_idx == -1) { -        throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Convergence error."); -    } - -    double win_center = (results[win_stop_idx].first + results[win_start_idx].first) / 2.0; -    double win_length = results[win_stop_idx].first - results[win_start_idx].first; -    if (win_length < master_clk_period/4) { -        throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Valid window too narrow."); -    } - -    //Cycle slip the relative delay by a clock cycle to prevent sample misalignment -    //fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need -    bool cycle_slip = (win_center-fpga_clk_delay >= master_clk_period); -    if (cycle_slip) { -        win_center -= master_clk_period; -    } - -    if (apply_delay) { -        UHD_MSG(status) << "Validating..." << std::flush; -        //Apply delay -        win_center = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, win_center);  //Sets ADC0 and ADC1 -        wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1); -        //Validate -        self_test_adcs(mb, 2000); -    } else { -        //Restore delay -        mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, cached_clk_delay);  //Sets ADC0 and ADC1 -    } - -    //Teardown -    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { -        mb.radio_perifs[r].adc->set_test_word("normal", "normal"); -        mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -    } -    UHD_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") % -        (win_center-fpga_clk_delay) % (cycle_slip?" +cyc":"") % win_length); - -    return win_center; -} - -static void check_adc(wb_iface::sptr iface, const boost::uint32_t val, const boost::uint32_t i) -{ -    boost::uint32_t adc_rb = iface->peek32(RB32_RX); -    adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA -    if (val != adc_rb) { -        throw uhd::runtime_error( -            (boost::format("ADC self-test failed for Radio%d. (Exp=0x%x, Got=0x%x)")%i%val%adc_rb).str()); -    } -} - -void x300_impl::self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms) { -    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { -        radio_perifs_t &perif = mb.radio_perifs[r]; - -        //First test basic patterns -        perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc,r); -        perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000,r); -        perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000,r); -        perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc,r); -        for (size_t k = 0; k < 14; k++) -        { -            perif.adc->set_test_word("zeros", "custom", 1 << k); -            check_adc(perif.ctrl, 1 << (k+2),r); -        } -        for (size_t k = 0; k < 14; k++) -        { -            perif.adc->set_test_word("custom", "zeros", 1 << k); -            check_adc(perif.ctrl, 1 << (k+18),r); -        } - -        //Turn on ramp pattern test -        perif.adc->set_test_word("ramp", "ramp"); -        perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); -        perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); -    } -    boost::this_thread::sleep(boost::posix_time::milliseconds(ramp_time_ms)); - -    bool passed = true; -    std::string status_str; -    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { -        radio_perifs_t &perif = mb.radio_perifs[r]; -        perif.misc_ins->refresh(); - -        std::string i_status, q_status; -        if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) -            if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR)) -                i_status = "Bit Errors!"; -            else -                i_status = "Good"; -        else -            i_status = "Not Locked!"; - -        if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) -            if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR)) -                q_status = "Bit Errors!"; -            else -                q_status = "Good"; -        else -            q_status = "Not Locked!"; - -        passed = passed && (i_status == "Good") && (q_status == "Good"); -        status_str += (boost::format(", ADC%d_I=%s, ADC%d_Q=%s")%r%i_status%r%q_status).str(); - -        //Return to normal mode -        perif.adc->set_test_word("normal", "normal"); -    } - -    if (not passed) { -        throw uhd::runtime_error( -            (boost::format("ADC self-test failed! Ramp checker status: {%s}")%status_str.substr(2)).str()); -    } -} - -void x300_impl::extended_adc_test(mboard_members_t& mb, double duration_s) -{ -    static const size_t SECS_PER_ITER = 5; -    UHD_MSG(status) << boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...\n") -        % duration_s % SECS_PER_ITER; - -    size_t num_iters = static_cast<size_t>(ceil(duration_s/SECS_PER_ITER)); -    size_t num_failures = 0; -    for (size_t iter = 0; iter < num_iters; iter++) { -        //Print date and time -        boost::posix_time::time_facet *facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S"); -        std::ostringstream time_strm; -        time_strm.imbue(std::locale(std::locale::classic(), facet)); -        time_strm << boost::posix_time::second_clock::local_time(); -        //Run self-test -        UHD_MSG(status) << boost::format("-- [%s] Iteration %06d... ") % time_strm.str() % (iter+1); -        try { -            self_test_adcs(mb, SECS_PER_ITER*1000); -            UHD_MSG(status) << "passed" << std::endl; -        } catch(std::exception &e) { -            num_failures++; -            UHD_MSG(status) << e.what() << std::endl; -        } - -    } -    if (num_failures == 0) { -        UHD_MSG(status) << "Extended ADC Self-Test PASSED\n"; -    } else { -        throw uhd::runtime_error( -                (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)\n") % num_failures % num_iters).str()); -    } -} | 
