From 9813505968e3c9beb1c8f16116cd8665524992b6 Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Thu, 9 Jul 2015 16:56:34 -0700 Subject: 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 --- host/lib/usrp/b200/b200_impl.cpp | 187 ++++++++------------- host/lib/usrp/b200/b200_impl.hpp | 9 +- host/lib/usrp/b200/b200_io_impl.cpp | 57 ++----- host/lib/usrp/common/CMakeLists.txt | 1 + host/lib/usrp/common/ad9361_ctrl.hpp | 18 +- host/lib/usrp/common/ad936x_manager.cpp | 280 ++++++++++++++++++++++++++++++++ host/lib/usrp/common/ad936x_manager.hpp | 132 +++++++++++++++ host/lib/usrp/e300/e300_defaults.hpp | 4 - host/lib/usrp/e300/e300_impl.cpp | 102 +++--------- host/lib/usrp/e300/e300_impl.hpp | 2 + host/lib/usrp/e300/e300_io_impl.cpp | 12 +- 11 files changed, 534 insertions(+), 270 deletions(-) create mode 100644 host/lib/usrp/common/ad936x_manager.cpp create mode 100644 host/lib/usrp/common/ad936x_manager.hpp 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(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("master_clock_rate", B200_DEFAULT_TICK_RATE); + double default_tick_rate = device_addr.cast("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE); _tree->access(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(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate")); @@ -644,11 +646,19 @@ void b200_impl::setup_radio(const size_t dspno) radio_perifs_t &perif = _radio_perifs[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(mb_path / "time" / "cmd") @@ -656,15 +666,33 @@ void b200_impl::setup_radio(const size_t dspno) _tree->access(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(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(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(rx_dsp_path / "freq" / "range") .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); _tree->create(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(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(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(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(rf_fe_path / "name").set("FE-"+key); - _tree->create(rf_fe_path / "sensors"); + static const std::vector 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(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(rf_fe_path / "gains" / name / "range") - .set(ad9361_ctrl::get_gain_range(key)); - - _tree->create(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(rf_fe_path / "connection").set("IQ"); - _tree->create(rf_fe_path / "enabled").set(true); - _tree->create(rf_fe_path / "use_lo_offset").set(false); - _tree->create(rf_fe_path / "bandwidth" / "value") - .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) - .set(56e6); - _tree->create(rf_fe_path / "bandwidth" / "range") - .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); - _tree->create(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(rf_fe_path / "freq" / "value") .subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1)) - .set(B200_DEFAULT_FREQ); - _tree->create(rf_fe_path / "freq" / "range") - .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)); - _tree->create(rf_fe_path / "sensors" / "temp") - .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl)); - - //setup RX related stuff - if(direction) - { - _tree->create(rf_fe_path / "dc_offset" / "enable" ) - .subscribe(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1)).set(true); - - _tree->create(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 filter_names = _codec_ctrl->get_filter_names(key); - for(size_t i = 0;i < filter_names.size(); i++) - { - _tree->create(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 ants = boost::assign::list_of("TX/RX")("RX2"); _tree->create >(rf_fe_path / "antenna" / "options").set(ants); _tree->create(rf_fe_path / "antenna" / "value") .subscribe(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1)) - .set("RX2"); - _tree->create(rf_fe_path / "sensors" / "rssi") - .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)); - - //AGC setup - const std::list mode_strings = boost::assign::list_of("slow")("fast"); - _tree->create(rf_fe_path / "gain" / "agc" / "enable") - .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1)) - .set(false); - _tree->create(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 >(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 ants(1, "TX/RX"); _tree->create >(rf_fe_path / "antenna" / "options").set(ants); _tree->create(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 _adf4001_iface; uhd::gps_ctrl::sptr _gps; @@ -152,7 +148,6 @@ private: boost::optional handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr); 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(lcm_rate); - if (uhd::math::fp_compare::fp_compare_delta(base_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > - uhd::math::fp_compare::fp_compare_delta(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("/mboards/0/tick_rate").get(), new_rate)) { + _tree->access("/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(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >= - uhd::math::fp_compare::fp_compare_delta(min_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) - ); - UHD_ASSERT_THROW( - uhd::math::fp_compare::fp_compare_delta(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) <= - uhd::math::fp_compare::fp_compare_delta(max_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) - ); - - if (!uhd::math::frequencies_are_equal(_tree->access("/mboards/0/tick_rate").get(), new_rate)) { - _tree->access("/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 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 . +// + +#include "ad936x_manager.hpp" +#include +#include +#include + +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(lcm_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > + uhd::math::fp_compare::fp_compare_delta(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(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) >= + uhd::math::fp_compare::fp_compare_delta(min_tick_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) + ); + UHD_ASSERT_THROW( + uhd::math::fp_compare::fp_compare_delta(new_rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) <= + uhd::math::fp_compare::fp_compare_delta(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("name").set("FE-"+key); + + // Sensors + subtree->create("sensors/temp") + .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl)) + ; + if (dir == RX_DIRECTION) { + subtree->create("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(uhd::fs_path("gains") / name / "range") + .set(ad9361_ctrl::get_gain_range(key)); + subtree->create(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("connection").set("IQ"); + subtree->create("enabled").set(true); + subtree->create("use_lo_offset").set(false); + + // Analog Bandwidths + subtree->create("bandwidth/value") + .set(ad936x_manager::DEFAULT_BANDWIDTH) + .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) + ; + subtree->create("bandwidth/range") + .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)) + ; + + // LO Tuning + subtree->create("freq/range") + .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)) + ; + subtree->create("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("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("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 mode_strings = boost::assign::list_of("slow")("fast"); + subtree->create("gain/agc/enable") + .set(DEFAULT_AGC_ENABLE) + .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1)) + ; + subtree->create("gain/agc/mode/value") + .subscribe(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front()) + ; + subtree->create< std::list >("gain/agc/mode/options") + .set(mode_strings) + ; + } + + // Frontend filters + BOOST_FOREACH(const std::string &filter_name, _codec_ctrl->get_filter_names(key)) { + subtree->create(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 _rx_frontends; + //! List of valid TX frontend names (TX1, TX2) + std::vector _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 . +// + +#ifndef INCLUDED_AD9361_MANAGER_HPP +#define INCLUDED_AD9361_MANAGER_HPP + +#include +#include +#include +#include +#include +#include "ad9361_ctrl.hpp" +#include + +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 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(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(mb_path / "tick_rate").set( - device_addr.cast("master_clock_rate", e300::DEFAULT_TICK_RATE)); + device_addr.cast("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 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 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(rf_fe_path / "name").set("FE-"+key); - _tree->create(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(rf_fe_path / "sensors" / "lo_locked") - .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, direction == "tx")); - _tree->create(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(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(rf_fe_path / "gains" / name / "value") - .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) - .set(e300::DEFAULT_FE_GAIN); - } - _tree->create(rf_fe_path / "connection").set("IQ"); - _tree->create(rf_fe_path / "enabled").set(true); - _tree->create(rf_fe_path / "use_lo_offset").set(false); - _tree->create(rf_fe_path / "bandwidth" / "value") - .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) - .set(e300::DEFAULT_FE_BW); - _tree->create(rf_fe_path / "bandwidth" / "range") - .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)); - _tree->create(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(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 filter_names = _codec_ctrl->get_filter_names(key); - for(size_t i = 0;i < filter_names.size(); i++) - { - _tree->create(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 ants = boost::assign::list_of("TX/RX")("RX2"); _tree->create >(rf_fe_path / "antenna" / "options").set(ants); _tree->create(rf_fe_path / "antenna" / "value") .subscribe(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1)) .set("RX2"); - _tree->create(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 ants(1, "TX/RX"); _tree->create >(rf_fe_path / "antenna" / "options").set(ants); _tree->create(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 my_streamer = boost::dynamic_pointer_cast(_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(_radio_perifs[dspno].tx_streamer.lock()); if (my_streamer) my_streamer->set_samp_rate(rate); - CHECK_BANDWIDTH("Tx"); + _codec_mgr->check_bandwidth(rate, "Tx"); } /*********************************************************************** -- cgit v1.2.3