aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2015-07-09 16:56:34 -0700
committerMartin Braun <martin.braun@ettus.com>2015-07-29 16:50:34 -0700
commit9813505968e3c9beb1c8f16116cd8665524992b6 (patch)
treef9b66313c6a138dbabeb7f113e1e6ce802a1a22d
parente94223d4b5db34b407a72f9aed56fe3ef4eeec75 (diff)
downloaduhd-9813505968e3c9beb1c8f16116cd8665524992b6.tar.gz
uhd-9813505968e3c9beb1c8f16116cd8665524992b6.tar.bz2
uhd-9813505968e3c9beb1c8f16116cd8665524992b6.zip
ad9361/b200/e300: Refactored AD936x + perifs management
- Created AD936x manager class - Moved functionality from B2x0 and E310 into manager - Separated property tree + perifs initialization in both device classes
-rw-r--r--host/lib/usrp/b200/b200_impl.cpp187
-rw-r--r--host/lib/usrp/b200/b200_impl.hpp9
-rw-r--r--host/lib/usrp/b200/b200_io_impl.cpp57
-rw-r--r--host/lib/usrp/common/CMakeLists.txt1
-rw-r--r--host/lib/usrp/common/ad9361_ctrl.hpp18
-rw-r--r--host/lib/usrp/common/ad936x_manager.cpp280
-rw-r--r--host/lib/usrp/common/ad936x_manager.hpp132
-rw-r--r--host/lib/usrp/e300/e300_defaults.hpp4
-rw-r--r--host/lib/usrp/e300/e300_impl.cpp102
-rw-r--r--host/lib/usrp/e300/e300_impl.hpp2
-rw-r--r--host/lib/usrp/e300/e300_io_impl.cpp12
11 files changed, 534 insertions, 270 deletions
diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp
index 17086de02..f6ed09a42 100644
--- a/host/lib/usrp/b200/b200_impl.cpp
+++ b/host/lib/usrp/b200/b200_impl.cpp
@@ -519,15 +519,17 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
UHD_ASSERT_THROW(num_radio_chains > 0);
UHD_ASSERT_THROW(num_radio_chains <= 2);
_radio_perifs.resize(num_radio_chains);
- for (size_t i = 0; i < _radio_perifs.size(); i++) this->setup_radio(i);
+ _codec_mgr = ad936x_manager::make(_codec_ctrl, num_radio_chains);
+ _codec_mgr->init_codec();
+ for (size_t i = 0; i < _radio_perifs.size(); i++)
+ this->setup_radio(i);
+
//now test each radio module's connection to the codec interface
- _codec_ctrl->data_port_loopback(true);
BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)
{
- this->codec_loopback_self_test(perif.ctrl);
+ _codec_mgr->loopback_self_test(perif.ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK);
}
- _codec_ctrl->data_port_loopback(false);
//register time now and pps onto available radio cores
_tree->create<time_spec_t>(mb_path / "time" / "now")
@@ -579,7 +581,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
////////////////////////////////////////////////////////////////////
//init the clock rate to something reasonable
- double default_tick_rate = device_addr.cast<double>("master_clock_rate", B200_DEFAULT_TICK_RATE);
+ double default_tick_rate = device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE);
_tree->access<double>(mb_path / "tick_rate").set(default_tick_rate);
//subdev spec contains full width of selections
@@ -601,8 +603,8 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :
// Set the DSP chains to some safe value
for (size_t i = 0; i < _radio_perifs.size(); i++) {
- _radio_perifs[i].ddc->set_host_rate(default_tick_rate / B200_DEFAULT_DECIM);
- _radio_perifs[i].duc->set_host_rate(default_tick_rate / B200_DEFAULT_INTERP);
+ _radio_perifs[i].ddc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_DECIM);
+ _radio_perifs[i].duc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_INTERP);
}
// We can automatically choose a master clock rate, but not if the user specifies one
_tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate"));
@@ -645,10 +647,18 @@ void b200_impl::setup_radio(const size_t dspno)
const fs_path mb_path = "/mboards/0";
////////////////////////////////////////////////////////////////////
+ // Set up transport
+ ////////////////////////////////////////////////////////////////////
+ const boost::uint32_t sid = (dspno == 0) ? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID;
+
+ ////////////////////////////////////////////////////////////////////
// radio control
////////////////////////////////////////////////////////////////////
- const boost::uint32_t sid = (dspno == 0)? B200_CTRL0_MSG_SID : B200_CTRL1_MSG_SID;
- perif.ctrl = radio_ctrl_core_3000::make(false/*lilE*/, _ctrl_transport, zero_copy_if::sptr()/*null*/, sid);
+ perif.ctrl = radio_ctrl_core_3000::make(
+ false/*lilE*/,
+ _ctrl_transport,
+ zero_copy_if::sptr()/*null*/,
+ sid);
perif.ctrl->hold_task(_async_task);
_async_task_data->radio_ctrl[dspno] = perif.ctrl; //weak
_tree->access<time_spec_t>(mb_path / "time" / "cmd")
@@ -656,15 +666,33 @@ void b200_impl::setup_radio(const size_t dspno)
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1));
this->register_loopback_self_test(perif.ctrl);
- perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR));
////////////////////////////////////////////////////////////////////
- // create rx dsp control objects
+ // Set up peripherals
////////////////////////////////////////////////////////////////////
+ perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR));
+ // create rx dsp control objects
perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));
perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP), true /*is_b200?*/);
perif.ddc->set_link_rate(10e9/8); //whatever
perif.ddc->set_mux("IQ", false, dspno == 1 ? true : false, dspno == 1 ? true : false);
+ perif.ddc->set_freq(0.0);
+ perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
+ perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
+ perif.duc->set_link_rate(10e9/8); //whatever
+ perif.duc->set_freq(0.0);
+
+ ////////////////////////////////////////////////////////////////////
+ // create time control objects
+ ////////////////////////////////////////////////////////////////////
+ time_core_3000::readback_bases_type time64_rb_bases;
+ time64_rb_bases.rb_now = RB64_TIME_NOW;
+ time64_rb_bases.rb_pps = RB64_TIME_PPS;
+ perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+
+ ////////////////////////////////////////////////////////////////////
+ // create rx dsp control objects
+ ////////////////////////////////////////////////////////////////////
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1))
.subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1));
@@ -677,8 +705,9 @@ void b200_impl::setup_radio(const size_t dspno)
.subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1))
;
_tree->create<double>(rx_dsp_path / "freq" / "value")
+ .set(0.0)
.coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1))
- .set(0.0);
+ ;
_tree->create<meta_range_t>(rx_dsp_path / "freq" / "range")
.publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc));
_tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
@@ -687,9 +716,6 @@ void b200_impl::setup_radio(const size_t dspno)
////////////////////////////////////////////////////////////////////
// create tx dsp control objects
////////////////////////////////////////////////////////////////////
- perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL));
- perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));
- perif.duc->set_link_rate(10e9/8); //whatever
_tree->access<double>(mb_path / "tick_rate")
.subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1))
.subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));
@@ -702,106 +728,51 @@ void b200_impl::setup_radio(const size_t dspno)
.subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1))
;
_tree->create<double>(tx_dsp_path / "freq" / "value")
+ .set(0.0)
.coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1))
- .set(0.0);
+ ;
_tree->create<meta_range_t>(tx_dsp_path / "freq" / "range")
- .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc));
-
- ////////////////////////////////////////////////////////////////////
- // create time control objects
- ////////////////////////////////////////////////////////////////////
- time_core_3000::readback_bases_type time64_rb_bases;
- time64_rb_bases.rb_now = RB64_TIME_NOW;
- time64_rb_bases.rb_pps = RB64_TIME_PPS;
- perif.time64 = time_core_3000::make(perif.ctrl, TOREG(SR_TIME), time64_rb_bases);
+ .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc))
+ ;
////////////////////////////////////////////////////////////////////
// create RF frontend interfacing
////////////////////////////////////////////////////////////////////
- for(size_t direction = 0; direction < 2; direction++)
- {
- const std::string x = direction? "rx" : "tx";
- const std::string key = std::string((direction? "RX" : "TX")) + std::string(((dspno == _fe1)? "1" : "2"));
- const fs_path rf_fe_path = mb_path / "dboards" / "A" / (x+"_frontends") / (dspno? "B" : "A");
-
- _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key);
- _tree->create<int>(rf_fe_path / "sensors");
+ static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION);
+ BOOST_FOREACH(direction_t dir, dirs) {
+ const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx";
+ const std::string key = std::string(((dir == RX_DIRECTION) ? "RX" : "TX")) + std::string(((dspno == _fe1) ? "1" : "2"));
+ const fs_path rf_fe_path
+ = mb_path / "dboards" / "A" / (x + "_frontends") / (dspno ? "B" : "A");
+
+ // This will connect all the AD936x-specific items
+ _codec_mgr->populate_frontend_subtree(
+ _tree->subtree(rf_fe_path), key, dir
+ );
+
+ // Now connect all the b200_impl-specific items
_tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked")
- .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, x == "tx"));
- BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key))
- {
- _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range")
- .set(ad9361_ctrl::get_gain_range(key));
-
- _tree->create<double>(rf_fe_path / "gains" / name / "value")
- .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
- .set(x == "rx" ? B200_DEFAULT_RX_GAIN : B200_DEFAULT_TX_GAIN);
- }
- _tree->create<std::string>(rf_fe_path / "connection").set("IQ");
- _tree->create<bool>(rf_fe_path / "enabled").set(true);
- _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false);
- _tree->create<double>(rf_fe_path / "bandwidth" / "value")
- .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
- .set(56e6);
- _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range")
- .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key));
- _tree->create<double>(rf_fe_path / "freq" / "value")
- .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key))
- .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
+ .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, dir == TX_DIRECTION))
+ ;
+ _tree->access<double>(rf_fe_path / "freq" / "value")
.subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1))
- .set(B200_DEFAULT_FREQ);
- _tree->create<meta_range_t>(rf_fe_path / "freq" / "range")
- .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range));
- _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "temp")
- .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl));
-
- //setup RX related stuff
- if(direction)
- {
- _tree->create<bool>(rf_fe_path / "dc_offset" / "enable" )
- .subscribe(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1)).set(true);
-
- _tree->create<bool>(rf_fe_path / "iq_balance" / "enable" )
- .subscribe(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1)).set(true);
- }
-
- //add all frontend filters
- std::vector<std::string> filter_names = _codec_ctrl->get_filter_names(key);
- for(size_t i = 0;i < filter_names.size(); i++)
- {
- _tree->create<filter_info_base::sptr>(rf_fe_path / "filters" / filter_names[i] / "value" )
- .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_names[i]))
- .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_names[i], _1));
- }
-
- //setup antenna stuff
- if (key[0] == 'R')
+ ;
+ if (dir == RX_DIRECTION)
{
static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2");
_tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
_tree->create<std::string>(rf_fe_path / "antenna" / "value")
.subscribe(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1))
- .set("RX2");
- _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi")
- .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key));
-
- //AGC setup
- const std::list<std::string> mode_strings = boost::assign::list_of("slow")("fast");
- _tree->create<bool>(rf_fe_path / "gain" / "agc" / "enable")
- .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1))
- .set(false);
- _tree->create<std::string>(rf_fe_path / "gain" / "agc" / "mode" / "value")
- .subscribe(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front());
- _tree->create<std::list<std::string> >(rf_fe_path / "gain" / "agc" / "mode" / "options")
- .set(mode_strings);
+ .set("RX2")
+ ;
+
}
- if (key[0] == 'T')
+ else if (dir == TX_DIRECTION)
{
static const std::vector<std::string> ants(1, "TX/RX");
_tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
_tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX");
}
-
}
}
@@ -824,30 +795,6 @@ void b200_impl::register_loopback_self_test(wb_iface::sptr iface)
UHD_MSG(status) << ((test_fail)? "fail" : "pass") << std::endl;
}
-void b200_impl::codec_loopback_self_test(wb_iface::sptr iface)
-{
- UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush;
- size_t hash = size_t(time(NULL));
- for (size_t i = 0; i < 100; i++)
- {
- boost::hash_combine(hash, i);
- const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
- iface->poke32(TOREG(SR_CODEC_IDLE), word32);
- iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate
- const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK);
- const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
- const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
- bool test_fail = word32 != rb_tx or word32 != rb_rx;
- if (test_fail) {
- UHD_MSG(status) << "fail" << std::endl;
- throw uhd::runtime_error("CODEC loopback test failed.");
- }
- }
- UHD_MSG(status) << "pass" << std::endl;
- /* Zero out the idle data. */
- iface->poke32(TOREG(SR_CODEC_IDLE), 0);
-}
-
/***********************************************************************
* Sample and tick rate comprehension below
**********************************************************************/
diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp
index 3360d4453..52ecb98f2 100644
--- a/host/lib/usrp/b200/b200_impl.hpp
+++ b/host/lib/usrp/b200/b200_impl.hpp
@@ -22,6 +22,7 @@
#include "b200_uart.hpp"
#include "b200_cores.hpp"
#include "ad9361_ctrl.hpp"
+#include "ad936x_manager.hpp"
#include "adf4001_ctrl.hpp"
#include "rx_vita_core_3000.hpp"
#include "tx_vita_core_3000.hpp"
@@ -50,12 +51,6 @@ static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 8;
static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0;
static const boost::uint16_t B200_FPGA_COMPAT_NUM = 9;
static const double B200_BUS_CLOCK_RATE = 100e6;
-static const double B200_DEFAULT_TICK_RATE = 16e6;
-static const double B200_DEFAULT_FREQ = 100e6; // Hz
-static const double B200_DEFAULT_DECIM = 128;
-static const double B200_DEFAULT_INTERP = 128;
-static const double B200_DEFAULT_RX_GAIN = 0; // dB
-static const double B200_DEFAULT_TX_GAIN = 0; // dB
static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83;
static const size_t B200_MAX_RATE_USB2 = 53248000; // bytes/s
static const size_t B200_MAX_RATE_USB3 = 500000000; // bytes/s
@@ -124,6 +119,7 @@ private:
b200_iface::sptr _iface;
radio_ctrl_core_3000::sptr _local_ctrl;
uhd::usrp::ad9361_ctrl::sptr _codec_ctrl;
+ uhd::usrp::ad936x_manager::sptr _codec_mgr;
b200_local_spi_core::sptr _spi_iface;
boost::shared_ptr<uhd::usrp::adf4001_ctrl> _adf4001_iface;
uhd::gps_ctrl::sptr _gps;
@@ -152,7 +148,6 @@ private:
boost::optional<uhd::msg_task::msg_type_t> handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr<AsyncTaskData>);
void register_loopback_self_test(uhd::wb_iface::sptr iface);
- void codec_loopback_self_test(uhd::wb_iface::sptr iface);
void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &);
void check_fw_compat(void);
void check_fpga_compat(void);
diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp
index c4e04f70a..20807bdd4 100644
--- a/host/lib/usrp/b200/b200_io_impl.cpp
+++ b/host/lib/usrp/b200/b200_io_impl.cpp
@@ -127,49 +127,20 @@ void b200_impl::set_auto_tick_rate(
return;
}
- // Step 2: Check if the lcm_rate is within available limits:
double base_rate = static_cast<double>(lcm_rate);
- if (uhd::math::fp_compare::fp_compare_delta<double>(base_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >
- uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) {
+ try {
+ // Step 2: Get a good tick rate value
+ const double new_rate = _codec_mgr->get_auto_tick_rate(base_rate, num_chans);
+ // Step 3: Set the new tick rate value (if any change)
+ if (!uhd::math::frequencies_are_equal(_tree->access<double>("/mboards/0/tick_rate").get(), new_rate)) {
+ _tree->access<double>("/mboards/0/tick_rate").set(new_rate);
+ }
+ } catch (const uhd::value_error &e) {
UHD_MSG(warning)
<< "Cannot automatically determine an appropriate tick rate for these sampling rates." << std::endl
<< "Consider using different sampling rates, or manually specify a suitable master clock rate." << std::endl;
return; // Let the others handle this
}
-
- // Step 3: Choose the new rate
- // Rules for choosing the tick rate:
- // Choose a rate that is a power of 2 larger than the sampling rate,
- // but at least 4. Cannot exceed the max tick rate, of course, but must
- // be larger than the minimum tick rate.
- // An equation that does all that is:
- //
- // f_auto = r * 2^floor(log2(f_max/r))
- // = base_rate * multiplier
- //
- // where r is the base rate and f_max is the maximum tick rate. The case
- // where floor() yields 1 must be caught.
- const double min_tick_rate = _codec_ctrl->get_clock_rate_range().start();
- // We use shifts here instead of 2^x because exp2() is not available in all compilers,
- // also this guarantees no rounding issues. The type cast to int32_t serves as floor():
- boost::int32_t multiplier = (1 << boost::int32_t(uhd::math::log2(max_tick_rate / base_rate)));
- if (multiplier == 2 and base_rate >= min_tick_rate) {
- // Don't bother (see above)
- multiplier = 1;
- }
- double new_rate = base_rate * multiplier;
- UHD_ASSERT_THROW(
- uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >=
- uhd::math::fp_compare::fp_compare_delta<double>(min_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)
- );
- UHD_ASSERT_THROW(
- uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) <=
- uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)
- );
-
- if (!uhd::math::frequencies_are_equal(_tree->access<double>("/mboards/0/tick_rate").get(), new_rate)) {
- _tree->access<double>("/mboards/0/tick_rate").set(new_rate);
- }
}
void b200_impl::update_tick_rate(const double new_tick_rate)
@@ -212,14 +183,6 @@ double b200_impl::coerce_rx_samp_rate(rx_dsp_core_3000::sptr ddc, size_t dspno,
return ddc->set_host_rate(rx_rate);
}
-#define CHECK_BANDWIDTH(dir) \
- if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \
- UHD_MSG(warning) \
- << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \
- << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \
- << std::endl; \
- }
-
void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)
{
boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
@@ -228,7 +191,7 @@ void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)
my_streamer->set_samp_rate(rate);
const double adj = _radio_perifs[dspno].ddc->get_scaling_adjustment();
my_streamer->set_scale_factor(adj);
- CHECK_BANDWIDTH("Rx");
+ _codec_mgr->check_bandwidth(rate, "Rx");
}
double b200_impl::coerce_tx_samp_rate(tx_dsp_core_3000::sptr duc, size_t dspno, const double tx_rate)
@@ -250,7 +213,7 @@ void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate)
my_streamer->set_samp_rate(rate);
const double adj = _radio_perifs[dspno].duc->get_scaling_adjustment();
my_streamer->set_scale_factor(adj);
- CHECK_BANDWIDTH("Tx");
+ _codec_mgr->check_bandwidth(rate, "Tx");
}
/***********************************************************************
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt
index 129cc569b..e63a09935 100644
--- a/host/lib/usrp/common/CMakeLists.txt
+++ b/host/lib/usrp/common/CMakeLists.txt
@@ -33,6 +33,7 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/adf435x_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/ad936x_manager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver/ad9361_device.cpp
${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp
${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp
diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp
index ac0404b24..044265422 100644
--- a/host/lib/usrp/common/ad9361_ctrl.hpp
+++ b/host/lib/usrp/common/ad9361_ctrl.hpp
@@ -32,9 +32,21 @@
namespace uhd { namespace usrp {
-/***********************************************************************
- * AD9361 Control Interface
- **********************************************************************/
+/*! AD936x Control Interface
+ *
+ * This is a convenient way to access the AD936x RF IC.
+ * It basically encodes knowledge of register values etc. into
+ * accessible API calls.
+ *
+ * \section ad936x_which The `which` parameter
+ *
+ * Many function calls require a `which` parameter to select
+ * the RF frontend. Valid values for `which` are:
+ * - RX1, RX2
+ * - TX1, TX2
+ *
+ * Frontend numbering is as designed by the AD9361.
+ */
class ad9361_ctrl : public boost::noncopyable
{
public:
diff --git a/host/lib/usrp/common/ad936x_manager.cpp b/host/lib/usrp/common/ad936x_manager.cpp
new file mode 100644
index 000000000..b060880cd
--- /dev/null
+++ b/host/lib/usrp/common/ad936x_manager.cpp
@@ -0,0 +1,280 @@
+//
+// Copyright 2015 Ettus Research
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "ad936x_manager.hpp"
+#include <uhd/utils/msg.hpp>
+#include <boost/foreach.hpp>
+#include <boost/functional/hash.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/****************************************************************************
+ * Default values
+ ***************************************************************************/
+const double ad936x_manager::DEFAULT_GAIN = 0;
+const double ad936x_manager::DEFAULT_BANDWIDTH = 56e6;
+const double ad936x_manager::DEFAULT_TICK_RATE = 16e6;
+const double ad936x_manager::DEFAULT_FREQ = 100e6; // Hz
+const uint32_t ad936x_manager::DEFAULT_DECIM = 128;
+const uint32_t ad936x_manager::DEFAULT_INTERP = 128;
+const bool ad936x_manager::DEFAULT_AUTO_DC_OFFSET = true;
+const bool ad936x_manager::DEFAULT_AUTO_IQ_BALANCE = true;
+const bool ad936x_manager::DEFAULT_AGC_ENABLE = true;
+
+class ad936x_manager_impl : public ad936x_manager
+{
+ public:
+ /************************************************************************
+ * Structor
+ ***********************************************************************/
+ ad936x_manager_impl(
+ const ad9361_ctrl::sptr &codec_ctrl,
+ const size_t n_frontends
+ ) : _codec_ctrl(codec_ctrl),
+ _n_frontends(n_frontends)
+ {
+ if (_n_frontends < 1 or _n_frontends > 2) {
+ throw uhd::runtime_error(str(
+ boost::format("AD936x device can only have either 1 or 2 frontends, not %d.")
+ % _n_frontends
+ ));
+ }
+ for (size_t i = 1; i <= _n_frontends; i++) {
+ _rx_frontends.push_back(str(boost::format("RX%d") % i));
+ _tx_frontends.push_back(str(boost::format("TX%d") % i));
+ }
+ }
+
+ /************************************************************************
+ * API Calls
+ ***********************************************************************/
+ void init_codec()
+ {
+ BOOST_FOREACH(const std::string &rx_fe, _rx_frontends) {
+ _codec_ctrl->set_gain(rx_fe, DEFAULT_GAIN);
+ _codec_ctrl->set_bw_filter(rx_fe, DEFAULT_BANDWIDTH);
+ _codec_ctrl->tune(rx_fe, DEFAULT_FREQ);
+ _codec_ctrl->set_dc_offset_auto(rx_fe, DEFAULT_AUTO_DC_OFFSET);
+ _codec_ctrl->set_iq_balance_auto(rx_fe, DEFAULT_AUTO_IQ_BALANCE);
+ _codec_ctrl->set_agc(rx_fe, DEFAULT_AGC_ENABLE);
+ }
+ BOOST_FOREACH(const std::string &tx_fe, _tx_frontends) {
+ _codec_ctrl->set_gain(tx_fe, DEFAULT_GAIN);
+ _codec_ctrl->set_bw_filter(tx_fe, DEFAULT_BANDWIDTH);
+ _codec_ctrl->tune(tx_fe, DEFAULT_FREQ);
+ }
+ }
+
+ void loopback_self_test(
+ wb_iface::sptr iface,
+ wb_iface::wb_addr_type codec_idle_addr,
+ wb_iface::wb_addr_type codec_readback_addr
+ ) {
+ _codec_ctrl->data_port_loopback(true);
+ UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush;
+ UHD_ASSERT_THROW(bool(iface));
+ size_t hash = size_t(time(NULL));
+ for (size_t i = 0; i < 100; i++)
+ {
+ boost::hash_combine(hash, i);
+ const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
+ iface->poke32(codec_idle_addr, word32);
+ // We do 2 peeks so we have enough idleness for loopback to propagate
+ iface->peek64(codec_readback_addr);
+ const boost::uint64_t rb_word64 = iface->peek64(codec_readback_addr);
+ const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
+ const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
+ bool test_fail = word32 != rb_tx or word32 != rb_rx;
+ if (test_fail) {
+ UHD_MSG(status) << "fail" << std::endl;
+ throw uhd::runtime_error("CODEC loopback test failed.");
+ }
+ }
+ UHD_MSG(status) << "pass" << std::endl;
+ /* Zero out the idle data. */
+ iface->poke32(codec_idle_addr, 0);
+ _codec_ctrl->data_port_loopback(false);
+ }
+
+
+ double get_auto_tick_rate(
+ const double lcm_rate,
+ size_t num_chans
+ ) {
+ UHD_ASSERT_THROW(num_chans >= 1 and num_chans <= _n_frontends);
+ const uhd::meta_range_t rate_range = _codec_ctrl->get_clock_rate_range();
+ const double min_tick_rate = rate_range.start();
+ const double max_tick_rate = rate_range.stop() / num_chans;
+
+ // Check if the requested rate is within available limits:
+ if (uhd::math::fp_compare::fp_compare_delta<double>(lcm_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >
+ uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)) {
+ throw uhd::value_error(str(
+ boost::format("[ad936x_manager] Cannot get determine a tick rate if sampling rate exceeds maximum tick rate (%f > %f)")
+ % lcm_rate % max_tick_rate
+ ));
+ }
+
+ // **** Choose the new rate ****
+ // Rules for choosing the tick rate:
+ // Choose a rate that is a power of 2 larger than the sampling rate,
+ // but at least 4. Cannot exceed the max tick rate, of course, but must
+ // be larger than the minimum tick rate.
+ // An equation that does all that is:
+ //
+ // f_auto = r * 2^floor(log2(f_max/r))
+ // = lcm_rate * multiplier
+ //
+ // where r is the base rate and f_max is the maximum tick rate. The case
+ // where floor() yields 1 must be caught.
+ // We use shifts here instead of 2^x because exp2() is not available in all compilers,
+ // also this guarantees no rounding issues. The type cast to int32_t serves as floor():
+ int32_t multiplier = (1 << int32_t(uhd::math::log2(max_tick_rate / lcm_rate)));
+ if (multiplier == 2 and lcm_rate >= min_tick_rate) {
+ // Don't bother (see above)
+ multiplier = 1;
+ }
+ const double new_rate = lcm_rate * multiplier;
+ UHD_ASSERT_THROW(
+ uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >=
+ uhd::math::fp_compare::fp_compare_delta<double>(min_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)
+ );
+ UHD_ASSERT_THROW(
+ uhd::math::fp_compare::fp_compare_delta<double>(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) <=
+ uhd::math::fp_compare::fp_compare_delta<double>(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ)
+ );
+
+ return new_rate;
+ }
+
+ bool check_bandwidth(double rate, const std::string dir)
+ {
+ if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) {
+ UHD_MSG(warning)
+ << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n"
+ << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)."
+ << std::endl;
+ return false;
+ }
+ return true;
+ }
+
+ void populate_frontend_subtree(uhd::property_tree::sptr subtree, const std::string &key, uhd::direction_t dir)
+ {
+ subtree->create<std::string>("name").set("FE-"+key);
+
+ // Sensors
+ subtree->create<sensor_value_t>("sensors/temp")
+ .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl))
+ ;
+ if (dir == RX_DIRECTION) {
+ subtree->create<sensor_value_t>("sensors/rssi")
+ .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key))
+ ;
+ }
+
+ // Gains
+ BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key))
+ {
+ subtree->create<meta_range_t>(uhd::fs_path("gains") / name / "range")
+ .set(ad9361_ctrl::get_gain_range(key));
+ subtree->create<double>(uhd::fs_path("gains") / name / "value")
+ .set(ad936x_manager::DEFAULT_GAIN)
+ .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
+ ;
+ }
+
+ // FE Settings
+ subtree->create<std::string>("connection").set("IQ");
+ subtree->create<bool>("enabled").set(true);
+ subtree->create<bool>("use_lo_offset").set(false);
+
+ // Analog Bandwidths
+ subtree->create<double>("bandwidth/value")
+ .set(ad936x_manager::DEFAULT_BANDWIDTH)
+ .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
+ ;
+ subtree->create<meta_range_t>("bandwidth/range")
+ .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key))
+ ;
+
+ // LO Tuning
+ subtree->create<meta_range_t>("freq/range")
+ .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range))
+ ;
+ subtree->create<double>("freq/value")
+ .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key))
+ .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
+ ;
+
+ // Frontend corrections
+ if(dir == RX_DIRECTION)
+ {
+ subtree->create<bool>("dc_offset/enable" )
+ .set(ad936x_manager::DEFAULT_AUTO_DC_OFFSET)
+ .subscribe(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1))
+ ;
+ subtree->create<bool>("iq_balance/enable" )
+ .set(ad936x_manager::DEFAULT_AUTO_IQ_BALANCE)
+ .subscribe(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1))
+ ;
+
+ // AGC setup
+ const std::list<std::string> mode_strings = boost::assign::list_of("slow")("fast");
+ subtree->create<bool>("gain/agc/enable")
+ .set(DEFAULT_AGC_ENABLE)
+ .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1))
+ ;
+ subtree->create<std::string>("gain/agc/mode/value")
+ .subscribe(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front())
+ ;
+ subtree->create< std::list<std::string> >("gain/agc/mode/options")
+ .set(mode_strings)
+ ;
+ }
+
+ // Frontend filters
+ BOOST_FOREACH(const std::string &filter_name, _codec_ctrl->get_filter_names(key)) {
+ subtree->create<filter_info_base::sptr>(uhd::fs_path("filters") / filter_name / "value" )
+ .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_name))
+ .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_name, _1));
+ }
+ }
+
+ private:
+ //! Store a pointer to an actual AD936x control object
+ ad9361_ctrl::sptr _codec_ctrl;
+
+ //! Do we have 1 or 2 frontends?
+ const size_t _n_frontends;
+
+ //! List of valid RX frontend names (RX1, RX2)
+ std::vector<std::string> _rx_frontends;
+ //! List of valid TX frontend names (TX1, TX2)
+ std::vector<std::string> _tx_frontends;
+}; /* class ad936x_manager_impl */
+
+ad936x_manager::sptr ad936x_manager::make(
+ const ad9361_ctrl::sptr &codec_ctrl,
+ const size_t n_frontends
+) {
+ return sptr(
+ new ad936x_manager_impl(codec_ctrl, n_frontends)
+ );
+}
+
diff --git a/host/lib/usrp/common/ad936x_manager.hpp b/host/lib/usrp/common/ad936x_manager.hpp
new file mode 100644
index 000000000..9b4a351c6
--- /dev/null
+++ b/host/lib/usrp/common/ad936x_manager.hpp
@@ -0,0 +1,132 @@
+//
+// Copyright 2015 Ettus Research
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_AD9361_MANAGER_HPP
+#define INCLUDED_AD9361_MANAGER_HPP
+
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/utils/math.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/types/direction.hpp>
+#include <boost/shared_ptr.hpp>
+#include "ad9361_ctrl.hpp"
+#include <stdint.h>
+
+namespace uhd { namespace usrp {
+
+/*! AD936x Manager class
+ *
+ * This class performs higher (management) tasks on the AD936x.
+ * It requires a uhd::usrp::ad9361_ctrl object to do the actual
+ * register peeks/pokes etc.
+ */
+class ad936x_manager
+{
+public:
+ typedef boost::shared_ptr<ad936x_manager> sptr;
+
+ static const double DEFAULT_GAIN;
+ static const double DEFAULT_BANDWIDTH;
+ static const double DEFAULT_TICK_RATE;
+ static const double DEFAULT_FREQ; // Hz
+ static const uint32_t DEFAULT_DECIM;
+ static const uint32_t DEFAULT_INTERP;
+ static const bool DEFAULT_AUTO_DC_OFFSET;
+ static const bool DEFAULT_AUTO_IQ_BALANCE;
+ static const bool DEFAULT_AGC_ENABLE;
+
+ /*!
+ * \param codec_ctrl The actual AD936x control object
+ * \param n_frontends Number of frontends (1 or 2)
+ */
+ static sptr make(
+ const ad9361_ctrl::sptr &codec_ctrl,
+ const size_t n_frontends
+ );
+
+ virtual ~ad936x_manager(void) {};
+
+ /*! Put the AD936x into a default state.
+ *
+ * Sets gains, LOs, bandwidths, etc. according to the DEFAULT_* constants.
+ */
+ virtual void init_codec(void) = 0;
+
+ /*! Run a loopback self test.
+ *
+ * This will write data to the AD936x and read it back again.
+ * If this test fails, it generally means the interface is broken,
+ * so we assume it passes and throw otherwise. Running this requires
+ * a core that we can peek and poke the loopback values into.
+ *
+ * \param iface An interface to the associated radio control core
+ * \param iface The radio control core's address to write the loopback value
+ * \param iface The radio control core's readback address to read back the returned value
+ *
+ * \throws a uhd::runtime_error if the loopback value didn't match.
+ */
+ virtual void loopback_self_test(
+ wb_iface::sptr iface,
+ wb_iface::wb_addr_type codec_idle_addr,
+ wb_iface::wb_addr_type codec_readback_addr
+ ) = 0;
+
+ /*! Determine a tick rate that will work with a given sampling rate
+ * (assuming a DDC/DUC chain is also available elsewhere).
+ *
+ * Example: If we want to stream with a rate of 5 Msps, then the AD936x
+ * must run at an integer multiple of that. Although not strictly necessary,
+ * we always try and return a multiple of 2. Let's say we need those 5 Msps
+ * on two channels, then a good rate is 20 MHz, which is 4 times the sampling
+ * rate (thus we can use 2 halfbands elsewhere).
+ * If different rates are used on different channels, this can be particularly
+ * useful. The clock rate of the AD936x needs to be a multiple of the least
+ * common multiple of all the rates. Example: We want to transmit with 3 Msps
+ * and receive with 5 Msps. The LCM of this is 15 Msps, which is used as an
+ * argument for this function. A good rate is then 30 MHz, which is twice
+ * the LCM.
+ *
+ * \param lcm_rate Least Common Multiple of all the rates involved.
+ * \param num_chans The number of channels used for the stream.
+ *
+ * \returns a valid tick rate that can be used with the given rate
+ * \throws a uhd::value_error if \p lcm_rate exceeds the max tick rate
+ */
+ virtual double get_auto_tick_rate(
+ const double lcm_rate,
+ size_t num_chans
+ ) = 0;
+
+ /*! Check if a given sampling rate is within the available analog bandwidth.
+ *
+ * If not, outputs a warning message and returns false.
+ */
+ virtual bool check_bandwidth(double rate, const std::string dir) = 0;
+
+ /*! Populate the property tree for the device frontend
+ */
+ virtual void populate_frontend_subtree(
+ uhd::property_tree::sptr subtree,
+ const std::string &key,
+ uhd::direction_t dir
+ ) = 0;
+
+}; /* class ad936x_manager */
+
+}} /* namespace uhd::usrp */
+
+#endif /* INCLUDED_AD9361_MANAGER_HPP */
diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp
index 32b3c33ef..267897e03 100644
--- a/host/lib/usrp/e300/e300_defaults.hpp
+++ b/host/lib/usrp/e300/e300_defaults.hpp
@@ -30,10 +30,6 @@ static const double DEFAULT_RX_SAMP_RATE = 1.0e6;
static const double DEFAULT_DDC_FREQ = 0.0;
static const double DEFAULT_DUC_FREQ = 0.0;
-static const double DEFAULT_FE_GAIN = 0.0;
-static const double DEFAULT_FE_FREQ = 1.0e9;
-static const double DEFAULT_FE_BW = 56e6;
-
static const std::string DEFAULT_TIME_SRC = "internal";
static const std::string DEFAULT_CLOCK_SRC = "internal";
diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp
index e8a201916..3607c6036 100644
--- a/host/lib/usrp/e300/e300_impl.cpp
+++ b/host/lib/usrp/e300/e300_impl.cpp
@@ -395,6 +395,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
_eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE));
}
+ _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS);
UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush;
if (_xport_path == AXI) {
@@ -478,14 +479,10 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
for(size_t instance = 0; instance < fpga::NUM_RADIOS; instance++)
this->_setup_radio(instance);
- _codec_ctrl->data_port_loopback(true);
-
// Radio 0 loopback through AD9361
- this->_codec_loopback_self_test(_radio_perifs[0].ctrl);
+ _codec_mgr->loopback_self_test(_radio_perifs[0].ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK);
// Radio 1 loopback through AD9361
- this->_codec_loopback_self_test(_radio_perifs[1].ctrl);
-
- _codec_ctrl->data_port_loopback(false);
+ _codec_mgr->loopback_self_test(_radio_perifs[1].ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK);
////////////////////////////////////////////////////////////////////
// internal gpios
@@ -581,7 +578,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
// init the clock rate to something reasonable
_tree->access<double>(mb_path / "tick_rate").set(
- device_addr.cast<double>("master_clock_rate", e300::DEFAULT_TICK_RATE));
+ device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE));
// subdev spec contains full width of selections
subdev_spec_t rx_spec, tx_spec;
@@ -741,30 +738,6 @@ std::string e300_impl::_get_version_hash(void)
% ((git_hash & 0xF000000) ? "-dirty" : ""));
}
-void e300_impl::_codec_loopback_self_test(wb_iface::sptr iface)
-{
- bool test_fail = false;
- UHD_ASSERT_THROW(bool(iface));
- UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush;
- size_t hash = size_t(time(NULL));
- for (size_t i = 0; i < 100; i++)
- {
- boost::hash_combine(hash, i);
- const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;
- iface->poke32(TOREG(SR_CODEC_IDLE), word32);
- iface->peek64(RB64_CODEC_READBACK); //enough idleness for loopback to propagate
- const boost::uint64_t rb_word64 = iface->peek64(RB64_CODEC_READBACK);
- const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
- const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
- test_fail = word32 != rb_tx or word32 != rb_rx;
- if (test_fail) break; //exit loop on any failure
- }
- UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl;
-
- /* Zero out the idle data. */
- iface->poke32(TOREG(SR_CODEC_IDLE), 0);
-}
-
boost::uint32_t e300_impl::_allocate_sid(const sid_config_t &config)
{
const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff;
@@ -1060,67 +1033,38 @@ void e300_impl::_setup_radio(const size_t dspno)
////////////////////////////////////////////////////////////////////
// create RF frontend interfacing
////////////////////////////////////////////////////////////////////
- static const std::vector<std::string> data_directions = boost::assign::list_of("rx")("tx");
- BOOST_FOREACH(const std::string& direction, data_directions)
- {
- const std::string key = boost::to_upper_copy(direction) + std::string(((dspno == FE0)? "1" : "2"));
+ static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION);
+ BOOST_FOREACH(direction_t dir, dirs) {
+ const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx";
+ const std::string key = boost::to_upper_copy(x) + std::string(((dspno == FE0)? "1" : "2"));
const fs_path rf_fe_path
= mb_path / "dboards" / "A" / (direction + "_frontends") / ((dspno == 0) ? "A" : "B");
- _tree->create<std::string>(rf_fe_path / "name").set("FE-"+key);
- _tree->create<int>(rf_fe_path / "sensors"); //empty TODO
+ // This will connect all the AD936x-specific items
+ _codec_mgr->populate_frontend_subtree(
+ _tree->subtree(rf_fe_path), key, dir
+ );
+
+ // This will connect all the e300_impl-specific items
_tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked")
- .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx"));
- _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "temp")
- .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl));
- BOOST_FOREACH(const std::string &name, ad9361_ctrl::get_gain_names(key))
- {
- _tree->create<meta_range_t>(rf_fe_path / "gains" / name / "range")
- .set(ad9361_ctrl::get_gain_range(key));
+ .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx"))
+ ;
- _tree->create<double>(rf_fe_path / "gains" / name / "value")
- .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))
- .set(e300::DEFAULT_FE_GAIN);
- }
- _tree->create<std::string>(rf_fe_path / "connection").set("IQ");
- _tree->create<bool>(rf_fe_path / "enabled").set(true);
- _tree->create<bool>(rf_fe_path / "use_lo_offset").set(false);
- _tree->create<double>(rf_fe_path / "bandwidth" / "value")
- .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))
- .set(e300::DEFAULT_FE_BW);
- _tree->create<meta_range_t>(rf_fe_path / "bandwidth" / "range")
- .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key));
- _tree->create<double>(rf_fe_path / "freq" / "value")
- .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key))
- .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))
- .subscribe(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1))
- .set(e300::DEFAULT_FE_FREQ);
- _tree->create<meta_range_t>(rf_fe_path / "freq" / "range")
- .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range));
-
- //only in local mode
- if(_xport_path == AXI) {
- //add all frontend filters
- std::vector<std::string> filter_names = _codec_ctrl->get_filter_names(key);
- for(size_t i = 0;i < filter_names.size(); i++)
- {
- _tree->create<filter_info_base::sptr>(rf_fe_path / "filters" / filter_names[i] / "value" )
- .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_names[i]))
- .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_names[i], _1));
- }
+ // Network mode currently doesn't support the filter API, so
+ // prevent it from using it:
+ if(_xport_path != AXI) {
+ _tree->remove(rf_fe_path / "filters");
}
- //setup RX related stuff
- if (key[0] == 'R') {
+ // Antenna Setup
+ if (dir == RX_DIRECTION) {
static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2");
_tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
_tree->create<std::string>(rf_fe_path / "antenna" / "value")
.subscribe(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1))
.set("RX2");
- _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "rssi")
- .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key));
}
- if (key[0] == 'T') {
+ if (dir == TX_DIRECTION) {
static const std::vector<std::string> ants(1, "TX/RX");
_tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options").set(ants);
_tree->create<std::string>(rf_fe_path / "antenna" / "value").set("TX/RX");
diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp
index 3ed133489..d61b95387 100644
--- a/host/lib/usrp/e300/e300_impl.hpp
+++ b/host/lib/usrp/e300/e300_impl.hpp
@@ -40,6 +40,7 @@
#include "rx_dsp_core_3000.hpp"
#include "tx_dsp_core_3000.hpp"
#include "ad9361_ctrl.hpp"
+#include "ad936x_manager.hpp"
#include "gpio_core_200.hpp"
#include "e300_global_regs.hpp"
@@ -288,6 +289,7 @@ private: // members
radio_perifs_t _radio_perifs[2];
double _tick_rate;
ad9361_ctrl::sptr _codec_ctrl;
+ ad936x_manager::sptr _codec_mgr;
fe_control_settings_t _settings;
global_regs::sptr _global_regs;
e300_sensor_manager::sptr _sensor_manager;
diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp
index dadfb71e9..29d250c8f 100644
--- a/host/lib/usrp/e300/e300_io_impl.cpp
+++ b/host/lib/usrp/e300/e300_io_impl.cpp
@@ -91,21 +91,13 @@ void e300_impl::_update_tick_rate(const double rate)
}
}
-#define CHECK_BANDWIDTH(dir) \
- if (rate > _codec_ctrl->get_bw_filter_range(dir).stop()) { \
- UHD_MSG(warning) \
- << "Selected " << dir << " bandwidth (" << (rate/1e6) << " MHz) exceeds\n" \
- << "analog frontend filter bandwidth (" << (_codec_ctrl->get_bw_filter_range(dir).stop()/1e6) << " MHz)." \
- << std::endl; \
- }
-
void e300_impl::_update_rx_samp_rate(const size_t dspno, const double rate)
{
boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_radio_perifs[dspno].rx_streamer.lock());
if (my_streamer)
my_streamer->set_samp_rate(rate);
- CHECK_BANDWIDTH("Rx");
+ _codec_mgr->check_bandwidth(rate, "Rx");
}
void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate)
@@ -114,7 +106,7 @@ void e300_impl::_update_tx_samp_rate(const size_t dspno, const double rate)
boost::dynamic_pointer_cast<sph::send_packet_streamer>(_radio_perifs[dspno].tx_streamer.lock());
if (my_streamer)
my_streamer->set_samp_rate(rate);
- CHECK_BANDWIDTH("Tx");
+ _codec_mgr->check_bandwidth(rate, "Tx");
}
/***********************************************************************