aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib')
-rw-r--r--host/lib/ic_reg_maps/gen_lmk04816_regs.py2
-rw-r--r--host/lib/usrp/x300/x300_adc_ctrl.cpp4
-rw-r--r--host/lib/usrp/x300/x300_clock_ctrl.cpp243
-rw-r--r--host/lib/usrp/x300/x300_clock_ctrl.hpp18
-rw-r--r--host/lib/usrp/x300/x300_fw_common.h2
-rw-r--r--host/lib/usrp/x300/x300_impl.cpp343
-rw-r--r--host/lib/usrp/x300/x300_impl.hpp48
-rw-r--r--host/lib/usrp/x300/x300_regs.hpp1
8 files changed, 602 insertions, 59 deletions
diff --git a/host/lib/ic_reg_maps/gen_lmk04816_regs.py b/host/lib/ic_reg_maps/gen_lmk04816_regs.py
index e89a82671..d1f0633a4 100644
--- a/host/lib/ic_reg_maps/gen_lmk04816_regs.py
+++ b/host/lib/ic_reg_maps/gen_lmk04816_regs.py
@@ -26,7 +26,7 @@ address0 0[0:4] 0
CLKout0_1_DIV 0[5:15] 25
CLKout0_1_HS 0[16] 0
RESET 0[17] 0 no_reset, reset
-CLKout0_1_DDLY 0[18:27] 0 five
+CLKout0_1_DDLY 0[18:27] 0
CLKout0_ADLY_SEL 0[28] 0 d_pd, d_ev_x, d_odd_y, d_both
CLKout1_ADLY_SEL 0[29] 0 d_pd, d_ev_x, d_odd_y, d_both
Required_0 0[30] 0
diff --git a/host/lib/usrp/x300/x300_adc_ctrl.cpp b/host/lib/usrp/x300/x300_adc_ctrl.cpp
index b0e4e4b95..edb4ce885 100644
--- a/host/lib/usrp/x300/x300_adc_ctrl.cpp
+++ b/host/lib/usrp/x300/x300_adc_ctrl.cpp
@@ -55,8 +55,8 @@ public:
_ads62p48_regs.lvds_cmos = ads62p48_regs_t::LVDS_CMOS_DDR_LVDS;
_ads62p48_regs.channel_control = ads62p48_regs_t::CHANNEL_CONTROL_INDEPENDENT;
_ads62p48_regs.data_format = ads62p48_regs_t::DATA_FORMAT_2S_COMPLIMENT;
- _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26;
- _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26;
+ _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_NORMAL;
+ _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_NORMAL;
this->send_ads62p48_reg(0);
diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp
index e182f649b..350e9e9bc 100644
--- a/host/lib/usrp/x300/x300_clock_ctrl.cpp
+++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp
@@ -29,6 +29,31 @@ static const double X300_REF_CLK_OUT_RATE = 10e6;
static const boost::uint16_t X300_MAX_CLKOUT_DIV = 1045;
static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6;
+struct x300_clk_delays {
+ x300_clk_delays() :
+ fpga_dly_ns(0.0),adc_dly_ns(0.0),dac_dly_ns(0.0),db_rx_dly_ns(0.0),db_tx_dly_ns(0.0)
+ {}
+ x300_clk_delays(double fpga, double adc, double dac, double db_rx, double db_tx) :
+ fpga_dly_ns(fpga),adc_dly_ns(adc),dac_dly_ns(dac),db_rx_dly_ns(db_rx),db_tx_dly_ns(db_tx)
+ {}
+
+ double fpga_dly_ns;
+ double adc_dly_ns;
+ double dac_dly_ns;
+ double db_rx_dly_ns;
+ double db_tx_dly_ns;
+};
+
+// Delay the FPGA_CLK by 900ps to ensure a safe ADC_SSCLK -> RADIO_CLK crossing.
+// If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC
+// because the data interface clock is generated from FPGA_CLK.
+// NOTE: This delay value was verified at room temperature only.
+static const x300_clk_delays X300_REV0_6_CLK_DELAYS = x300_clk_delays(
+ /*fpga=*/0.900, /*adc=*/0.000, /*dac=*/0.900, /*db_rx=*/0.000, /*db_tx=*/0.000);
+
+static const x300_clk_delays X300_REV7_CLK_DELAYS = x300_clk_delays(
+ /*fpga=*/0.000, /*adc=*/4.400, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000);
+
using namespace uhd;
x300_clock_ctrl::~x300_clock_ctrl(void){
@@ -213,6 +238,187 @@ public:
_spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32);
}
+ double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) {
+ //All dividers have are delayed by 5 taps by default. The delay
+ //set by this function is relative to the 5 tap delay
+ static const boost::uint16_t DDLY_MIN_TAPS = 5;
+ static const boost::uint16_t DDLY_MAX_TAPS = 522; //Extended mode
+
+ //The resolution and range of the analog delay is fixed
+ static const double ADLY_RES_NS = 0.025;
+ static const double ADLY_MIN_NS = 0.500;
+ static const double ADLY_MAX_NS = 0.975;
+
+ //Each digital tap delays the clock by one VCO period
+ double vco_period_ns = 1.0e9/_vco_freq;
+ double half_vco_period_ns = vco_period_ns/2.0;
+
+ //Implement as much of the requested delay using digital taps. Whatever is leftover
+ //will be made up using the analog delay element and the half-cycle digital tap.
+ //A caveat here is that the analog delay starts at ADLY_MIN_NS, so we need to back off
+ //by that much when coming up with the digital taps so that the difference can be made
+ //up using the analog delay.
+ boost::uint16_t ddly_taps = 0;
+ if (delay_ns < ADLY_MIN_NS) {
+ ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns)/vco_period_ns));
+ } else {
+ ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns-ADLY_MIN_NS)/vco_period_ns));
+ }
+ double leftover_delay = delay_ns - (vco_period_ns * ddly_taps);
+
+ //Compute settings
+ boost::uint16_t ddly_value = ddly_taps + DDLY_MIN_TAPS;
+ bool adly_en = false;
+ boost::uint8_t adly_value = 0;
+ boost::uint8_t half_shift_en = 0;
+
+ if (ddly_value > DDLY_MAX_TAPS) {
+ throw uhd::value_error("set_clock_delay: Requested delay is out of range.");
+ }
+
+ double coerced_delay = (vco_period_ns * ddly_taps);
+ if (leftover_delay > ADLY_MAX_NS) {
+ //The VCO is running too slowly for us to compensate the digital delay difference using
+ //analog delay. Do the best we can.
+ adly_en = true;
+ adly_value = static_cast<boost::uint8_t>(round((ADLY_MAX_NS-ADLY_MIN_NS)/ADLY_RES_NS));
+ coerced_delay += ADLY_MAX_NS;
+ } else if (leftover_delay >= ADLY_MIN_NS && leftover_delay <= ADLY_MAX_NS) {
+ //The leftover delay can be compensated by the analog delay up to the analog delay resolution
+ adly_en = true;
+ adly_value = static_cast<boost::uint8_t>(round((leftover_delay-ADLY_MIN_NS)/ADLY_RES_NS));
+ coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value);
+ } else if (leftover_delay >= (ADLY_MIN_NS - half_vco_period_ns) && leftover_delay < ADLY_MIN_NS) {
+ //The leftover delay if less than the minimum supported analog delay but if we move the digital
+ //delay back by half a VCO cycle then it will be in the range of the analog delay. So do that!
+ adly_en = true;
+ adly_value = static_cast<boost::uint8_t>(round((leftover_delay+half_vco_period_ns-ADLY_MIN_NS)/ADLY_RES_NS));
+ half_shift_en = 1;
+ coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value)-half_vco_period_ns;
+ } else {
+ //Even after moving the digital delay back by half a cycle, we cannot make up the difference
+ //so give up on compensating for the difference from the digital delay tap.
+ //If control reaches here then the value of leftover_delay is possible very small and will still
+ //be close to what the client requested.
+ }
+
+ UHD_LOGV(often)
+ << boost::format("x300_clock_ctrl::set_clock_delay: Which=%d, Requested=%f, Digital Taps=%d, Half Shift=%d, Analog Delay=%d (%s), Coerced Delay=%fns"
+ ) % which % delay_ns % ddly_value % (half_shift_en?"ON":"OFF") % ((int)adly_value) % (adly_en?"ON":"OFF") % coerced_delay << std::endl;
+
+ //Apply settings
+ switch (which)
+ {
+ case X300_CLOCK_WHICH_FPGA:
+ _lmk04816_regs.CLKout0_1_DDLY = ddly_value;
+ _lmk04816_regs.CLKout0_1_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout0_1_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_PD;
+ }
+ write_regs(0);
+ write_regs(6);
+ _delays.fpga_dly_ns = coerced_delay;
+ break;
+ case X300_CLOCK_WHICH_DB0_RX:
+ case X300_CLOCK_WHICH_DB1_RX:
+ _lmk04816_regs.CLKout2_3_DDLY = ddly_value;
+ _lmk04816_regs.CLKout2_3_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout2_3_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_PD;
+ }
+ write_regs(1);
+ write_regs(6);
+ _delays.db_rx_dly_ns = coerced_delay;
+ break;
+ case X300_CLOCK_WHICH_DB0_TX:
+ case X300_CLOCK_WHICH_DB1_TX:
+ _lmk04816_regs.CLKout4_5_DDLY = ddly_value;
+ _lmk04816_regs.CLKout4_5_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout4_5_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_PD;
+ }
+ write_regs(2);
+ write_regs(7);
+ _delays.db_tx_dly_ns = coerced_delay;
+ break;
+ case X300_CLOCK_WHICH_DAC0:
+ case X300_CLOCK_WHICH_DAC1:
+ _lmk04816_regs.CLKout6_7_DDLY = ddly_value;
+ _lmk04816_regs.CLKout6_7_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout6_7_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_PD;
+ }
+ write_regs(3);
+ write_regs(7);
+ _delays.dac_dly_ns = coerced_delay;
+ break;
+ case X300_CLOCK_WHICH_ADC0:
+ case X300_CLOCK_WHICH_ADC1:
+ _lmk04816_regs.CLKout8_9_DDLY = ddly_value;
+ _lmk04816_regs.CLKout8_9_HS = half_shift_en;
+ if (adly_en) {
+ _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_BOTH;
+ _lmk04816_regs.CLKout8_9_ADLY = adly_value;
+ } else {
+ _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_PD;
+ _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_PD;
+ }
+ write_regs(4);
+ write_regs(8);
+ _delays.adc_dly_ns = coerced_delay;
+ break;
+ default:
+ throw uhd::value_error("set_clock_delay: Requested source is invalid.");
+ }
+
+ //Delays are applied only on a sync event
+ if (resync) sync_clocks();
+
+ return coerced_delay;
+ }
+
+ double get_clock_delay(const x300_clock_which_t which) {
+ switch (which)
+ {
+ case X300_CLOCK_WHICH_FPGA:
+ return _delays.fpga_dly_ns;
+ case X300_CLOCK_WHICH_DB0_RX:
+ case X300_CLOCK_WHICH_DB1_RX:
+ return _delays.db_rx_dly_ns;
+ case X300_CLOCK_WHICH_DB0_TX:
+ case X300_CLOCK_WHICH_DB1_TX:
+ return _delays.db_tx_dly_ns;
+ case X300_CLOCK_WHICH_DAC0:
+ case X300_CLOCK_WHICH_DAC1:
+ return _delays.dac_dly_ns;
+ case X300_CLOCK_WHICH_ADC0:
+ case X300_CLOCK_WHICH_ADC1:
+ return _delays.adc_dly_ns;
+ default:
+ throw uhd::value_error("get_clock_delay: Requested source is invalid.");
+ }
+ }
private:
@@ -409,9 +615,6 @@ private:
_lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP;
this->write_regs(0);
_lmk04816_regs.CLKout0_1_DIV = master_clock_div;
- _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_EV_X;
- _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH;
- _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH;
this->write_regs(0);
// Register 1
@@ -435,11 +638,6 @@ private:
_lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS
_lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX
_lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX
- // Delay the FPGA_CLK by 900ps to ensure a safe ADC_SSCLK -> RADIO_CLK crossing.
- // If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC
- // because the data interface clock is generated from FPGA_CLK.
- // NOTE: This delay value was verified at room temperature only.
- _lmk04816_regs.CLKout0_1_ADLY = 0x10;
// Register 7
_lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX
@@ -447,7 +645,6 @@ private:
_lmk04816_regs.CLKout6_TYPE = lmk04816_regs_t::CLKOUT6_TYPE_LVPECL_700MVPP; //DB0_DAC
_lmk04816_regs.CLKout7_TYPE = lmk04816_regs_t::CLKOUT7_TYPE_LVPECL_700MVPP; //DB1_DAC
_lmk04816_regs.CLKout8_TYPE = lmk04816_regs_t::CLKOUT8_TYPE_LVPECL_700MVPP; //DB0_ADC
- _lmk04816_regs.CLKout6_7_ADLY = _lmk04816_regs.CLKout0_1_ADLY;
// Register 8
_lmk04816_regs.CLKout9_TYPE = lmk04816_regs_t::CLKOUT9_TYPE_LVPECL_700MVPP; //DB1_ADC
@@ -506,6 +703,19 @@ private:
// PLL2_P_30 set in individual cases above
// PLL2_N_30 set in individual cases above
+ if (_hw_rev >= 7) {
+ _delays = X300_REV7_CLK_DELAYS;
+ } else {
+ _delays = X300_REV0_6_CLK_DELAYS;
+ }
+
+ //Apply delay values
+ set_clock_delay(X300_CLOCK_WHICH_FPGA, _delays.fpga_dly_ns, false);
+ set_clock_delay(X300_CLOCK_WHICH_DB0_RX, _delays.db_rx_dly_ns, false); //Sets both Ch0 and Ch1
+ set_clock_delay(X300_CLOCK_WHICH_DB0_TX, _delays.db_tx_dly_ns, false); //Sets both Ch0 and Ch1
+ set_clock_delay(X300_CLOCK_WHICH_ADC0, _delays.adc_dly_ns, false); //Sets both Ch0 and Ch1
+ set_clock_delay(X300_CLOCK_WHICH_DAC0, _delays.dac_dly_ns, false); //Sets both Ch0 and Ch1
+
/* Write the configuration values into the LMK */
for (size_t i = 1; i <= 16; ++i) {
this->write_regs(i);
@@ -517,13 +727,14 @@ private:
this->sync_clocks();
}
- const spi_iface::sptr _spiface;
- const size_t _slaveno;
- const size_t _hw_rev;
- const double _master_clock_rate;
- const double _system_ref_rate;
- lmk04816_regs_t _lmk04816_regs;
- double _vco_freq;
+ const spi_iface::sptr _spiface;
+ const size_t _slaveno;
+ const size_t _hw_rev;
+ const double _master_clock_rate;
+ const double _system_ref_rate;
+ lmk04816_regs_t _lmk04816_regs;
+ double _vco_freq;
+ x300_clk_delays _delays;
};
x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface,
diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp
index 9c08aa356..160a14e6d 100644
--- a/host/lib/usrp/x300/x300_clock_ctrl.hpp
+++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp
@@ -33,7 +33,7 @@ enum x300_clock_which_t
X300_CLOCK_WHICH_DB0_TX,
X300_CLOCK_WHICH_DB1_RX,
X300_CLOCK_WHICH_DB1_TX,
- X300_CLOCK_WHICH_TEST,
+ X300_CLOCK_WHICH_FPGA,
};
class x300_clock_ctrl : boost::noncopyable
@@ -94,6 +94,22 @@ public:
*/
virtual void set_ref_out(const bool) = 0;
+ /*! Set the clock delay for the given clock divider.
+ * \param which which clock
+ * \param rate the delay in nanoseconds
+ * \param resync resync clocks to apply delays
+ * \return the actual delay value set
+ * \throw exception when which invalid or delay_ns out of range
+ */
+ virtual double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) = 0;
+
+ /*! Get the clock delay for the given clock divider.
+ * \param which which clock
+ * \return the actual delay value set
+ * \throw exception when which invalid
+ */
+ virtual double get_clock_delay(const x300_clock_which_t which) = 0;
+
/*! Reset the clocks.
* Should be called if the reference clock changes
* to reduce the time required to achieve a lock.
diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h
index a526cabe5..00eafe091 100644
--- a/host/lib/usrp/x300/x300_fw_common.h
+++ b/host/lib/usrp/x300/x300_fw_common.h
@@ -31,7 +31,7 @@ extern "C" {
#define X300_FW_COMPAT_MAJOR 4
#define X300_FW_COMPAT_MINOR 0
-#define X300_FPGA_COMPAT_MAJOR 10
+#define X300_FPGA_COMPAT_MAJOR 11
//shared memory sections - in between the stack and the program space
#define X300_FW_SHMEM_BASE 0x6000
diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp
index b2b9e5c6a..a2b2c9f9e 100644
--- a/host/lib/usrp/x300/x300_impl.cpp
+++ b/host/lib/usrp/x300/x300_impl.cpp
@@ -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>
@@ -688,9 +687,16 @@ 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 */);
+ }
+ self_test_adcs(mb);
////////////////////////////////////////////////////////////////////
// front panel gpio
@@ -820,8 +826,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();
@@ -841,15 +854,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());
@@ -857,6 +862,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
////////////////////////////////////////////////////////////////////
@@ -864,8 +871,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);
@@ -874,31 +893,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
@@ -1789,3 +1793,270 @@ 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
+ int win_start = -1, win_stop = -1;
+
+ 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 >= 4) break;
+ }
+ }
+ //UHD_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % dly_tap % err_code);
+ }
+ 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 < 4) {
+ 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)\n") % ideal_tap % (win_stop-win_start) % tap_delay;
+ }
+}
+
+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));
+ for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) {
+ radio_perifs_t &perif = mb.radio_perifs[r];
+
+ if (!perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED) ||
+ perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR) ||
+ !perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED) ||
+ perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR))
+ {
+ throw uhd::runtime_error(
+ (boost::format("ADC self-test failed for Radio%d. (Ramp checker failure)")%r).str());
+ }
+
+ //Return to normal mode
+ perif.adc->set_test_word("normal", "normal");
+ }
+}
+
diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp
index c27133745..dca6360b8 100644
--- a/host/lib/usrp/x300/x300_impl.hpp
+++ b/host/lib/usrp/x300/x300_impl.hpp
@@ -49,6 +49,8 @@
#include <uhd/transport/nirio/niusrprio_session.h>
#include <uhd/transport/vrt_if_packet.hpp>
#include "recv_packet_demuxer_3000.hpp"
+#include <uhd/utils/soft_register.hpp>
+#include "x300_regs.hpp"
static const std::string X300_FW_FILE_NAME = "usrp_x300_fw.bin";
@@ -169,9 +171,43 @@ public:
private:
boost::shared_ptr<async_md_type> _async_md;
+ class radio_misc_outs_reg : public uhd::soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N, /*width*/ 1, /*shift*/ 1); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET, /*width*/ 1, /*shift*/ 2); //[2]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB, /*width*/ 1, /*shift*/ 3); //[3]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL, /*width*/ 5, /*shift*/ 4); //[8:4]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED, /*width*/ 1, /*shift*/ 9); //[9]
+
+ radio_misc_outs_reg(): uhd::soft_reg32_wo_t(TOREG(SR_MISC_OUTS)) {
+ //Initial values
+ set(DAC_ENABLED, 0);
+ set(DAC_RESET_N, 0);
+ set(ADC_RESET, 0);
+ set(ADC_DATA_DLY_STB, 0);
+ set(ADC_DATA_DLY_VAL, 16);
+ set(ADC_CHECKER_ENABLED, 0);
+ }
+ };
+ class radio_misc_ins_reg : public uhd::soft_reg32_ro_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2); //[2]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3); //[3]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR, /*width*/ 1, /*shift*/ 4); //[4]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR, /*width*/ 1, /*shift*/ 5); //[5]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR, /*width*/ 1, /*shift*/ 6); //[6]
+ UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR, /*width*/ 1, /*shift*/ 7); //[7]
+
+ radio_misc_ins_reg(): uhd::soft_reg32_ro_t(RB32_MISC_INS) { }
+ };
+
//perifs in the radio core
struct radio_perifs_t
{
+ //Interfaces
radio_ctrl_core_3000::sptr ctrl;
spi_core_3000::sptr spi;
x300_adc_ctrl::sptr adc;
@@ -184,6 +220,9 @@ private:
gpio_core_200_32wo::sptr leds;
rx_frontend_core_200::sptr rx_fe;
tx_frontend_core_200::sptr tx_fe;
+ //Registers
+ radio_misc_outs_reg::sptr misc_outs;
+ radio_misc_ins_reg::sptr misc_ins;
};
//overflow recovery impl
@@ -211,7 +250,8 @@ private:
i2c_core_100_wb32::sptr zpu_i2c;
//perifs in each radio
- radio_perifs_t radio_perifs[2]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B
+ static const size_t NUM_RADIOS = 2;
+ radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B
uhd::usrp::dboard_eeprom_t db_eeproms[8];
//! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs
size_t get_radio_index(const std::string &slot_name) {
@@ -259,7 +299,7 @@ private:
* \param mb_i Motherboard index
* \param slot_name Slot name (A or B).
*/
- void setup_radio(const size_t, const std::string &slot_name);
+ void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr);
size_t _sid_framer;
struct sid_config_t
@@ -364,6 +404,10 @@ private:
boost::uint32_t get_fp_gpio(gpio_core_200::sptr);
void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t);
+ void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false);
+ double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false);
+ void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100);
+
//**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
diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp
index 4c5729efe..e984eb908 100644
--- a/host/lib/usrp/x300/x300_regs.hpp
+++ b/host/lib/usrp/x300/x300_regs.hpp
@@ -49,6 +49,7 @@ localparam RB64_TIME_PPS = 16;
localparam RB32_TEST = 24;
localparam RB32_RX = 28;
localparam RB32_FP_GPIO = 32;
+localparam RB32_MISC_INS = 36;
localparam BL_ADDRESS = 0;
localparam BL_DATA = 1;