aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/x300/x300_impl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/x300/x300_impl.cpp')
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp631
1 files changed, 532 insertions, 99 deletions
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index aff150acb..c742d34df 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2013-2014 Ettus Research LLC
+// Copyright 2013-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
@@ -16,7 +16,6 @@
//
#include "x300_impl.hpp"
-#include "x300_regs.hpp"
#include "x300_lvbitx.hpp"
#include "x310_lvbitx.hpp"
#include <boost/algorithm/string.hpp>
@@ -38,10 +37,11 @@
#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"
-#define X300_REV(x) (x - "A" + 1)
+#define X300_REV(x) ((x) - "A" + 1)
using namespace uhd;
using namespace uhd::usrp;
@@ -236,7 +236,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu
return addrs;
}
-static device_addrs_t x300_find(const device_addr_t &hint_)
+device_addrs_t x300_find(const device_addr_t &hint_)
{
//handle the multi-device discovery
device_addrs_t hints = separate_device_addr(hint_);
@@ -400,7 +400,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
default:
nirio_status_to_exception(status, "Motherboard detection error. Please ensure that you \
have a valid USRP X3x0, NI USRP-294xR or NI USRP-295xR device and that all the device \
- driver have been loaded.");
+ drivers have loaded successfully.");
}
//Load the lvbitx onto the device
UHD_MSG(status) << boost::format("Using LVBITX bitfile %s...\n") % lvbitx->get_bitfile_path();
@@ -508,9 +508,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
x300_load_fw(mb.zpu_ctrl, x300_fw_image);
}
- //check compat -- good place to do after conditional loading
+ //check compat numbers
+ //check fpga compat before fw compat because the fw is a subset of the fpga image
+ this->check_fpga_compat(mb_path, mb);
this->check_fw_compat(mb_path, mb.zpu_ctrl);
- this->check_fpga_compat(mb_path, mb.zpu_ctrl);
//store which FPGA image is loaded
mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl);
@@ -558,6 +559,13 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
.set(mb_eeprom)
.subscribe(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1));
+ bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom");
+ if (recover_mb_eeprom) {
+ UHD_MSG(warning) << "UHD is operating in EEPROM Recovery Mode which disables hardware version "
+ "checks.\nOperating in this mode may cause hardware damage and unstable "
+ "radio performance!"<< std::endl;
+ }
+
////////////////////////////////////////////////////////////////////
// parse the product number
////////////////////////////////////////////////////////////////////
@@ -570,7 +578,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
product_name = "X310";
break;
default:
- break;
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("Unrecognized product type.\n"
+ "Either the software does not support this device in which case please update your driver software to the latest version and retry OR\n"
+ "The product code in the EEPROM is corrupt and may require reprogramming.");
}
_tree->create<std::string>(mb_path / "name").set(product_name);
_tree->create<std::string>(mb_path / "codename").set("Yetti");
@@ -602,36 +613,57 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
}
////////////////////////////////////////////////////////////////////
- // create clock control objects
+ // read hardware revision and compatibility number
////////////////////////////////////////////////////////////////////
- UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl;
-
mb.hw_rev = 0;
if(mb_eeprom.has_key("revision") and not mb_eeprom["revision"].empty()) {
try {
mb.hw_rev = boost::lexical_cast<size_t>(mb_eeprom["revision"]);
} catch(...) {
- UHD_MSG(warning) << "Revision in EEPROM is invalid! Please reprogram your EEPROM." << std::endl;
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("Revision in EEPROM is invalid! Please reprogram your EEPROM.");
}
} else {
- UHD_MSG(warning) << "No revision detected MB EEPROM must be reprogrammed!" << std::endl;
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("No revision detected. MB EEPROM must be reprogrammed!");
}
- if(mb.hw_rev == 0) {
- UHD_MSG(warning) << "Defaulting to X300 RevD Clock Settings. This will result in non-optimal lock times." << std::endl;
- mb.hw_rev = X300_REV("D");
+ size_t hw_rev_compat = 0;
+ if (mb.hw_rev >= 7) { //Revision compat was added with revision 7
+ if (mb_eeprom.has_key("revision_compat") and not mb_eeprom["revision_compat"].empty()) {
+ try {
+ hw_rev_compat = boost::lexical_cast<size_t>(mb_eeprom["revision_compat"]);
+ } catch(...) {
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("Revision compat in EEPROM is invalid! Please reprogram your EEPROM.");
+ }
+ } else {
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error("No revision compat detected. MB EEPROM must be reprogrammed!");
+ }
+ } else {
+ //For older HW just assume that revision_compat = revision
+ hw_rev_compat = mb.hw_rev;
}
- if (mb.hw_rev > X300_MAX_HW_REV) {
- throw uhd::runtime_error(str(
- boost::format("Unsupported board revision number: %d.\n"
- "The maximum board revision number supported in this version is %d.\n"
- "Please update your UHD version.")
- % mb.hw_rev % X300_MAX_HW_REV
- ));
+ if (hw_rev_compat > X300_REVISION_COMPAT) {
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error(str(boost::format(
+ "Hardware is too new for this software. Please upgrade to a driver that supports hardware revision %d.")
+ % mb.hw_rev));
+ } else if (mb.hw_rev < X300_REVISION_MIN) { //Compare min against the revision (and not compat) to give us more leeway for partial support for a compat
+ if (not recover_mb_eeprom)
+ throw uhd::runtime_error(str(boost::format(
+ "Software is too new for this hardware. Please downgrade to a driver that supports hardware revision %d.")
+ % mb.hw_rev));
}
- //Create clock control. NOTE: This does not configure the LMK yet.
+ ////////////////////////////////////////////////////////////////////
+ // create clock control objects
+ ////////////////////////////////////////////////////////////////////
+ UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl;
+
+ //Initialize clock control registers. NOTE: This does not configure the LMK yet.
initialize_clock_control(mb);
mb.clock = x300_clock_ctrl::make(mb.zpu_spi,
1 /*slaveno*/,
@@ -696,23 +728,33 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)
////////////////////////////////////////////////////////////////////
// setup radios
////////////////////////////////////////////////////////////////////
- UHD_MSG(status) << "Initialize Radio control..." << std::endl;
- this->setup_radio(mb_i, "A");
- this->setup_radio(mb_i, "B");
+ this->setup_radio(mb_i, "A", dev_addr);
+ this->setup_radio(mb_i, "B", dev_addr);
+
+ ////////////////////////////////////////////////////////////////////
+ // ADC test and cal
+ ////////////////////////////////////////////////////////////////////
+ if (dev_addr.has_key("self_cal_adc_delay")) {
+ self_cal_adc_xfer_delay(mb, true /* Apply ADC delay */);
+ }
+ if (dev_addr.has_key("ext_adc_self_test")) {
+ extended_adc_test(mb, dev_addr.cast<double>("ext_adc_self_test", 30));
+ } else {
+ self_test_adcs(mb);
+ }
////////////////////////////////////////////////////////////////////
// front panel gpio
////////////////////////////////////////////////////////////////////
mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO);
- const std::vector<std::string> GPIO_ATTRS = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX");
- BOOST_FOREACH(const std::string &attr, GPIO_ATTRS)
+ BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)
{
- _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr)
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second)
.set(0)
- .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr, _1));
+ .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr.first, _1));
}
_tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK")
- .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio, "READBACK"));
+ .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio));
////////////////////////////////////////////////////////////////////
// register the time keepers - only one can be the highlander
@@ -829,8 +871,15 @@ x300_impl::~x300_impl(void)
{
BOOST_FOREACH(mboard_members_t &mb, _mb)
{
- mb.radio_perifs[0].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC
- mb.radio_perifs[1].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC
+ //disable/reset ADC/DAC
+ mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1);
+ mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0);
+ mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 0);
+ mb.radio_perifs[0].misc_outs->flush();
+ mb.radio_perifs[1].misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1);
+ mb.radio_perifs[1].misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0);
+ mb.radio_perifs[1].misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 0);
+ mb.radio_perifs[1].misc_outs->flush();
//kill the claimer task and unclaim the device
mb.claimer_task.reset();
@@ -850,15 +899,7 @@ x300_impl::~x300_impl(void)
}
}
-static void check_adc(wb_iface::sptr iface, const boost::uint32_t val)
-{
- boost::uint32_t adc_rb = iface->peek32(RB32_RX);
- adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA
- //UHD_MSG(status) << "adc_rb " << std::hex << adc_rb << " val " << std::hex << val << std::endl;
- UHD_ASSERT_THROW(adc_rb == val);
-}
-
-void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
+void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, const uhd::device_addr_t &dev_addr)
{
const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i);
UHD_ASSERT_THROW(mb_i < _mb.size());
@@ -866,6 +907,8 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
const size_t radio_index = mb.get_radio_index(slot_name);
radio_perifs_t &perif = mb.radio_perifs[radio_index];
+ UHD_MSG(status) << boost::format("Initialize Radio%d control...") % radio_index << std::endl;
+
////////////////////////////////////////////////////////////////////
// radio control
////////////////////////////////////////////////////////////////////
@@ -873,8 +916,20 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
boost::uint32_t ctrl_sid;
both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid);
perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, slot_name);
- perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //reset adc + dac
- perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 1) | (1 << 0)); //out of reset + dac enable
+
+ perif.misc_outs = boost::make_shared<radio_misc_outs_reg>();
+ perif.misc_ins = boost::make_shared<radio_misc_ins_reg>();
+ perif.misc_outs->initialize(*perif.ctrl, true);
+ perif.misc_ins->initialize(*perif.ctrl);
+
+ //reset adc + dac
+ perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1);
+ perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0);
+ perif.misc_outs->flush();
+ perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 0);
+ perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 1);
+ perif.misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 1);
+ perif.misc_outs->flush();
this->register_loopback_self_test(perif.ctrl);
@@ -883,31 +938,16 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)
perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate());
perif.leds = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_LEDS));
+ //Capture delays are calibrated every time. The status is only printed is the user
+ //asks to run the xfer self cal using "self_cal_adc_delay"
+ self_cal_adc_capture_delay(mb, radio_index, dev_addr.has_key("self_cal_adc_delay"));
+
_tree->access<time_spec_t>(mb_path / "time" / "cmd")
.subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1));
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1));
////////////////////////////////////////////////////////////////
- // ADC self test
- ////////////////////////////////////////////////////////////////
- perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc);
- perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000);
- perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000);
- perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc);
- for (size_t k = 0; k < 14; k++)
- {
- perif.adc->set_test_word("zeros", "custom", 1 << k);
- check_adc(perif.ctrl, 1 << (k+2));
- }
- for (size_t k = 0; k < 14; k++)
- {
- perif.adc->set_test_word("custom", "zeros", 1 << k);
- check_adc(perif.ctrl, 1 << (k+18));
- }
- perif.adc->set_test_word("normal", "normal");
-
- ////////////////////////////////////////////////////////////////
// create codec control objects
////////////////////////////////////////////////////////////////
_tree->create<int>(mb_path / "rx_codecs" / slot_name / "gains"); //phony property so this dir exists
@@ -1442,8 +1482,8 @@ bool x300_impl::wait_for_ref_locked(wb_iface::sptr ctrl, double timeout)
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
} while (boost::get_system_time() < timeout_time);
- //failed to lock on reference
- return false;
+ //Check one last time
+ return get_ref_locked(ctrl).to_bool();
}
sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl)
@@ -1544,20 +1584,24 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep
* front-panel GPIO
**********************************************************************/
-boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &)
+boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio)
{
return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX));
}
-void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, const boost::uint32_t value)
+void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value)
{
- if (attr == "CTRL") return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value);
- if (attr == "DDR") return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value);
- if (attr == "OUT") return gpio->set_gpio_out(dboard_iface::UNIT_RX, value);
- if (attr == "ATR_0X") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value);
- if (attr == "ATR_RX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value);
- if (attr == "ATR_TX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value);
- if (attr == "ATR_XX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value);
+ switch (attr)
+ {
+ case GPIO_CTRL: return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value);
+ case GPIO_DDR: return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value);
+ case GPIO_OUT: return gpio->set_gpio_out(dboard_iface::UNIT_RX, value);
+ case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value);
+ case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value);
+ case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value);
+ case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value);
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
}
/***********************************************************************
@@ -1688,25 +1732,33 @@ void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface)
% compat_major % compat_minor));
}
-void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface)
+void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t &members)
{
- boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));
+ boost::uint32_t compat_num = members.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));
boost::uint32_t compat_major = (compat_num >> 16);
boost::uint32_t compat_minor = (compat_num & 0xffff);
if (compat_major != X300_FPGA_COMPAT_MAJOR)
{
+ std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+ std::string image_loader_cmd = str(boost::format("\"%s\" --args=\"type=x300,%s=%s\"")
+ % image_loader_path
+ % (members.xport_path == "eth" ? "addr"
+ : "resource")
+ % members.addr);
+
throw uhd::runtime_error(str(boost::format(
"Expected FPGA compatibility number %d, but got %d:\n"
"The FPGA image on your device is not compatible with this host code build.\n"
"Download the appropriate FPGA images for this version of UHD.\n"
"%s\n\n"
"Then burn a new image to the on-board flash storage of your\n"
- "USRP X3xx device using the burner utility. %s\n\n"
+ "USRP X3xx device using the image loader utility. Use this command:\n\n%s\n\n"
"For more information, refer to the UHD manual:\n\n"
" http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_flash"
) % int(X300_FPGA_COMPAT_MAJOR) % compat_major
- % print_utility_error("uhd_images_downloader.py") % print_utility_error("usrp_x3xx_fpga_burner")));
+ % print_utility_error("uhd_images_downloader.py")
+ % image_loader_cmd));
}
_tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u")
% compat_major % compat_minor));
@@ -1727,17 +1779,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& res
if (nirio_status_not_fatal(status)) {
//The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping
switch (pid) {
- case X300_USRP_PCIE_SSID:
+ case X300_USRP_PCIE_SSID_ADC_33:
+ case X300_USRP_PCIE_SSID_ADC_18:
mb_type = USRP_X300_MB; break;
- case X310_USRP_PCIE_SSID:
- case X310_2940R_PCIE_SSID:
- case X310_2942R_PCIE_SSID:
- case X310_2943R_PCIE_SSID:
- case X310_2944R_PCIE_SSID:
- case X310_2950R_PCIE_SSID:
- case X310_2952R_PCIE_SSID:
- case X310_2953R_PCIE_SSID:
- case X310_2954R_PCIE_SSID:
+ case X310_USRP_PCIE_SSID_ADC_33:
+ case X310_2940R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2940R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2942R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2942R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2943R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2943R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2944R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2950R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2950R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2952R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2952R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2953R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2953R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2954R_40MHz_PCIE_SSID_ADC_33:
+ case X310_USRP_PCIE_SSID_ADC_18:
+ case X310_2940R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2940R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2942R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2942R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2943R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2943R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2944R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2950R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2950R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2952R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2952R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2953R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2953R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2954R_40MHz_PCIE_SSID_ADC_18:
mb_type = USRP_X310_MB; break;
default:
mb_type = UNKNOWN; break;
@@ -1762,17 +1836,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo
switch (product_num) {
//The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping
- case X300_USRP_PCIE_SSID:
+ case X300_USRP_PCIE_SSID_ADC_33:
+ case X300_USRP_PCIE_SSID_ADC_18:
mb_type = USRP_X300_MB; break;
- case X310_USRP_PCIE_SSID:
- case X310_2940R_PCIE_SSID:
- case X310_2942R_PCIE_SSID:
- case X310_2943R_PCIE_SSID:
- case X310_2944R_PCIE_SSID:
- case X310_2950R_PCIE_SSID:
- case X310_2952R_PCIE_SSID:
- case X310_2953R_PCIE_SSID:
- case X310_2954R_PCIE_SSID:
+ case X310_USRP_PCIE_SSID_ADC_33:
+ case X310_2940R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2940R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2942R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2942R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2943R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2943R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2944R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2950R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2950R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2952R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2952R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2953R_40MHz_PCIE_SSID_ADC_33:
+ case X310_2953R_120MHz_PCIE_SSID_ADC_33:
+ case X310_2954R_40MHz_PCIE_SSID_ADC_33:
+ case X310_USRP_PCIE_SSID_ADC_18:
+ case X310_2940R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2940R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2942R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2942R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2943R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2943R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2944R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2950R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2950R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2952R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2952R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2953R_40MHz_PCIE_SSID_ADC_18:
+ case X310_2953R_120MHz_PCIE_SSID_ADC_18:
+ case X310_2954R_40MHz_PCIE_SSID_ADC_18:
mb_type = USRP_X310_MB; break;
default:
UHD_MSG(warning) << "X300 unknown product code in EEPROM: " << product_num << std::endl;
@@ -1782,3 +1878,340 @@ 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_ref_locked(mb.zpu_ctrl, 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_ref_locked(mb.zpu_ctrl, 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());
+ }
+}