diff options
Diffstat (limited to 'host/lib/usrp/b200')
| -rw-r--r-- | host/lib/usrp/b200/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_iface.cpp | 4 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_iface.hpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_image_loader.cpp | 125 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_impl.cpp | 346 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_impl.hpp | 103 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_io_impl.cpp | 161 | ||||
| -rw-r--r-- | host/lib/usrp/b200/b200_regs.hpp | 2 | 
8 files changed, 556 insertions, 188 deletions
diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt index ce89b5d80..cd8ebcba7 100644 --- a/host/lib/usrp/b200/CMakeLists.txt +++ b/host/lib/usrp/b200/CMakeLists.txt @@ -26,6 +26,7 @@ LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF)  IF(ENABLE_B200)      LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/b200_image_loader.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/b200_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/b200_iface.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 270d3bb4b..4754a6357 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -511,7 +511,7 @@ public:              throw uhd::io_error((boost::format("Short write on set FPGA hash (expecting: %d, returned: %d)") % bytes_to_send % ret).str());      } -    boost::uint32_t load_fpga(const std::string filestring) { +    boost::uint32_t load_fpga(const std::string filestring, bool force) {          boost::uint8_t fx3_state = 0;          boost::uint32_t wait_count; @@ -522,7 +522,7 @@ public:          hash_type hash = generate_hash(filename);          hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash); -        if (hash == loaded_hash) return 0; +        if (hash == loaded_hash and !force) return 0;          // Establish default largest possible control request transfer size based on operating USB speed          int transfer_size = VREQ_DEFAULT_SIZE; diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 1d123439a..0c7ee6b9e 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -97,7 +97,7 @@ public:      virtual void set_fpga_reset_pin(const bool reset) = 0;      //! load an FPGA image -    virtual boost::uint32_t load_fpga(const std::string filestring) = 0; +    virtual boost::uint32_t load_fpga(const std::string filestring, bool force=false) = 0;      virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0; diff --git a/host/lib/usrp/b200/b200_image_loader.cpp b/host/lib/usrp/b200/b200_image_loader.cpp new file mode 100644 index 000000000..87010244c --- /dev/null +++ b/host/lib/usrp/b200/b200_image_loader.cpp @@ -0,0 +1,125 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// 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 <boost/assign.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> + +#include <uhd/exception.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> + +#include "b200_iface.hpp" +#include "b200_impl.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +namespace uhd{ + +static b200_iface::sptr get_b200_iface(const image_loader::image_loader_args_t &image_loader_args, +                                       mboard_eeprom_t &mb_eeprom, +                                       bool user_specified){ + +    std::vector<usb_device_handle::sptr> dev_handles = get_b200_device_handles(image_loader_args.args); +    b200_iface::sptr iface; + +    if(dev_handles.size() > 0){ +        BOOST_FOREACH(usb_device_handle::sptr dev_handle, dev_handles){ +            if(dev_handle->firmware_loaded()){ +                iface = b200_iface::make(usb_control::make(dev_handle,0)); +                mb_eeprom = mboard_eeprom_t(*iface, "B200"); +                if(user_specified){ +                    if(image_loader_args.args.has_key("serial") and +                       mb_eeprom.get("serial") != image_loader_args.args.get("serial")){ +                           continue; +                    } +                    if(image_loader_args.args.has_key("name") and +                       mb_eeprom.get("name") != image_loader_args.args.get("name")){ +                           continue; +                    } +                    return iface; +                } +                else return iface; // Just return first found +            } +        } +    } + +    // No applicable devices found, return empty sptr so we can exit +    iface.reset(); +    mb_eeprom = mboard_eeprom_t(); +    return iface; +} + +static bool b200_image_loader(const image_loader::image_loader_args_t &image_loader_args){ +    if(!image_loader_args.load_fpga) +        return false; + +    bool user_specified = (image_loader_args.args.has_key("serial") or +                           image_loader_args.args.has_key("name")); + +    // See if a B2x0 with the given args is found +    mboard_eeprom_t mb_eeprom; +    b200_iface::sptr iface = get_b200_iface(image_loader_args, mb_eeprom, user_specified); +    if(!iface) return false; // No initialized B2x0 found + +    std::string fpga_path; +    if(image_loader_args.fpga_path == ""){ +        /* +         * Normally, we can auto-generate the FPGA filename from what's in the EEPROM, +         * but if the applicable value is not in the EEPROM, the user must give a specific +         * filename for us to use. +         */ +        std::string product = mb_eeprom.get("product"); +        if(not B2X0_PRODUCT_ID.has_key(boost::lexical_cast<boost::uint16_t>(product))){ +            if(user_specified){ +                // The user specified a bad device but expects us to know what it is +                throw uhd::runtime_error("Could not determine model. You must manually specify an FPGA image filename."); +            } +            else{ +                return false; +            } +        } +        else{ +            fpga_path = find_image_path(B2X0_FPGA_FILE_NAME.get(get_b200_type(mb_eeprom))); +        } +    } +    else fpga_path = image_loader_args.fpga_path; + +    std::cout << boost::format("Unit: USRP %s (%s)") +                 % B2X0_STR_NAMES.get(get_b200_type(mb_eeprom), "B2XX") +                 % mb_eeprom.get("serial") +              << std::endl; + +    iface->load_fpga(fpga_path, true); + +    return true; +} + +UHD_STATIC_BLOCK(register_b200_image_loader){ +    std::string recovery_instructions = "This device is likely in an unusable state. Power-cycle the\n" +                                        "device, and the firmware/FPGA will be reloaded the next time\n" +                                        "UHD uses the device."; + +    image_loader::register_image_loader("b200", b200_image_loader, recovery_instructions); +} + +} /* namespace uhd */ diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 0409adf30..f5129bc24 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2012-2014 Ettus Research LLC +// Copyright 2012-2015 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -37,6 +37,8 @@  #include <ctime>  #include <cmath> +#include "../../transport/libusb1_base.hpp" +  using namespace uhd;  using namespace uhd::usrp;  using namespace uhd::transport; @@ -76,7 +78,7 @@ public:  //! Look up the type of B-Series device we're currently running.  //  If the product ID stored in mb_eeprom is invalid, throws a  //  uhd::runtime_error. -static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom) +b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)  {      if (mb_eeprom["product"].empty()) {          throw uhd::runtime_error("B200: Missing product ID on EEPROM."); @@ -91,6 +93,21 @@ static b200_type_t get_b200_type(const mboard_eeprom_t &mb_eeprom)      return B2X0_PRODUCT_ID[product_id];  } +std::vector<usb_device_handle::sptr> get_b200_device_handles(const device_addr_t &hint) +{ +    std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list; + +    if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")), +                                                                      uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")))); +    } else { +        vid_pid_pair_list = b200_vid_pid_pairs; +    } + +    //find the usrps and load firmware +    return usb_device_handle::get_device_list(vid_pid_pair_list); +} +  static device_addrs_t b200_find(const device_addr_t &hint)  {      device_addrs_t b200_addrs; @@ -104,25 +121,13 @@ static device_addrs_t b200_find(const device_addr_t &hint)          if (hint_i.has_key("addr") || hint_i.has_key("resource")) return b200_addrs;      } -    boost::uint16_t vid, pid; - -    if(hint.has_key("vid") && hint.has_key("pid") && hint.has_key("type") && hint["type"] == "b200") { -        vid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("vid")); -        pid = uhd::cast::hexstr_cast<boost::uint16_t>(hint.get("pid")); -    } else { -        vid = B200_VENDOR_ID; -        pid = B200_PRODUCT_ID; -    } -      // Important note:      // The get device list calls are nested inside the for loop.      // This allows the usb guts to decontruct when not in use,      // so that re-enumeration after fw load can occur successfully.      // This requirement is a courtesy of libusb1.0 on windows. - -    //find the usrps and load firmware      size_t found = 0; -    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +    BOOST_FOREACH(usb_device_handle::sptr handle, get_b200_device_handles(hint)) {          //extract the firmware path for the b200          std::string b200_fw_image;          try{ @@ -153,7 +158,7 @@ static device_addrs_t b200_find(const device_addr_t &hint)      //search for the device until found or timeout      while (boost::get_system_time() < timeout_time and b200_addrs.empty() and found != 0)      { -        BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) +        BOOST_FOREACH(usb_device_handle::sptr handle, get_b200_device_handles(hint))          {              usb_control::sptr control;              try{control = usb_control::make(handle, 0);} @@ -191,7 +196,24 @@ static device_addrs_t b200_find(const device_addr_t &hint)   **********************************************************************/  static device::sptr b200_make(const device_addr_t &device_addr)  { -    return device::sptr(new b200_impl(device_addr)); +    uhd::transport::usb_device_handle::sptr handle; + +    // We try twice, because the first time, the link might be in a bad state +    // and we might need to reset the link, but if that didn't help, trying +    // a third time is pointless. +    try { +        return device::sptr(new b200_impl(device_addr, handle)); +    } +    catch (const uhd::usb_error &e) { +        libusb::device_handle::sptr dev_handle(libusb::device_handle::get_cached_handle( +            boost::static_pointer_cast<libusb::special_handle>(handle)->get_device() +        )); +        dev_handle->clear_endpoints(B200_USB_CTRL_RECV_ENDPOINT, B200_USB_CTRL_SEND_ENDPOINT); +        dev_handle->clear_endpoints(B200_USB_DATA_RECV_ENDPOINT, B200_USB_DATA_SEND_ENDPOINT); +        dev_handle->reset_device(); +    } + +    return device::sptr(new b200_impl(device_addr, handle));  }  UHD_STATIC_BLOCK(register_b200_device) @@ -202,7 +224,7 @@ UHD_STATIC_BLOCK(register_b200_device)  /***********************************************************************   * Structors   **********************************************************************/ -b200_impl::b200_impl(const device_addr_t &device_addr) : +b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::sptr &handle) :      _revision(0),      _tick_rate(0.0) // Forces a clock initialization at startup  { @@ -213,16 +235,52 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :      //try to match the given device address with something on the USB bus      boost::uint16_t vid = B200_VENDOR_ID;      boost::uint16_t pid = B200_PRODUCT_ID; +    bool specified_vid = false; +    bool specified_pid = false; +      if (device_addr.has_key("vid")) +    {          vid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("vid")); +        specified_vid = true; +    } +      if (device_addr.has_key("pid")) +    {          pid = uhd::cast::hexstr_cast<boost::uint16_t>(device_addr.get("pid")); +        specified_pid = true; +    } + +    std::vector<usb_device_handle::vid_pid_pair_t> vid_pid_pair_list;//search list for devices. + +    // Search only for specified VID and PID if both specified +    if (specified_vid && specified_pid) +    { +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,pid)); +    } +    // Search for all supported PIDs limited to specified VID if only VID specified +    else if (specified_vid) +    { +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B200_PRODUCT_ID)); +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B200_PRODUCT_NI_ID)); +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(vid,B210_PRODUCT_NI_ID)); +    } +    // Search for all supported VIDs limited to specified PID if only PID specified +    else if (specified_pid) +    { +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID,pid)); +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,pid)); +    } +    // Search for all supported devices if neither VID nor PID specified +    else +    { +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID,B200_PRODUCT_ID)); +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,B200_PRODUCT_NI_ID)); +        vid_pid_pair_list.push_back(usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID,B210_PRODUCT_NI_ID)); +    } -    std::vector<usb_device_handle::sptr> device_list = -        usb_device_handle::get_device_list(vid, pid); +    std::vector<usb_device_handle::sptr> device_list = usb_device_handle::get_device_list(vid_pid_pair_list);      //locate the matching handle in the device list -    usb_device_handle::sptr handle;      BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) {          if (dev_handle->get_serial() == device_addr["serial"]){              handle = dev_handle; @@ -320,10 +378,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :      ctrl_xport_args["send_frame_size"] = min_frame_size;      ctrl_xport_args["num_send_frames"] = "16"; +    // This may throw a uhd::usb_error, which will be caught by b200_make().      _ctrl_transport = usb_zero_copy::make(          handle, -        4, 8, //interface, endpoint -        3, 4, //interface, endpoint +        B200_USB_CTRL_RECV_INTERFACE, B200_USB_CTRL_RECV_ENDPOINT, //interface, endpoint +        B200_USB_CTRL_SEND_INTERFACE, B200_USB_CTRL_SEND_ENDPOINT, //interface, endpoint          ctrl_xport_args      );      while (_ctrl_transport->get_recv_buff(0.0)){} //flush ctrl xport @@ -402,10 +461,11 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :      data_xport_args["send_frame_size"] = device_addr.get("send_frame_size", "8192");      data_xport_args["num_send_frames"] = device_addr.get("num_send_frames", "16"); +    // This may throw a uhd::usb_error, which will be caught by b200_make().      _data_transport = usb_zero_copy::make(          handle,        // identifier -        2, 6,          // IN interface, endpoint -        1, 2,          // OUT interface, endpoint +        B200_USB_DATA_RECV_INTERFACE, B200_USB_DATA_RECV_ENDPOINT, //interface, endpoint +        B200_USB_DATA_SEND_INTERFACE, B200_USB_DATA_SEND_ENDPOINT, //interface, endpoint          data_xport_args    // param hints      );      while (_data_transport->get_recv_buff(0.0)){} //flush ctrl xport @@ -447,6 +507,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :          .publish(boost::bind(&b200_impl::get_tick_rate, this))          .subscribe(boost::bind(&b200_impl::update_tick_rate, this, _1));      _tree->create<time_spec_t>(mb_path / "time" / "cmd"); +    _tree->create<bool>(mb_path / "auto_tick_rate").set(false);      ////////////////////////////////////////////////////////////////////      // and do the misc mboard sensors @@ -477,15 +538,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") @@ -512,6 +575,19 @@ b200_impl::b200_impl(const device_addr_t &device_addr) :      _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources);      //////////////////////////////////////////////////////////////////// +    // front panel gpio +    //////////////////////////////////////////////////////////////////// +    _radio_perifs[0].fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); +    BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) +    { +            _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second) +            .set(0) +            .subscribe(boost::bind(&b200_impl::set_fp_gpio, this, _radio_perifs[0].fp_gpio, attr.first, _1)); +    } +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") +        .publish(boost::bind(&b200_impl::get_fp_gpio, this, _radio_perifs[0].fp_gpio)); + +    ////////////////////////////////////////////////////////////////////      // dboard eeproms but not really      ////////////////////////////////////////////////////////////////////      dboard_eeprom_t db_eeprom; @@ -524,7 +600,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 @@ -546,8 +622,13 @@ 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")); +    if (not device_addr.has_key("master_clock_rate")) { +        UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl;      }      //GPS installed: use external ref, time, and init time spec @@ -585,10 +666,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") @@ -596,121 +685,96 @@ 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(rx_dsp_core_3000::DEFAULT_CORDIC_FREQ); +    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(tx_dsp_core_3000::DEFAULT_CORDIC_FREQ); + +    //////////////////////////////////////////////////////////////////// +    // 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); + +    //////////////////////////////////////////////////////////////////// +    // connect 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));      const fs_path rx_dsp_path = mb_path / "rx_dsps" / dspno; -    _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") -        .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); -    _tree->create<double>(rx_dsp_path / "rate" / "value") -        .set(0.0) // We can only load a sensible value after the tick rate was set -        .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) +    perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path)); +    _tree->access<double>(rx_dsp_path / "rate" / "value") +        .coerce(boost::bind(&b200_impl::coerce_rx_samp_rate, this, perif.ddc, dspno, _1))          .subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1))      ; -    _tree->create<double>(rx_dsp_path / "freq" / "value") -        .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")          .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));      ////////////////////////////////////////////////////////////////////      // 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));      const fs_path tx_dsp_path = mb_path / "tx_dsps" / dspno; -    _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") -        .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); -    _tree->create<double>(tx_dsp_path / "rate" / "value") -        .set(0.0) // We can only load a sensible value after the tick rate was set -        .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) +    perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); +    _tree->access<double>(tx_dsp_path / "rate" / "value") +        .coerce(boost::bind(&b200_impl::coerce_tx_samp_rate, this, perif.duc, dspno, _1))          .subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1))      ; -    _tree->create<double>(tx_dsp_path / "freq" / "value") -        .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);      ////////////////////////////////////////////////////////////////////      // 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(40e6); -        _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)); - -        //setup RX related 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)); +                .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");          } -      }  } @@ -733,34 +797,10 @@ 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   **********************************************************************/ -void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction /*= NULL*/) +void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string &direction /*= ""*/)  {      const size_t max_chans = 2;      if (chan_count > max_chans) @@ -768,7 +808,7 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co          throw uhd::value_error(boost::str(              boost::format("cannot not setup %d %s channels (maximum is %d)")                  % chan_count -                % (direction ? direction : "data") +                % (direction.empty() ? "data" : direction)                  % max_chans          ));      } @@ -782,20 +822,26 @@ void b200_impl::enforce_tick_rate_limits(size_t chan_count, double tick_rate, co                      % (tick_rate/1e6)                      % (max_tick_rate/1e6)                      % chan_count -                    % (direction ? direction : "data") +                    % (direction.empty() ? "data" : direction)              ));          }      }  } -double b200_impl::set_tick_rate(const double rate) +double b200_impl::set_tick_rate(const double new_tick_rate)  { -    UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz\n") % (rate/1e6)); - -    check_tick_rate_with_current_streamers(rate);   // Defined in b200_io_impl.cpp +    UHD_MSG(status) << (boost::format("Asking for clock rate %.6f MHz... ") % (new_tick_rate/1e6)) << std::flush; +    check_tick_rate_with_current_streamers(new_tick_rate);   // Defined in b200_io_impl.cpp + +    // Make sure the clock rate is actually changed before doing +    // the full Monty of setting regs and loopback tests etc. +    if (std::abs(new_tick_rate - _tick_rate) < 1.0) { +        UHD_MSG(status) << "OK" << std::endl; +        return _tick_rate; +    } -    _tick_rate = _codec_ctrl->set_clock_rate(rate); -    UHD_MSG(status) << (boost::format("Actually got clock rate %.6f MHz\n") % (_tick_rate/1e6)); +    _tick_rate = _codec_ctrl->set_clock_rate(new_tick_rate); +    UHD_MSG(status) << std::endl << (boost::format("Actually got clock rate %.6f MHz.") % (_tick_rate/1e6)) << std::endl;      //reset after clock rate change      this->reset_codec_dcm(); @@ -856,6 +902,26 @@ void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom)  } +boost::uint32_t b200_impl::get_fp_gpio(gpio_core_200::sptr gpio) +{ +    return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); +} + +void b200_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) +{ +    switch (attr) +    { +    case GPIO_CTRL:   return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); +    case GPIO_DDR:    return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); +    case GPIO_OUT:    return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); +    case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); +    case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); +    case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); +    case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); +    default:        UHD_THROW_INVALID_CODE_PATH(); +    } +} +  /***********************************************************************   * Reference time and clock   **********************************************************************/ diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 65796d1a4..cbd51426d 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -1,5 +1,5 @@  // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2015 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -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" @@ -43,18 +44,13 @@  #include <uhd/usrp/gps_ctrl.hpp>  #include <uhd/transport/usb_zero_copy.hpp>  #include <uhd/transport/bounded_buffer.hpp> +#include <boost/assign.hpp>  #include <boost/weak_ptr.hpp>  #include "recv_packet_demuxer_3000.hpp" -static const boost::uint8_t  B200_FW_COMPAT_NUM_MAJOR = 7; +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 = 8; +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 = 32e6; -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 @@ -82,23 +78,48 @@ static const boost::uint32_t B200_RX_GPS_UART_SID = FLIP_SID(B200_TX_GPS_UART_SI  static const boost::uint32_t B200_LOCAL_CTRL_SID = 0x00000040;  static const boost::uint32_t B200_LOCAL_RESP_SID = FLIP_SID(B200_LOCAL_CTRL_SID); -/*********************************************************************** - * The B200 Capability Constants - **********************************************************************/ +static const unsigned char B200_USB_CTRL_RECV_INTERFACE = 4; +static const unsigned char B200_USB_CTRL_RECV_ENDPOINT  = 8; +static const unsigned char B200_USB_CTRL_SEND_INTERFACE = 3; +static const unsigned char B200_USB_CTRL_SEND_ENDPOINT  = 4; + +static const unsigned char B200_USB_DATA_RECV_INTERFACE = 2; +static const unsigned char B200_USB_DATA_RECV_ENDPOINT  = 6; +static const unsigned char B200_USB_DATA_SEND_INTERFACE = 1; +static const unsigned char B200_USB_DATA_SEND_ENDPOINT  = 2; + +/* + * VID/PID pairs for all B2xx products + */ +static std::vector<uhd::transport::usb_device_handle::vid_pid_pair_t> b200_vid_pid_pairs = +    boost::assign::list_of +        (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_ID, B200_PRODUCT_ID)) +        (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID)) +        (uhd::transport::usb_device_handle::vid_pid_pair_t(B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID)) +    ; + +b200_type_t get_b200_type(const uhd::usrp::mboard_eeprom_t &mb_eeprom); +std::vector<uhd::transport::usb_device_handle::sptr> get_b200_device_handles(const uhd::device_addr_t &hint);  //! Implementation guts  class b200_impl : public uhd::device  {  public:      //structors -    b200_impl(const uhd::device_addr_t &); +    b200_impl(const uhd::device_addr_t &, uhd::transport::usb_device_handle::sptr &handle);      ~b200_impl(void);      //the io interface      uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args);      uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args);      bool recv_async_msg(uhd::async_metadata_t &, double); -    void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction = NULL); + +    //! Check that the combination of stream args and tick rate are valid. +    // +    // Basically figures out the arguments for enforce_tick_rate_limits() +    // and calls said method. If arguments are invalid, throws a +    // uhd::value_error. +    void check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction = "");  private:      b200_type_t _b200_type; @@ -108,6 +129,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; @@ -136,7 +158,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); @@ -154,6 +175,7 @@ private:      {          radio_ctrl_core_3000::sptr ctrl;          gpio_core_200_32wo::sptr atr; +        gpio_core_200::sptr fp_gpio;          time_core_3000::sptr time64;          rx_vita_core_3000::sptr framer;          rx_dsp_core_3000::sptr ddc; @@ -200,14 +222,63 @@ private:      void update_enables(void);      void update_atrs(void); +    boost::uint32_t get_fp_gpio(gpio_core_200::sptr); +    void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); +      double _tick_rate;      double get_tick_rate(void){return _tick_rate;}      double set_tick_rate(const double rate); + +    /*! \brief Choose a tick rate (master clock rate) that works well for the given sampling rate. +     * +     * This function will try and choose a master clock rate automatically. +     * See the function definition for details on the algorithm. +     * +     * The chosen tick rate is the largest multiple of two that is smaler +     * than the max tick rate. +     * The base rate is either given explicitly, or is the lcm() of the tx +     * and rx sampling rates. In that case, it reads the rates directly +     * from the property tree. It also tries to guess the number of channels +     * (for the max possible tick rate) by checking the available streamers. +     * This value, too, can explicitly be given. +     * +     * \param rate If this is given, it will be used as a minimum rate, or +     *             argument to lcm(). +     * \param tree_dsp_path The sampling rate from this property tree path +     *                      will be ignored. +     * \param num_chans If given, specifies the number of channels. +     */ +    void set_auto_tick_rate( +            const double rate=0, +            const uhd::fs_path &tree_dsp_path="", +            size_t num_chans=0 +    ); +      void update_tick_rate(const double); -    void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const char* direction = NULL); + +    /*! Check if \p tick_rate works with \p chan_count channels. +     * +     * Throws a uhd::value_error if not. +     */ +    void enforce_tick_rate_limits(size_t chan_count, double tick_rate, const std::string  &direction = "");      void check_tick_rate_with_current_streamers(double rate); +    /*! Return the max number of channels on active rx_streamer or tx_streamer objects associated with this device. +     * +     * \param direction Set to "TX" to only check tx_streamers, "RX" to only check +     *                  rx_streamers. Any other value will check if \e any active +     *                  streamers are available. +     * \return Return the number of tx streamers (direction=="TX"), the number of rx +     *         streamers (direction=="RX") or the total number of streamers. +     */ +    size_t max_chan_count(const std::string &direction=""); + +    //! Coercer, attached to the "rate/value" property on the rx dsps. +    double coerce_rx_samp_rate(rx_dsp_core_3000::sptr, size_t, const double);      void update_rx_samp_rate(const size_t, const double); + +    //! Coercer, attached to the "rate/value" property on the tx dsps. +    double coerce_tx_samp_rate(tx_dsp_core_3000::sptr, size_t, const double);      void update_tx_samp_rate(const size_t, const double);  }; diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 1e11e7ff6..20807bdd4 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2014 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -21,8 +21,10 @@  #include "../../transport/super_recv_packet_handler.hpp"  #include "../../transport/super_send_packet_handler.hpp"  #include "async_packet_handler.hpp" +#include <uhd/utils/math.hpp>  #include <boost/bind.hpp>  #include <boost/make_shared.hpp> +#include <boost/math/common_factor.hpp>  #include <set>  using namespace uhd; @@ -34,30 +36,32 @@ using namespace uhd::transport;   **********************************************************************/  void b200_impl::check_tick_rate_with_current_streamers(double rate)  { -    size_t max_tx_chan_count = 0, max_rx_chan_count = 0; +    // Defined in b200_impl.cpp +    enforce_tick_rate_limits(max_chan_count("RX"), rate, "RX"); +    enforce_tick_rate_limits(max_chan_count("TX"), rate, "TX"); +} + +// direction can either be "TX", "RX", or empty (default) +size_t b200_impl::max_chan_count(const std::string &direction /* = "" */) +{ +    size_t max_count = 0;      BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)      { -        { +        if ((direction == "RX" or direction.empty()) and not perif.rx_streamer.expired()) {              boost::shared_ptr<sph::recv_packet_streamer> rx_streamer =                  boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); -            if (rx_streamer) -                max_rx_chan_count = std::max(max_rx_chan_count, rx_streamer->get_num_channels()); +            max_count = std::max(max_count, rx_streamer->get_num_channels());          } - -        { +        if ((direction == "TX" or direction.empty()) and not perif.tx_streamer.expired()) {              boost::shared_ptr<sph::send_packet_streamer> tx_streamer =                  boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); -            if (tx_streamer) -                max_tx_chan_count = std::max(max_tx_chan_count, tx_streamer->get_num_channels()); +            max_count = std::max(max_count, tx_streamer->get_num_channels());          }      } - -    // Defined in b200_impl.cpp -    enforce_tick_rate_limits(max_rx_chan_count, rate, "RX"); -    enforce_tick_rate_limits(max_tx_chan_count, rate, "TX"); +    return max_count;  } -void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const char* direction /*= NULL*/) +void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_rate, const std::string &direction /*= ""*/)  {      std::set<size_t> chans_set;      for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) @@ -69,33 +73,115 @@ void b200_impl::check_streamer_args(const uhd::stream_args_t &args, double tick_      enforce_tick_rate_limits(chans_set.size(), tick_rate, direction);   // Defined in b200_impl.cpp  } -void b200_impl::update_tick_rate(const double rate) +void b200_impl::set_auto_tick_rate( +        const double rate, +        const fs_path &tree_dsp_path, +        size_t num_chans +) { +    if (num_chans == 0) { // Divine them +        num_chans = std::max(size_t(1), max_chan_count()); +    } +    const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE/num_chans; +    if (rate != 0.0 and +        (uhd::math::fp_compare::fp_compare_delta<double>(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("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.") +                % (rate / 1e6) % (max_tick_rate / 1e6) +        )); +    } + +    // See also the doxygen documentation for these steps in b200_impl.hpp +    // Step 1: Obtain LCM and max rate from all relevant dsps +    boost::uint32_t lcm_rate = (rate == 0) ? 1 : static_cast<boost::uint32_t>(floor(rate + 0.5)); +    for (int i = 0; i < 2; i++) { // Loop through rx and tx +        std::string dir = (i == 0) ? "tx" : "rx"; +        // We have no way of knowing which DSPs are used, so we check them all. +        BOOST_FOREACH(const std::string &dsp_no, _tree->list(str(boost::format("/mboards/0/%s_dsps") % dir))) { +            fs_path dsp_path = str(boost::format("/mboards/0/%s_dsps/%s") % dir % dsp_no); +            if (dsp_path == tree_dsp_path) { +                continue; +            } +            double this_dsp_rate = _tree->access<double>(dsp_path / "rate/value").get(); +            // Check if the user selected something completely unreasonable: +            if (uhd::math::fp_compare::fp_compare_delta<double>(this_dsp_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("Requested sampling rate (%.2f Msps) exceeds maximum tick rate of %.2f MHz.") +                        % (this_dsp_rate / 1e6) % (max_tick_rate / 1e6) +                )); +            } +            // If this_dsp_rate == 0.0, the sampling rate for this DSP hasn't been set, so +            // we don't take that into consideration. +            if (this_dsp_rate == 0.0) { +                continue; +            } +            lcm_rate = boost::math::lcm<boost::uint32_t>( +                    lcm_rate, +                    static_cast<boost::uint32_t>(floor(this_dsp_rate + 0.5)) +            ); +        } +    } +    if (lcm_rate == 1) { +        // In this case, no one has ever set a sampling rate. +        return; +    } + +    double base_rate = static_cast<double>(lcm_rate); +    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 +    } +} + +void b200_impl::update_tick_rate(const double new_tick_rate)  { -    check_tick_rate_with_current_streamers(rate); +    check_tick_rate_with_current_streamers(new_tick_rate);      BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)      {          boost::shared_ptr<sph::recv_packet_streamer> my_streamer =              boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock()); -        if (my_streamer) my_streamer->set_tick_rate(rate); -        perif.framer->set_tick_rate(_tick_rate); +        if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); +        perif.framer->set_tick_rate(new_tick_rate);      }      BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)      {          boost::shared_ptr<sph::send_packet_streamer> my_streamer =              boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock()); -        if (my_streamer) my_streamer->set_tick_rate(rate); -        perif.deframer->set_tick_rate(_tick_rate); +        if (my_streamer) my_streamer->set_tick_rate(new_tick_rate); +        perif.deframer->set_tick_rate(new_tick_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; \ +#define CHECK_RATE_AND_THROW(rate)  \ +        if (uhd::math::fp_compare::fp_compare_delta<double>(rate, uhd::math::FREQ_COMPARISON_DELTA_HZ) > \ +            uhd::math::fp_compare::fp_compare_delta<double>(ad9361_device_t::AD9361_MAX_CLOCK_RATE, uhd::math::FREQ_COMPARISON_DELTA_HZ)) { \ +            throw uhd::value_error(str( \ +                    boost::format("Requested sampling rate (%.2f Msps) exceeds maximum tick rate.") \ +                    % (rate / 1e6) \ +            )); \ +        } + +double b200_impl::coerce_rx_samp_rate(rx_dsp_core_3000::sptr ddc, size_t dspno, const double rx_rate) +{ +    // Have to set tick rate first, or the ddc will change the requested rate based on default tick rate +    if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { +        CHECK_RATE_AND_THROW(rx_rate); +        const std::string dsp_path = (boost::format("/mboards/0/rx_dsps/%s") % dspno).str(); +        set_auto_tick_rate(rx_rate, dsp_path);      } +    return ddc->set_host_rate(rx_rate); +}  void b200_impl::update_rx_samp_rate(const size_t dspno, const double rate)  { @@ -105,7 +191,18 @@ 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) +{ +    // Have to set tick rate first, or the duc will change the requested rate based on default tick rate +    if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { +        CHECK_RATE_AND_THROW(tx_rate); +        const std::string dsp_path = (boost::format("/mboards/0/tx_dsps/%s") % dspno).str(); +        set_auto_tick_rate(tx_rate, dsp_path); +    } +    return duc->set_host_rate(tx_rate);  }  void b200_impl::update_tx_samp_rate(const size_t dspno, const double rate) @@ -116,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");  }  /*********************************************************************** @@ -276,6 +373,9 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_)      if (args.otw_format.empty()) args.otw_format = "sc16";      args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; +    if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { +        set_auto_tick_rate(0, "", args.channels.size()); +    }      check_streamer_args(args, this->get_tick_rate(), "RX");      boost::shared_ptr<sph::recv_packet_streamer> my_streamer; @@ -383,7 +483,10 @@ tx_streamer::sptr b200_impl::get_tx_stream(const uhd::stream_args_t &args_)      if (args.otw_format.empty()) args.otw_format = "sc16";      args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; -    check_streamer_args(args, this->get_tick_rate(), "TX"); +    if (_tree->access<bool>("/mboards/0/auto_tick_rate").get()) { +        set_auto_tick_rate(0, "", args.channels.size()); +    } +    check_streamer_args(args, this->get_tick_rate(), "RX");      boost::shared_ptr<sph::send_packet_streamer> my_streamer;      for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) diff --git a/host/lib/usrp/b200/b200_regs.hpp b/host/lib/usrp/b200/b200_regs.hpp index 900651f94..8f2dd03f3 100644 --- a/host/lib/usrp/b200/b200_regs.hpp +++ b/host/lib/usrp/b200/b200_regs.hpp @@ -46,11 +46,13 @@ localparam SR_TX_DSP    = 184;  localparam SR_TIME      = 128;  localparam SR_RX_FMT    = 136;  localparam SR_TX_FMT    = 138; +localparam SR_FP_GPIO   = 200;  localparam RB32_TEST            = 0;  localparam RB64_TIME_NOW        = 8;  localparam RB64_TIME_PPS        = 16;  localparam RB64_CODEC_READBACK  = 24; +localparam RB32_FP_GPIO         = 32;  //pll constants  static const int AD9361_SLAVENO = (1 << 0);  | 
