Example #1
0
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());
    }
}
Example #2
0
/***********************************************************************
 * Setup function
 **********************************************************************/
static uhd::usrp::multi_usrp::sptr setup_usrp_for_cal(std::string &args, std::string &subdev, std::string &serial)
{
    std::cout << std::endl;
    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);

    // Configure subdev
    if (!subdev.empty()) {
        usrp->set_tx_subdev_spec(subdev);
        usrp->set_rx_subdev_spec(subdev);
    }
    UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl;
    serial = get_serial(usrp, "tx");
    UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl;

    //set the antennas to cal
    if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){
        throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate.");
    }
    usrp->set_rx_antenna("CAL");
    usrp->set_tx_antenna("CAL");

    //fail if daughterboard has no serial
    check_for_empty_serial(usrp);

    //set optimum defaults
    set_optimum_defaults(usrp);

    return usrp;
}
Example #3
0
void load_fpga_image(const std::string &path)
{
    if (not boost::filesystem::exists("/dev/xdevcfg"))
        ::system("mknod /dev/xdevcfg c 259 0");

    UHD_MSG(status) << "Loading FPGA image: " << path << "..." << std::flush;

    std::ifstream fpga_file(path.c_str(), std::ios_base::binary);
    UHD_ASSERT_THROW(fpga_file.good());

    std::FILE *wfile;
    wfile = std::fopen("/dev/xdevcfg", "wb");
    UHD_ASSERT_THROW(!(wfile == NULL));

    char buff[16384]; // devcfg driver can't handle huge writes
    do {
        fpga_file.read(buff, sizeof(buff));
        std::fwrite(buff, 1, size_t(fpga_file.gcount()), wfile);
    } while (fpga_file);

    fpga_file.close();
    std::fclose(wfile);

    UHD_MSG(status) << " done" << std::endl;
}
static void apply_fe_corrections(
    uhd::property_tree::sptr sub_tree,
    const uhd::fs_path &db_path,
    const uhd::fs_path &fe_path,
    const std::string &file_prefix,
    const double lo_freq
){
    //extract eeprom serial
    const uhd::usrp::dboard_eeprom_t db_eeprom = sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get();

    //make the calibration file path
    const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal" / (file_prefix + db_eeprom.serial + ".csv");
    UHD_MSG(status) << "Looking for FE correction at: " << cal_data_path.c_str() << "...  ";
    if (not fs::exists(cal_data_path)) {
        UHD_MSG(status) << "Not found" << std::endl;
        return;
    }

    UHD_MSG(status) << "Found, loading...  ";

    //parse csv file or get from cache
    if (not fe_cal_cache.has_key(cal_data_path.string())){
        std::ifstream cal_data(cal_data_path.string().c_str());
        const uhd::csv::rows_type rows = uhd::csv::to_rows(cal_data);

        bool read_data = false, skip_next = false;;
        std::vector<fe_cal_t> datas;
        BOOST_FOREACH(const uhd::csv::row_type &row, rows){
            if (not read_data and not row.empty() and row[0] == "DATA STARTS HERE"){
                read_data = true;
                skip_next = true;
                continue;
            }
            if (not read_data) continue;
            if (skip_next){
                skip_next = false;
                continue;
            }
            fe_cal_t data;
            std::sscanf(row[0].c_str(), "%lf" , &data.lo_freq);
            std::sscanf(row[1].c_str(), "%lf" , &data.iq_corr_real);
            std::sscanf(row[2].c_str(), "%lf" , &data.iq_corr_imag);
            datas.push_back(data);
        }
        std::sort(datas.begin(), datas.end(), fe_cal_comp);
        fe_cal_cache[cal_data_path.string()] = datas;
        UHD_MSG(status) << "Loaded" << std::endl;
    } else {
Example #5
0
static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &done)
{
	std::ifstream bitstream;

	bitstream.open(file_name.c_str(), std::ios::binary);
	if (!bitstream.is_open()) throw uhd::os_error(
		"Could not open the file: " + file_name
	);

	spidev spi("/dev/spidev1.0");
	char buf[BUF_SIZE];
	char rbuf[BUF_SIZE];

	do {
		bitstream.read(buf, BUF_SIZE);
		spi.send(buf, rbuf, bitstream.gcount());

		if (error.get_value())
			throw uhd::os_error("INIT_B went high, error occured.");

		if (!done.get_value())
			UHD_MSG(status) << "Configuration complete." << std::endl;

	} while (bitstream.gcount() == BUF_SIZE);
}
bool uhd::set_thread_priority_safe(float priority, bool realtime){
    try{
        set_thread_priority(priority, realtime);
        return true;
    }catch(const std::exception &e){
        UHD_MSG(warning) << boost::format(
            "Unable to set the thread priority. Performance may be negatively affected.\n"
            "Please see the general application notes in the manual for instructions.\n"
            "%s\n"
        ) % e.what();
        return false;
    }
}
Example #7
0
/*!
 *    3                   2                   1                   0
 *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
 * +-----------------------+-------+-------+-------+-------+-+-----+
 * |      must be zero     | Q3| I3| Q2| I2| Q1| I1| Q0| I0|Z| NCH |
 * +-----------------------+-------+-------+-------+-------+-+-----+
 */
static uint32_t calc_rx_mux(const std::vector<mapping_pair_t> &mapping){
    //create look-up-table for mapping dboard name and connection type to ADC flags
    static const int ADC0 = 0, ADC1 = 1, ADC2 = 2, ADC3 = 3;
    static const uhd::dict<std::string, uhd::dict<std::string, int> > name_to_conn_to_flag = boost::assign::map_list_of
        ("A", boost::assign::map_list_of
            ("IQ", calc_rx_mux_pair(ADC0, ADC1)) //I and Q
            ("QI", calc_rx_mux_pair(ADC1, ADC0)) //I and Q
            ("I",  calc_rx_mux_pair(ADC0, ADC0)) //I and Q (Q identical but ignored Z=1)
            ("Q",  calc_rx_mux_pair(ADC1, ADC1)) //I and Q (Q identical but ignored Z=1)
        )
        ("B", boost::assign::map_list_of
            ("IQ", calc_rx_mux_pair(ADC2, ADC3)) //I and Q
            ("QI", calc_rx_mux_pair(ADC3, ADC2)) //I and Q
            ("I",  calc_rx_mux_pair(ADC2, ADC2)) //I and Q (Q identical but ignored Z=1)
            ("Q",  calc_rx_mux_pair(ADC3, ADC3)) //I and Q (Q identical but ignored Z=1)
        )
    ;

    //extract the number of channels
    const size_t nchan = mapping.size();

    //calculate the channel flags
    int channel_flags = 0;
    size_t num_reals = 0, num_quads = 0;
    BOOST_FOREACH(const mapping_pair_t &pair, uhd::reversed(mapping)){
        const std::string name = pair.first, conn = pair.second;
        if (conn == "IQ" or conn == "QI") num_quads++;
        if (conn == "I" or conn == "Q") num_reals++;
        channel_flags = (channel_flags << 4) | name_to_conn_to_flag[name][conn];
    }

    //calculate Z:
    //    for all real sources: Z = 1
    //    for all quadrature sources: Z = 0
    //    for mixed sources: warning + Z = 0
    int Z = (num_quads > 0)? 0 : 1;
    if (num_quads != 0 and num_reals != 0) UHD_MSG(warning) << boost::format(
        "Mixing real and quadrature rx subdevices is not supported.\n"
        "The Q input to the real source(s) will be non-zero.\n"
    );

    //calculate the rx mux value
    return ((channel_flags & 0xffff) << 4) | ((Z & 0x1) << 3) | ((nchan & 0x7) << 0);
}
Example #8
0
File: log.cpp Project: paneda/uhd
uhd::_log::log::~log(void)
{
    if (not _log_it)
        return;

    _ss << std::endl;
    try {
        log_rs().log_to_file(_ss.str());
    }
    catch(const std::exception &e) {
        /*!
         * Critical behavior below.
         * The following steps must happen in order to avoid a lock-up condition.
         * This is because the message facility will call into the logging facility.
         * Therefore we must disable the logger (level = never) before messaging.
         */
        log_rs().level = never;
        UHD_MSG(error)
                << "Logging failed: " << e.what() << std::endl
                << "Logging has been disabled for this process" << std::endl
                ;
    }
}
Example #9
0
/***********************************************************************
 * Main
 **********************************************************************/
int UHD_SAFE_MAIN(int argc, char *argv[])
{
    std::string args, subdev, serial;
    double tx_wave_freq, tx_wave_ampl, rx_offset;
    double freq_start, freq_stop, freq_step;
    size_t nsamps;
    double precision;

    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "help message")
        ("verbose", "enable some verbose")
        ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]")
        ("subdev", po::value<std::string>(&subdev), "Subdevice specification (default: first subdevice, often 'A')")
        ("tx_wave_freq", po::value<double>(&tx_wave_freq)->default_value(507.123e3), "Transmit wave frequency in Hz")
        ("tx_wave_ampl", po::value<double>(&tx_wave_ampl)->default_value(0.7), "Transmit wave amplitude in counts")
        ("rx_offset", po::value<double>(&rx_offset)->default_value(.9344e6), "RX LO offset from the TX LO in Hz")
        ("freq_start", po::value<double>(&freq_start), "Frequency start in Hz (do not specify for default)")
        ("freq_stop", po::value<double>(&freq_stop), "Frequency stop in Hz (do not specify for default)")
        ("freq_step", po::value<double>(&freq_step)->default_value(default_freq_step), "Step size for LO sweep in Hz")
        ("nsamps", po::value<size_t>(&nsamps), "Samples per data capture")
        ("precision", po::value<double>(&precision)->default_value(default_precision), "Correction precision (default=0.0001)")
    ;

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    //print the help message
    if (vm.count("help"))
    {
        std::cout << boost::format("USRP Generate TX IQ Balance Calibration Table %s") % desc << std::endl;
        std::cout <<
            "This application measures leakage between RX and TX on a transceiver daughterboard to self-calibrate.\n"
            "Note: Not all daughterboards support this feature. Refer to the UHD manual for details.\n"
            << std::endl;
        return EXIT_FAILURE;
    }

    // Create a USRP device
    uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, subdev, serial);

    if (not vm.count("nsamps"))
        nsamps = size_t(usrp->get_rx_rate() / default_fft_bin_size);

    //create a receive streamer
    uhd::stream_args_t stream_args("fc32"); //complex floats
    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);

    //create a transmitter thread
    boost::thread_group threads;
    threads.create_thread(boost::bind(&tx_thread, usrp, tx_wave_freq, tx_wave_ampl));

    //re-usable buffer for samples
    std::vector<samp_type> buff;

    //store the results here
    std::vector<result_t> results;

    if (not vm.count("freq_start")) freq_start = usrp->get_fe_tx_freq_range().start();
    if (not vm.count("freq_stop")) freq_stop = usrp->get_fe_tx_freq_range().stop();

    //check start and stop frequencies
    if (freq_start < usrp->get_fe_tx_freq_range().start())
    {
        std::cerr << "freq_start must be " << usrp->get_fe_tx_freq_range().start() << " or greater for this daughter board" << std::endl;
        return EXIT_FAILURE;
    }
    if (freq_stop > usrp->get_fe_tx_freq_range().stop())
    {
        std::cerr << "freq_stop must be " << usrp->get_fe_tx_freq_range().stop() << " or less for this daughter board" << std::endl;
        return EXIT_FAILURE;
    }

    //check rx_offset
    double min_rx_offset = usrp->get_rx_freq_range().start() - usrp->get_fe_tx_freq_range().start();
    double max_rx_offset = usrp->get_rx_freq_range().stop() - usrp->get_fe_tx_freq_range().stop();
    if (rx_offset < min_rx_offset or rx_offset > max_rx_offset)
    {
        std::cerr << "rx_offset must be between " << min_rx_offset << " and "
            << max_rx_offset << " for this daughter board" << std::endl;
        return EXIT_FAILURE;
    }

    UHD_MSG(status) << boost::format("Calibration frequency range: %d MHz -> %d MHz") % (freq_start/1e6) % (freq_stop/1e6) << std::endl;

    for (double tx_lo_i = freq_start; tx_lo_i <= freq_stop; tx_lo_i += freq_step)
    {
        const double tx_lo = tune_rx_and_tx(usrp, tx_lo_i, rx_offset);

        //frequency constants for this tune event
        const double actual_rx_rate = usrp->get_rx_rate();
        const double actual_tx_freq = usrp->get_tx_freq();
        const double actual_rx_freq = usrp->get_rx_freq();
        const double bb_tone_freq = actual_tx_freq + tx_wave_freq - actual_rx_freq;
        const double bb_imag_freq = actual_tx_freq - tx_wave_freq - actual_rx_freq;

        //reset TX IQ balance
        usrp->set_tx_iq_balance(0.0);

        //set optimal RX gain setting for this frequency
        set_optimal_rx_gain(usrp, rx_stream, tx_wave_freq);

        //capture initial uncorrected value
        capture_samples(usrp, rx_stream, buff, nsamps);
        const double initial_suppression = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate) - compute_tone_dbrms(buff, bb_imag_freq/actual_rx_rate);

        //bounds and results from searching
        double phase_corr_start = -1.0;
        double phase_corr_stop = 1.0;
        double phase_corr_step = (phase_corr_stop - phase_corr_start)/(num_search_steps+1);
        double ampl_corr_start = -1.0;
        double ampl_corr_stop = 1.0;
        double ampl_corr_step = (ampl_corr_stop - ampl_corr_start)/(num_search_steps+1);
        double best_suppression = 0;
        double best_phase_corr = 0;
        double best_ampl_corr = 0;
        while (phase_corr_step >= precision or ampl_corr_step >= precision)
        {
            for (double phase_corr = phase_corr_start + phase_corr_step; phase_corr <= phase_corr_stop - phase_corr_step; phase_corr += phase_corr_step)
            {
                for (double ampl_corr = ampl_corr_start + ampl_corr_step; ampl_corr <= ampl_corr_stop - ampl_corr_step; ampl_corr += ampl_corr_step)
                {
                    const std::complex<double> correction(ampl_corr, phase_corr);
                    usrp->set_tx_iq_balance(correction);

                    //receive some samples
                    capture_samples(usrp, rx_stream, buff, nsamps);
                    const double tone_dbrms = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate);
                    const double imag_dbrms = compute_tone_dbrms(buff, bb_imag_freq/actual_rx_rate);
                    const double suppression = tone_dbrms - imag_dbrms;

                    if (suppression > best_suppression)
                    {
                        best_suppression = suppression;
                        best_phase_corr = phase_corr;
                        best_ampl_corr = ampl_corr;
                    }
                }
            }

            phase_corr_start = best_phase_corr - phase_corr_step;
            phase_corr_stop = best_phase_corr + phase_corr_step;
            phase_corr_step = (phase_corr_stop - phase_corr_start)/(num_search_steps+1);
            ampl_corr_start = best_ampl_corr - ampl_corr_step;
            ampl_corr_stop = best_ampl_corr + ampl_corr_step;
            ampl_corr_step = (ampl_corr_stop - ampl_corr_start)/(num_search_steps+1);
        }

        if (best_suppression > initial_suppression) //keep result
        {
            result_t result;
            result.freq = tx_lo;
            result.real_corr = best_ampl_corr;
            result.imag_corr = best_phase_corr;
            result.best = best_suppression;
            result.delta = best_suppression - initial_suppression;
            results.push_back(result);
            if (vm.count("verbose"))
                std::cout << boost::format("TX IQ: %f MHz: best suppression %f dB, corrected %f dB") % (tx_lo/1e6) % result.best % result.delta << std::endl;
            else
                std::cout << "." << std::flush;
        }
    }
    std::cout << std::endl;

    //stop the transmitter
    threads.interrupt_all();
    boost::this_thread::sleep(boost::posix_time::milliseconds(500));    //wait for threads to finish
    threads.join_all();

    store_results(results, "TX", "tx", "iq", serial);

    return EXIT_SUCCESS;
}
Example #10
0
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;
}
Example #11
0
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;
    }
}