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()); } }
/*********************************************************************** * 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; }
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 {
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; } }
/*! * 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); }
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 ; } }
/*********************************************************************** * 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; }
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; }
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; } }