diff options
| author | Martin Braun <martin.braun@ettus.com> | 2015-07-09 16:56:34 -0700 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2015-07-29 16:50:34 -0700 | 
| commit | 9813505968e3c9beb1c8f16116cd8665524992b6 (patch) | |
| tree | f9b66313c6a138dbabeb7f113e1e6ce802a1a22d /host | |
| parent | e94223d4b5db34b407a72f9aed56fe3ef4eeec75 (diff) | |
| download | uhd-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
Diffstat (limited to 'host')
| -rw-r--r-- | host/lib/usrp/b200/b200_impl.cpp | 187 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_impl.hpp | 9 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_io_impl.cpp | 57 | ||||
| -rw-r--r-- | host/lib/usrp/common/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad9361_ctrl.hpp | 18 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad936x_manager.cpp | 280 | ||||
| -rw-r--r-- | host/lib/usrp/common/ad936x_manager.hpp | 132 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_defaults.hpp | 4 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_impl.cpp | 102 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_impl.hpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_io_impl.cpp | 12 | 
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");  }  /***********************************************************************  | 
