diff options
Diffstat (limited to 'host/lib/usrp')
76 files changed, 5745 insertions, 2611 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index f6788b5ef..ce913aaf6 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -18,6 +18,10 @@  ########################################################################  # This file included, use CMake directory variables  ######################################################################## +find_package(GPSD) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) +  LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/dboard_base.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/dboard_eeprom.cpp @@ -30,6 +34,15 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec.cpp  ) +LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBGPS_FOUND" OFF) + +IF(ENABLE_GPSD) +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.cpp +    ) +    LIBUHD_APPEND_LIBS(${LIBGPS_LIBRARY}) +ENDIF(ENABLE_GPSD) +  INCLUDE_SUBDIRECTORY(cores)  INCLUDE_SUBDIRECTORY(dboard)  INCLUDE_SUBDIRECTORY(common) 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); 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.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp index 65e8e2df9..2d9f297b3 100644 --- a/host/lib/usrp/common/ad9361_ctrl.cpp +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -16,7 +16,6 @@  //  #include "ad9361_ctrl.hpp" -#include <uhd/exception.hpp>  #include <uhd/types/ranges.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/types/serial.hpp> @@ -108,6 +107,27 @@ public:          return _device.set_gain(direction, chain, value);      } +    void set_agc(const std::string &which, bool enable) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); +         _device.set_agc(chain, enable); +    } + +    void set_agc_mode(const std::string &which, const std::string &mode) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); +        if(mode == "slow") { +            _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_SLOW_AGC); +        } else if (mode == "fast"){ +            _device.set_agc_mode(chain, ad9361_device_t::GAIN_MODE_FAST_AGC); +        } else { +            throw uhd::runtime_error("ad9361_ctrl got an invalid AGC option."); +        } +    } +      //! set a new clock rate, return the exact value      double set_clock_rate(const double rate)      { @@ -175,6 +195,62 @@ public:          return sensor_value_t("RSSI", _device.get_rssi(chain), "dB");      } +    //! read the internal temp sensor. Average over 3 results +    sensor_value_t get_temperature() +    { +        return sensor_value_t("temp", _device.get_average_temperature(), "C"); +    } + +    void set_dc_offset_auto(const std::string &which, const bool on) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        _device.set_dc_offset_auto(direction,on); +    } + +    void set_iq_balance_auto(const std::string &which, const bool on) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        _device.set_iq_balance_auto(direction,on); +    } + +    double set_bw_filter(const std::string &which, const double bw) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        return _device.set_bw_filter(direction, bw); +    } + +    std::vector<std::string> get_filter_names(const std::string &which) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        return _device.get_filter_names(direction); +    } + +    filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); +        return _device.get_filter(direction, chain, filter_name); +    } + +    void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr filter) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); +        ad9361_device_t::chain_t chain = _get_chain_from_antenna(which); +        _device.set_filter(direction, chain, filter_name, filter); +    } +  private:      static ad9361_device_t::direction_t _get_direction_from_antenna(const std::string& antenna)      { diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index b7d7b8e26..044265422 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -22,15 +22,31 @@  #include <uhd/types/ranges.hpp>  #include <uhd/types/serial.hpp>  #include <uhd/types/sensors.hpp> +#include <uhd/exception.hpp>  #include <boost/shared_ptr.hpp>  #include <ad9361_device.h>  #include <string> +#include <complex> +#include <uhd/types/filters.hpp> +#include <vector>  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: @@ -77,15 +93,18 @@ public:          return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end      } -    //! set the filter bandwidth for the frontend -    double set_bw_filter(const std::string &/*which*/, const double /*bw*/) -    { -        return 56e6; //TODO -    } +    //! set the filter bandwidth for the frontend's analog low pass +    virtual double set_bw_filter(const std::string &/*which*/, const double /*bw*/) = 0;      //! set the gain for a particular gain element      virtual double set_gain(const std::string &which, const double value) = 0; +    //! Enable or disable the AGC module +    virtual void set_agc(const std::string &which, bool enable) = 0; + +    //! configure the AGC module to slow or fast mode +    virtual void set_agc_mode(const std::string &which, const std::string &mode) = 0; +      //! set a new clock rate, return the exact value      virtual double set_clock_rate(const double rate) = 0; @@ -95,14 +114,46 @@ public:      //! tune the given frontend, return the exact value      virtual double tune(const std::string &which, const double value) = 0; +    //! set the DC offset for I and Q manually +    void set_dc_offset(const std::string &, const std::complex<double>) +    { +        //This feature should not be used according to Analog Devices +        throw uhd::runtime_error("ad9361_ctrl::set_dc_offset this feature is not supported on this device."); +    } + +    //! enable or disable the BB/RF DC tracking feature +    virtual void set_dc_offset_auto(const std::string &which, const bool on) = 0; + +    //! set the IQ correction value manually +    void set_iq_balance(const std::string &, const std::complex<double>) +    { +        //This feature should not be used according to Analog Devices +        throw uhd::runtime_error("ad9361_ctrl::set_iq_balance this feature is not supported on this device."); +    } + +    //! enable or disable the quadrature calibration +    virtual void set_iq_balance_auto(const std::string &which, const bool on) = 0; +      //! get the current frequency for the given frontend      virtual double get_freq(const std::string &which) = 0; -    //! turn on/off data port loopback +    //! turn on/off Catalina's data port loopback      virtual void data_port_loopback(const bool on) = 0;      //! read internal RSSI sensor      virtual sensor_value_t get_rssi(const std::string &which) = 0; + +    //! read the internal temp sensor +    virtual sensor_value_t get_temperature() = 0; + +    //! List all available filters by name +    virtual std::vector<std::string> get_filter_names(const std::string &which) = 0; + +    //! Return a list of all filters +    virtual filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) = 0; + +    //! Write back a filter +    virtual void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr) = 0;  };  }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_client.h b/host/lib/usrp/common/ad9361_driver/ad9361_client.h index 5e848d4c0..e9ea1404a 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_client.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_client.h @@ -1,5 +1,18 @@  // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 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_CLIENT_H diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index db5de52d0..29241f6ba 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -1,5 +1,18 @@  // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 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 "ad9361_filter_taps.h" @@ -11,6 +24,7 @@  #include <cmath>  #include <uhd/exception.hpp>  #include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp>  #include <boost/cstdint.hpp>  #include <boost/date_time/posix_time/posix_time.hpp>  #include <boost/thread/thread.hpp> @@ -78,6 +92,7 @@ int get_num_taps(int max_num_taps) {  const double ad9361_device_t::AD9361_MAX_GAIN        = 89.75;  const double ad9361_device_t::AD9361_MAX_CLOCK_RATE  = 61.44e6; +const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6;  // Max bandwdith is due to filter rolloff in analog filter stage  const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6; @@ -87,7 +102,7 @@ const double ad9361_device_t::AD9361_RECOMMENDED_MAX_BANDWIDTH = 56e6;   * how many taps are in the filter, and given a vector of the taps   * themselves.  */ -void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs) +void ad9361_device_t::_program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs)  {      boost::uint16_t base; @@ -102,8 +117,20 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b      /* Encode number of filter taps for programming register */      boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; +    boost::uint8_t reg_chain = 0; +    switch (chain) { +    case CHAIN_1: +        reg_chain = 0x01 << 3; +        break; +    case CHAIN_2: +        reg_chain = 0x02 << 3; +        break; +    default: +        reg_chain = 0x03 << 3; +    } +      /* Turn on the filter clock. */ -    _io_iface->poke8(base + 5, reg_numtaps | 0x1a); +    _io_iface->poke8(base + 5, reg_numtaps | reg_chain | 0x02);      boost::this_thread::sleep(boost::posix_time::milliseconds(1));      /* Zero the unused taps just in case they have stale data */ @@ -112,7 +139,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b          _io_iface->poke8(base + 0, addr);          _io_iface->poke8(base + 1, 0x0);          _io_iface->poke8(base + 2, 0x0); -        _io_iface->poke8(base + 5, reg_numtaps | 0x1e); +        _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2));          _io_iface->poke8(base + 4, 0x00);          _io_iface->poke8(base + 4, 0x00);      } @@ -122,7 +149,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b          _io_iface->poke8(base + 0, addr);          _io_iface->poke8(base + 1, (coeffs[addr]) & 0xff);          _io_iface->poke8(base + 2, (coeffs[addr] >> 8) & 0xff); -        _io_iface->poke8(base + 5, reg_numtaps | 0x1e); +        _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1) | (1 << 2));          _io_iface->poke8(base + 4, 0x00);          _io_iface->poke8(base + 4, 0x00);      } @@ -133,9 +160,9 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b       before the clock stops. Wait 4 sample clock periods after setting D2 high while that data writes into the table"       */ -    _io_iface->poke8(base + 5, reg_numtaps | 0x1A); +    _io_iface->poke8(base + 5, reg_numtaps | reg_chain | (1 << 1));      if (direction == RX) { -        _io_iface->poke8(base + 5, reg_numtaps | 0x18); +        _io_iface->poke8(base + 5, reg_numtaps | reg_chain );          /* Rx Gain, set to prevent digital overflow/saturation in filters             0:+6dB, 1:0dB, 2:-6dB, 3:-12dB             page 35 of UG-671 */ @@ -144,7 +171,7 @@ void ad9361_device_t::_program_fir_filter(direction_t direction, int num_taps, b          /* Tx Gain. bit[0]. set to prevent digital overflow/saturation in filters             0: 0dB, 1:-6dB             page 25 of UG-671 */ -        _io_iface->poke8(base + 5, reg_numtaps | 0x18); +        _io_iface->poke8(base + 5, reg_numtaps | reg_chain );      }  } @@ -175,7 +202,7 @@ void ad9361_device_t::_setup_rx_fir(size_t num_taps, boost::int32_t decimation)          }      } -    _program_fir_filter(RX, num_taps, coeffs.get()); +    _program_fir_filter(RX, CHAIN_BOTH, num_taps, coeffs.get());  }  /* Program the TX FIR Filter. */ @@ -207,7 +234,7 @@ void ad9361_device_t::_setup_tx_fir(size_t num_taps, boost::int32_t interpolatio          }      } -    _program_fir_filter(TX, num_taps, coeffs.get()); +    _program_fir_filter(TX, CHAIN_BOTH, num_taps, coeffs.get());  }  /*********************************************************************** @@ -282,16 +309,24 @@ void ad9361_device_t::_calibrate_synth_charge_pumps()   *   * Note that the filter calibration depends heavily on the baseband   * bandwidth, so this must be re-done after any change to the RX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_rx_analog_filter() + * rate. + * UG570 Page 33 states that this filter should be calibrated to 1.4 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_rx_analog_filter(double req_rfbw)  { -    /* For filter tuning, baseband BW is half the complex BW, and must be -     * between 28e6 and 0.2e6. */ -    double bbbw = _baseband_bw / 2.0; +    double bbbw = req_rfbw / 2.0; +    if(bbbw > _baseband_bw / 2.0) +    { +        UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; +        bbbw = _baseband_bw / 2.0; +    } + +    /* Baseband BW must be between 28e6 and 0.143e6. +     * Max filter BW is 39.2 MHz. 39.2 / 1.4 = 28 +     * Min filter BW is 200kHz. 200 / 1.4 = 143 */      if (bbbw > 28e6) {          bbbw = 28e6; -    } else if (bbbw < 0.20e6) { -        bbbw = 0.20e6; +    } else if (bbbw < 0.143e6) { +        bbbw = 0.143e6;      }      double rxtune_clk = ((1.4 * bbbw * 2 * M_PI) / M_LN2); @@ -340,16 +375,25 @@ double ad9361_device_t::_calibrate_baseband_rx_analog_filter()   *   * Note that the filter calibration depends heavily on the baseband   * bandwidth, so this must be re-done after any change to the TX sample - * rate. */ -double ad9361_device_t::_calibrate_baseband_tx_analog_filter() + * rate. + * UG570 Page 32 states that this filter should be calibrated to 1.6 * bbbw*/ +double ad9361_device_t::_calibrate_baseband_tx_analog_filter(double req_rfbw)  { -    /* For filter tuning, baseband BW is half the complex BW, and must be -     * between 28e6 and 0.2e6. */ -    double bbbw = _baseband_bw / 2.0; +    double bbbw = req_rfbw / 2.0; + +    if(bbbw > _baseband_bw / 2.0) +    { +        UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; +        bbbw = _baseband_bw / 2.0; +    } + +    /* Baseband BW must be between 20e6 and 0.391e6. +     * Max filter BW is 32 MHz. 32 / 1.6 = 20 +     * Min filter BW is 625 kHz. 625 / 1.6 = 391 */      if (bbbw > 20e6) {          bbbw = 20e6; -    } else if (bbbw < 0.625e6) { -        bbbw = 0.625e6; +    } else if (bbbw < 0.391e6) { +        bbbw = 0.391e6;      }      double txtune_clk = ((1.6 * bbbw * 2 * M_PI) / M_LN2); @@ -386,16 +430,25 @@ double ad9361_device_t::_calibrate_baseband_tx_analog_filter()  /* Calibrate the secondary TX filter.   *   * This filter also depends on the TX sample rate, so if a rate change is - * made, the previous calibration will no longer be valid. */ -void ad9361_device_t::_calibrate_secondary_tx_filter() + * made, the previous calibration will no longer be valid. + * UG570 Page 32 states that this filter should be calibrated to 5 * bbbw*/ +double ad9361_device_t::_calibrate_secondary_tx_filter(double req_rfbw)  { -    /* For filter tuning, baseband BW is half the complex BW, and must be -     * between 20e6 and 0.53e6. */ -    double bbbw = _baseband_bw / 2.0; +    double bbbw = req_rfbw / 2.0; + +    if(bbbw > _baseband_bw / 2.0) +    { +        UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; +        bbbw = _baseband_bw / 2.0; +    } + +    /* Baseband BW must be between 20e6 and 0.54e6. +     * Max filter BW is 100 MHz. 100 / 5 = 20 +     * Min filter BW is 2.7 MHz. 2.7 / 5 = 0.54 */      if (bbbw > 20e6) {          bbbw = 20e6; -    } else if (bbbw < 0.53e6) { -        bbbw = 0.53e6; +    } else if (bbbw < 0.54e6) { +        bbbw = 0.54e6;      }      double bbbw_mhz = bbbw / 1e6; @@ -456,13 +509,17 @@ void ad9361_device_t::_calibrate_secondary_tx_filter()      _io_iface->poke8(0x0d2, reg0d2);      _io_iface->poke8(0x0d1, reg0d1);      _io_iface->poke8(0x0d0, reg0d0); + +    return bbbw;  }  /* Calibrate the RX TIAs.   *   * Note that the values in the TIA register, after calibration, vary with - * the RX gain settings. */ -void ad9361_device_t::_calibrate_rx_TIAs() + * the RX gain settings. + * We do not really program the BW here. Most settings are taken form the BB LPF registers + * UG570 page 33 states that this filter should be calibrated to 2.5 * bbbw */ +double ad9361_device_t::_calibrate_rx_TIAs(double req_rfbw)  {      boost::uint8_t reg1eb = _io_iface->peek8(0x1eb) & 0x3F;      boost::uint8_t reg1ec = _io_iface->peek8(0x1ec) & 0x7F; @@ -473,13 +530,21 @@ void ad9361_device_t::_calibrate_rx_TIAs()      boost::uint8_t reg1de = 0x00;      boost::uint8_t reg1df = 0x00; -    /* For calibration, baseband BW is half the complex BW, and must be -     * between 28e6 and 0.2e6. */ -    double bbbw = _baseband_bw / 2.0; -    if (bbbw > 20e6) { -        bbbw = 20e6; -    } else if (bbbw < 0.20e6) { -        bbbw = 0.20e6; +    double bbbw = req_rfbw / 2.0; + +    if(bbbw > _baseband_bw / 2.0) +    { +        UHD_LOG << "baseband bandwidth too large for current sample rate. Setting bandwidth to: "<<_baseband_bw; +        bbbw = _baseband_bw / 2.0; +    } + +    /* Baseband BW must be between 28e6 and 0.4e6. +     * Max filter BW is 70 MHz. 70 / 2.5 = 28 +     * Min filter BW is 1 MHz. 1 / 2.5 =  0.4*/ +    if (bbbw > 28e6) { +        bbbw = 28e6; +    } else if (bbbw < 0.40e6) { +        bbbw = 0.40e6;      }      double ceil_bbbw_mhz = std::ceil(bbbw / 1e6); @@ -520,6 +585,8 @@ void ad9361_device_t::_calibrate_rx_TIAs()      _io_iface->poke8(0x1df, reg1df);      _io_iface->poke8(0x1dc, reg1dc);      _io_iface->poke8(0x1de, reg1de); + +    return bbbw;  }  /* Setup the AD9361 ADC. @@ -651,11 +718,12 @@ void ad9361_device_t::_setup_adc()  }  /* Calibrate the baseband DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function! */ + * Disables tracking + */  void ad9361_device_t::_calibrate_baseband_dc_offset()  { +    _io_iface->poke8(0x18b, 0x83); //Reset RF DC tracking flag +      _io_iface->poke8(0x193, 0x3f); // Calibration settings      _io_iface->poke8(0x190, 0x0f); // Set tracking coefficient      //write_ad9361_reg(device, 0x190, /*0x0f*//*0xDF*/0x80*1 | 0x40*1 | (16+8/*+4*/)); // Set tracking coefficient: don't *4 counter, do decim /4, increased gain shift @@ -675,9 +743,8 @@ void ad9361_device_t::_calibrate_baseband_dc_offset()  }  /* Calibrate the RF DC offset. - * - * Note that this function is called from within the TX quadrature - * calibration function. */ + * Disables tracking + */  void ad9361_device_t::_calibrate_rf_dc_offset()  {      /* Some settings are frequency-dependent. */ @@ -692,7 +759,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset()      }      _io_iface->poke8(0x185, 0x20); // RF DC Offset wait count -    _io_iface->poke8(0x18b, 0x83); +    _io_iface->poke8(0x18b, 0x83); // Disable tracking      _io_iface->poke8(0x189, 0x30);      /* Run the calibration! */ @@ -708,6 +775,16 @@ void ad9361_device_t::_calibrate_rf_dc_offset()      }  } +void ad9361_device_t::_configure_bb_rf_dc_tracking(const bool on) +{ +    if(on) +    { +        _io_iface->poke8(0x18b, 0xad); // Enable BB and RF DC tracking +    } else { +        _io_iface->poke8(0x18b, 0x83); // Disable BB and RF DC tracking +    } +} +  /* Start the RX quadrature calibration.   *   * Note that we are using AD9361's 'tracking' feature for RX quadrature @@ -719,17 +796,21 @@ void ad9361_device_t::_calibrate_rx_quadrature()      _io_iface->poke8(0x168, 0x03); // Set tone level for cal      _io_iface->poke8(0x16e, 0x25); // RX Gain index to use for cal      _io_iface->poke8(0x16a, 0x75); // Set Kexp phase -    _io_iface->poke8(0x16b, 0x15); // Set Kexp amplitude -    _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode -    _io_iface->poke8(0x18b, 0xad); +    _io_iface->poke8(0x16b, 0x95); // Set Kexp amplitude + +    if(_use_iq_balance_correction) +    { +        _io_iface->poke8(0x169, 0xcf); // Continuous tracking mode. Gets disabled in _tx_quadrature_cal_routine! +    }  } -/* TX quadtrature calibration routine. +/* TX quadrature calibration routine.   *   * The TX quadrature needs to be done twice, once for each TX chain, with   * only one register change in between. Thus, this function enacts the   * calibrations, and it is called from calibrate_tx_quadrature. */  void ad9361_device_t::_tx_quadrature_cal_routine() { +      /* This is a weird process, but here is how it works:       * 1) Read the calibrated NCO frequency bits out of 0A3.       * 2) Write the two bits to the RX NCO freq part of 0A0. @@ -765,7 +846,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() {      /* The gain table index used for calibration must be adjusted for the       * mid-table to get a TIA index = 1 and LPF index = 0. */ -    if ((_rx_freq >= 1300e6) && (_rx_freq < 4000e6)) { +    if (_rx_freq < 1300e6) {          _io_iface->poke8(0x0aa, 0x22); // Cal gain table index      } else {          _io_iface->poke8(0x0aa, 0x25); // Cal gain table index @@ -774,12 +855,6 @@ void ad9361_device_t::_tx_quadrature_cal_routine() {      _io_iface->poke8(0x0a4, 0xf0); // Cal setting conut      _io_iface->poke8(0x0ae, 0x00); // Cal LPF gain index (split mode) -    /* First, calibrate the baseband DC offset. */ -    _calibrate_baseband_dc_offset(); - -    /* Second, calibrate the RF DC offset. */ -    _calibrate_rf_dc_offset(); -      /* Now, calibrate the TX quadrature! */      size_t count = 0;      _io_iface->poke8(0x016, 0x10); @@ -794,9 +869,7 @@ void ad9361_device_t::_tx_quadrature_cal_routine() {  }  /* Run the TX quadrature calibration. - * - * Note that from within this function we are also triggering the baseband - * and RF DC calibrations. */ + */  void ad9361_device_t::_calibrate_tx_quadrature()  {      /* Make sure we are, in fact, in the ALERT state. If not, something is @@ -880,7 +953,7 @@ void ad9361_device_t::_program_mixer_gm_subtable()  void ad9361_device_t::_program_gain_table() {      /* Figure out which gain table we should be using for our current       * frequency band. */ -    boost::uint8_t (*gain_table)[5] = NULL; +    boost::uint8_t (*gain_table)[3] = NULL;      boost::uint8_t new_gain_table;      if (_rx_freq < 1300e6) {          gain_table = gain_table_sub_1300mhz; @@ -911,9 +984,9 @@ void ad9361_device_t::_program_gain_table() {      boost::uint8_t index = 0;      for (; index < 77; index++) {          _io_iface->poke8(0x130, index); -        _io_iface->poke8(0x131, gain_table[index][1]); -        _io_iface->poke8(0x132, gain_table[index][2]); -        _io_iface->poke8(0x133, gain_table[index][3]); +        _io_iface->poke8(0x131, gain_table[index][0]); +        _io_iface->poke8(0x132, gain_table[index][1]); +        _io_iface->poke8(0x133, gain_table[index][2]);          _io_iface->poke8(0x137, 0x1E);          _io_iface->poke8(0x134, 0x00);          _io_iface->poke8(0x134, 0x00); @@ -939,28 +1012,58 @@ void ad9361_device_t::_program_gain_table() {  /* Setup gain control registers.   * - * This really only needs to be done once, at initialization. */ -void ad9361_device_t::_setup_gain_control() + * This really only needs to be done once, at initialization. + * If AGC is used the mode select bits (Reg 0x0FA) must be written manually */ +void ad9361_device_t::_setup_gain_control(bool agc)  { -    _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select -    _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl -    _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size -    _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index -    _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time -    _io_iface->poke8(0x100, 0x6F); // Max Digital Gain -    _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold -    _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold -    _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold -    _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold -    _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index -    _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index -    _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index -    _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index -    _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index -    _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index -    _io_iface->poke8(0x114, 0x30); // Low Power Threshold -    _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit -    _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control +    /* The AGC mode configuration should be good for all cases. +     * However, non AGC configuration still used for backward compatibility. */ +    if (agc) { +        /*mode select bits must be written before hand!*/ +        _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl +        _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size +        _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index +        _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time +        _io_iface->poke8(0x100, 0x6F); // Max Digital Gain +        _io_iface->poke8(0x101, 0x0A); // Max Digital Gain +        _io_iface->poke8(0x103, 0x08); // Max Digital Gain +        _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold +        _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold +        _io_iface->poke8(0x106, 0x22); // Max Digital Gain +        _io_iface->poke8(0x107, 0x2B); // Large LMT Overload Threshold +        _io_iface->poke8(0x108, 0x31); +        _io_iface->poke8(0x111, 0x0A); +        _io_iface->poke8(0x11A, 0x1C); +        _io_iface->poke8(0x120, 0x0C); +        _io_iface->poke8(0x121, 0x44); +        _io_iface->poke8(0x122, 0x44); +        _io_iface->poke8(0x123, 0x11); +        _io_iface->poke8(0x124, 0xF5); +        _io_iface->poke8(0x125, 0x3B); +        _io_iface->poke8(0x128, 0x03); +        _io_iface->poke8(0x129, 0x56); +        _io_iface->poke8(0x12A, 0x22); +    } else { +        _io_iface->poke8(0x0FA, 0xE0); // Gain Control Mode Select +        _io_iface->poke8(0x0FB, 0x08); // Table, Digital Gain, Man Gain Ctrl +        _io_iface->poke8(0x0FC, 0x23); // Incr Step Size, ADC Overrange Size +        _io_iface->poke8(0x0FD, 0x4C); // Max Full/LMT Gain Table Index +        _io_iface->poke8(0x0FE, 0x44); // Decr Step Size, Peak Overload Time +        _io_iface->poke8(0x100, 0x6F); // Max Digital Gain +        _io_iface->poke8(0x104, 0x2F); // ADC Small Overload Threshold +        _io_iface->poke8(0x105, 0x3A); // ADC Large Overload Threshold +        _io_iface->poke8(0x107, 0x31); // Large LMT Overload Threshold +        _io_iface->poke8(0x108, 0x39); // Small LMT Overload Threshold +        _io_iface->poke8(0x109, 0x23); // Rx1 Full/LMT Gain Index +        _io_iface->poke8(0x10A, 0x58); // Rx1 LPF Gain Index +        _io_iface->poke8(0x10B, 0x00); // Rx1 Digital Gain Index +        _io_iface->poke8(0x10C, 0x23); // Rx2 Full/LMT Gain Index +        _io_iface->poke8(0x10D, 0x18); // Rx2 LPF Gain Index +        _io_iface->poke8(0x10E, 0x00); // Rx2 Digital Gain Index +        _io_iface->poke8(0x114, 0x30); // Low Power Threshold +        _io_iface->poke8(0x11A, 0x27); // Initial LMT Gain Limit +        _io_iface->poke8(0x081, 0x00); // Tx Symbol Gain Control +    }  }  /* Setup the RX or TX synthesizers. @@ -1257,6 +1360,7 @@ double ad9361_device_t::_setup_rates(const double rate)      int divfactor = 0;      _tfir_factor = 0;      _rfir_factor = 0; +      if (rate < 0.33e6) {          // RX1 + RX2 enabled, 3, 2, 2, 4          _regs.rxfilt = B8(11101111); @@ -1412,6 +1516,19 @@ void ad9361_device_t::initialize()      _rx2_gain = 0;      _tx1_gain = 0;      _tx2_gain = 0; +    _use_dc_offset_correction = true; +    _use_iq_balance_correction = true; +    _rx1_agc_mode = GAIN_MODE_SLOW_AGC; +    _rx2_agc_mode = GAIN_MODE_SLOW_AGC; +    _rx1_agc_enable = false; +    _rx2_agc_enable = false; +    _last_calibration_freq = -AD9361_CAL_VALID_WINDOW; +    _rx_analog_bw = 0; +    _tx_analog_bw = 0; +    _rx_tia_lp_bw = 0; +    _tx_sec_lp_bw = 0; +    _rx_bb_lp_bw = 0; +    _tx_bb_lp_bw = 0;      /* Reset the device. */      _io_iface->poke8(0x000, 0x01); @@ -1490,7 +1607,6 @@ void ad9361_device_t::initialize()      _io_iface->poke8(0x019, 0x00); // AuxDAC2 Word[9:2]      _io_iface->poke8(0x01A, 0x00); // AuxDAC1 Config and Word[1:0]      _io_iface->poke8(0x01B, 0x00); // AuxDAC2 Config and Word[1:0] -    _io_iface->poke8(0x022, 0x4A); // Invert Bypassed LNA      _io_iface->poke8(0x023, 0xFF); // AuxDAC Manaul/Auto Control      _io_iface->poke8(0x026, 0x00); // AuxDAC Manual Select Bit/GPO Manual Select      _io_iface->poke8(0x030, 0x00); // AuxDAC1 Rx Delay @@ -1498,10 +1614,18 @@ void ad9361_device_t::initialize()      _io_iface->poke8(0x032, 0x00); // AuxDAC2 Rx Delay      _io_iface->poke8(0x033, 0x00); // AuxDAC2 Tx Delay +    /* LNA bypass polarity inversion +     *     According to the register map, we should invert the bypass path to +     *     match LNA phase. Extensive testing, however, shows otherwise and that +     *     to align bypass and LNA phases, the bypass inversion switch should be +     *     turned off. +     */ +    _io_iface->poke8(0x022, 0x0A); +      /* Setup AuxADC */      _io_iface->poke8(0x00B, 0x00); // Temp Sensor Setup (Offset)      _io_iface->poke8(0x00C, 0x00); // Temp Sensor Setup (Temp Window) -    _io_iface->poke8(0x00D, 0x03); // Temp Sensor Setup (Periodic Measure) +    _io_iface->poke8(0x00D, 0x00); // Temp Sensor Setup (Manual  Measure)      _io_iface->poke8(0x00F, 0x04); // Temp Sensor Setup (Decimation)      _io_iface->poke8(0x01C, 0x10); // AuxADC Setup (Clock Div)      _io_iface->poke8(0x01D, 0x01); // AuxADC Setup (Decimation/Enable) @@ -1555,17 +1679,18 @@ void ad9361_device_t::initialize()      _program_mixer_gm_subtable();      _program_gain_table(); -    _setup_gain_control(); +    _setup_gain_control(false); -    _calibrate_baseband_rx_analog_filter(); -    _calibrate_baseband_tx_analog_filter(); -    _calibrate_rx_TIAs(); -    _calibrate_secondary_tx_filter(); +    set_bw_filter(RX, _baseband_bw); +    set_bw_filter(TX, _baseband_bw);      _setup_adc(); +    _calibrate_baseband_dc_offset(); +    _calibrate_rf_dc_offset();      _calibrate_tx_quadrature();      _calibrate_rx_quadrature(); +    _configure_bb_rf_dc_tracking(_use_dc_offset_correction);      // cals done, set PPORT config      switch (_client_params->get_digital_interface_mode()) { @@ -1680,18 +1805,19 @@ double ad9361_device_t::set_clock_rate(const double req_rate)      _program_mixer_gm_subtable();      _program_gain_table(); -    _setup_gain_control(); +    _setup_gain_control(false);      _reprogram_gains(); -    _calibrate_baseband_rx_analog_filter(); -    _calibrate_baseband_tx_analog_filter(); -    _calibrate_rx_TIAs(); -    _calibrate_secondary_tx_filter(); +    set_bw_filter(RX, _baseband_bw); +    set_bw_filter(TX, _baseband_bw);      _setup_adc(); +    _calibrate_baseband_dc_offset(); +    _calibrate_rf_dc_offset();      _calibrate_tx_quadrature();      _calibrate_rx_quadrature(); +    _configure_bb_rf_dc_tracking(_use_dc_offset_correction);      // cals done, set PPORT config      switch (_client_params->get_digital_interface_mode()) { @@ -1843,9 +1969,16 @@ double ad9361_device_t::tune(direction_t direction, const double value)      /* Update the gain settings. */      _reprogram_gains(); -    /* Run the calibration algorithms. */ -    _calibrate_tx_quadrature(); -    _calibrate_rx_quadrature(); +    /* Only run the following calibrations if we are more than 100MHz away +     * from the previous calibration point. */ +    if (std::abs(_last_calibration_freq - tune_freq) > AD9361_CAL_VALID_WINDOW) { +        /* Run the calibration algorithms. */ +        _calibrate_rf_dc_offset(); +        _calibrate_tx_quadrature(); +        _calibrate_rx_quadrature(); +        _configure_bb_rf_dc_tracking(_use_dc_offset_correction); +        _last_calibration_freq = tune_freq; +    }      /* If we were in the FDD state, return it now. */      if (not_in_alert) { @@ -1960,4 +2093,678 @@ double ad9361_device_t::get_rssi(chain_t chain)      return rssi;  } +/* + * Returns the reading of the internal temperature sensor. + * One point calibration of the sensor was done according to datasheet + * leading to the given default constant correction factor. + */ +double ad9361_device_t::_get_temperature(const double cal_offset, const double timeout) +{ +    //set 0x01D[0] to 1 to disable AuxADC GPIO reading +    boost::uint8_t tmp = 0; +    tmp = _io_iface->peek8(0x01D); +    _io_iface->poke8(0x01D, (tmp | 0x01)); +    _io_iface->poke8(0x00B, 0); //set offset to 0 + +    _io_iface->poke8(0x00C, 0x01); //start reading, clears bit 0x00C[1] +    boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); +    boost::posix_time::time_duration elapsed; +    //wait for valid data (toggle of bit 1 in 0x00C) +    while(((_io_iface->peek8(0x00C) >> 1) & 0x01) == 0) { +        boost::this_thread::sleep(boost::posix_time::microseconds(100)); +        elapsed = boost::posix_time::microsec_clock::local_time() - start_time; +        if(elapsed.total_milliseconds() > (timeout*1000)) +        { +            throw uhd::runtime_error("[ad9361_device_t] timeout while reading temperature"); +        } +    } +    _io_iface->poke8(0x00C, 0x00); //clear read flag + +    boost::uint8_t temp = _io_iface->peek8(0x00E); //read temperature. +    double tmp_temp = temp/1.140f; //according to ADI driver +    tmp_temp = tmp_temp + cal_offset; //Constant offset acquired by one point calibration. + +    return tmp_temp; +} + +double ad9361_device_t::get_average_temperature(const double cal_offset, const size_t num_samples) +{ +    double d_temp = 0; +    for(size_t i = 0; i < num_samples; i++) { +        double tmp_temp = _get_temperature(cal_offset); +        d_temp += (tmp_temp/num_samples); +    } +    return d_temp; +} + +void ad9361_device_t::set_dc_offset_auto(direction_t direction, const bool on) +{ +    if(direction == RX) +    { +         _use_dc_offset_correction = on; +         _configure_bb_rf_dc_tracking(_use_dc_offset_correction); +        if(on) +        { +            _io_iface->poke8(0x182, (_io_iface->peek8(0x182) & (~((1 << 7) | (1 << 6) | (1 << 3) | (1 << 2))))); //Clear force bits +            //Do a single shot DC offset cal before enabling tracking (Not possible if not in ALERT state. Is it necessary?) +        } else { +            //clear current config values +            _io_iface->poke8(0x182, (_io_iface->peek8(0x182) | ((1 << 7) | (1 << 6) | (1 << 3) | (1 << 2)))); //Set input A and input B&C force enable bits +            _io_iface->poke8(0x174, 0x00); +            _io_iface->poke8(0x175, 0x00); +            _io_iface->poke8(0x176, 0x00); +            _io_iface->poke8(0x177, 0x00); +            _io_iface->poke8(0x178, 0x00); +            _io_iface->poke8(0x17D, 0x00); +            _io_iface->poke8(0x17E, 0x00); +            _io_iface->poke8(0x17F, 0x00); +            _io_iface->poke8(0x180, 0x00); +            _io_iface->poke8(0x181, 0x00); +        } +    } else { +        // DC offset is removed during TX quad cal +        throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] INVALID_CODE_PATH"); +    } +} + +void ad9361_device_t::set_iq_balance_auto(direction_t direction, const bool on) +{ +    if(direction == RX) +    { +        _use_iq_balance_correction = on; +        if(on) +        { +            //disable force registers and enable tracking +            _io_iface->poke8(0x182, (_io_iface->peek8(0x182) & (~ ( (1<<1) | (1<<0) | (1<<5) | (1<<4) )))); +            _calibrate_rx_quadrature(); +        } else { +            //disable IQ tracking +            _io_iface->poke8(0x169, 0xc0); +            //clear current config values +            _io_iface->poke8(0x182, (_io_iface->peek8(0x182) | ((1 << 1) | (1 << 0) | (1 << 5) | (1 << 4)))); //Set Rx2 input B&C force enable bit +            _io_iface->poke8(0x17B, 0x00); +            _io_iface->poke8(0x17C, 0x00); +            _io_iface->poke8(0x179, 0x00); +            _io_iface->poke8(0x17A, 0x00); +            _io_iface->poke8(0x170, 0x00); +            _io_iface->poke8(0x171, 0x00); +            _io_iface->poke8(0x172, 0x00); +            _io_iface->poke8(0x173, 0x00); +        } +    } else { +        throw uhd::runtime_error("[ad9361_device_t] [set_iq_balance_auto] INVALID_CODE_PATH"); +    } +} + +/* Sets the RX gain mode to be used. + * If a transition from an AGC to an non AGC mode occurs (or vice versa) + * the gain configuration will be reloaded. */ +void ad9361_device_t::_setup_agc(chain_t chain, gain_mode_t gain_mode) +{ +    boost::uint8_t gain_mode_reg = 0; +    boost::uint8_t gain_mode_prev = 0; +    boost::uint8_t gain_mode_bits_pos = 0; + +    gain_mode_reg = _io_iface->peek8(0x0FA); +    gain_mode_prev = (gain_mode_reg & 0x0F); + +    if (chain == CHAIN_1) { +        gain_mode_bits_pos = 0; +    } else if (chain == CHAIN_2) { +        gain_mode_bits_pos = 2; +    } else +    { +        throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); +    } + +    gain_mode_reg = (gain_mode_reg & (~(0x03<<gain_mode_bits_pos))); //clear mode bits +    switch (gain_mode) { +        case GAIN_MODE_MANUAL: +            //leave bits cleared +            break; +        case GAIN_MODE_SLOW_AGC: +            gain_mode_reg = (gain_mode_reg | (0x02<<gain_mode_bits_pos)); +            break; +        case GAIN_MODE_FAST_AGC: +            gain_mode_reg = (gain_mode_reg | (0x01<<gain_mode_bits_pos)); +            break; +        default: +            throw uhd::runtime_error("[ad9361_device_t] Gain mode does not exist"); +    } +    _io_iface->poke8(0x0FA, gain_mode_reg); +    boost::uint8_t gain_mode_status = _io_iface->peek8(0x0FA); +    gain_mode_status = (gain_mode_status & 0x0F); +    /*Check if gain mode configuration needs to be reprogrammed*/ +    if (((gain_mode_prev == 0) && (gain_mode_status != 0)) || ((gain_mode_prev != 0) && (gain_mode_status == 0))) { +        if (gain_mode_status == 0) { +            /*load manual mode config*/ +            _setup_gain_control(false); +        } else { +            /*load agc mode config*/ +            _setup_gain_control(true); +        } +    } +} + +void ad9361_device_t::set_agc(chain_t chain, bool enable) +{ +    if(chain == CHAIN_1) { +        _rx1_agc_enable = enable; +        if(enable) { +            _setup_agc(chain, _rx1_agc_mode); +        } else { +            _setup_agc(chain, GAIN_MODE_MANUAL); +        } +    } else if (chain == CHAIN_2){ +        _rx2_agc_enable = enable; +        if(enable) { +            _setup_agc(chain, _rx2_agc_mode); +        } else { +            _setup_agc(chain, GAIN_MODE_MANUAL); +        } +    } else +    { +        throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); +    } +} + +void ad9361_device_t::set_agc_mode(chain_t chain, gain_mode_t gain_mode) +{ +    if(chain == CHAIN_1) { +        _rx1_agc_mode = gain_mode; +        if(_rx1_agc_enable) { +            _setup_agc(chain, _rx1_agc_mode); +        } +    } else if(chain == CHAIN_2){ +        _rx2_agc_mode = gain_mode; +        if(_rx2_agc_enable) { +            _setup_agc(chain, _rx2_agc_mode); +        } +    } else +    { +        throw uhd::runtime_error("[ad9361_device_t] Wrong value for chain"); +    } +} + +std::vector<std::string> ad9361_device_t::get_filter_names(direction_t direction) +{ +    std::vector<std::string> ret; +    if(direction == RX) { +        for(std::map<std::string, filter_query_helper>::iterator it = _rx_filters.begin(); it != _rx_filters.end(); ++it) { +            ret.push_back(it->first); +        } +    } else if (direction == TX) +    { +        for(std::map<std::string, filter_query_helper>::iterator it = _tx_filters.begin(); it != _tx_filters.end(); ++it) { +            ret.push_back(it->first); +        } +    } +    return ret; +} + +filter_info_base::sptr ad9361_device_t::get_filter(direction_t direction, chain_t chain, const std::string &name) +{ +    if(direction == RX) { +        if (not _rx_filters[name].get) +        { +            throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); +        } +        return _rx_filters[name].get(direction, chain); +    } else if (direction == TX) { +        if (not _tx_filters[name].get) +        { +            throw uhd::runtime_error("ad9361_device_t::get_filter this filter can not be read."); +        } +        return _tx_filters[name].get(direction, chain); +    } + +    throw uhd::runtime_error("ad9361_device_t::get_filter wrong direction parameter."); +} + +void ad9361_device_t::set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter) +{ + +    if(direction == RX) { +        if(not _rx_filters[name].set) +        { +            throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); +        } +        _rx_filters[name].set(direction, chain, filter); +    } else if (direction == TX) { +        if(not _tx_filters[name].set) +        { +            throw uhd::runtime_error("ad9361_device_t::set_filter this filter can not be written."); +        } +        _tx_filters[name].set(direction, chain, filter); +    } + +} + +double ad9361_device_t::set_bw_filter(direction_t direction, const double rf_bw) +{ +    //both low pass filters are programmed to the same bw. However, their cutoffs will differ. +    //Together they should create the requested bb bw. +    double set_analog_bb_bw = 0; +    if(direction == RX) +    { +        _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(rf_bw); //returns bb bw +        _rx_tia_lp_bw = _calibrate_rx_TIAs(rf_bw); +        _rx_analog_bw = _rx_bb_lp_bw; +        set_analog_bb_bw = _rx_analog_bw; +    } else { +        _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(rf_bw); //returns bb bw +        _tx_sec_lp_bw = _calibrate_secondary_tx_filter(rf_bw); +        _tx_analog_bw = _tx_bb_lp_bw; +        set_analog_bb_bw = _tx_analog_bw; +    } +    return (2.0 * set_analog_bb_bw); +} + +void ad9361_device_t::_set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps) +{ +    size_t num_taps = taps.size(); +    size_t num_taps_avail = _get_num_fir_taps(direction); +    if(num_taps == num_taps_avail) +    { +        boost::scoped_array<boost::uint16_t> coeffs(new boost::uint16_t[num_taps_avail]); +        for (size_t i = 0; i < num_taps_avail; i++) +        { +            coeffs[i] = boost::uint16_t(taps[i]); +        } +        _program_fir_filter(direction, chain, num_taps_avail, coeffs.get()); +    } else if(num_taps < num_taps_avail){ +        throw uhd::runtime_error("ad9361_device_t::_set_fir_taps not enough coefficients."); +    } else { +        throw uhd::runtime_error("ad9361_device_t::_set_fir_taps too many coefficients."); +    } +} + +size_t ad9361_device_t::_get_num_fir_taps(direction_t direction) +{ +    boost::uint8_t num = 0; +    if(direction == RX) +        num = _io_iface->peek8(0x0F5); +    else +        num = _io_iface->peek8(0x065); +    num = ((num >> 5) & 0x07); +    return ((num + 1) * 16); +} + +size_t ad9361_device_t::_get_fir_dec_int(direction_t direction) +{ +    boost::uint8_t dec_int = 0; +    if(direction == RX) +        dec_int = _io_iface->peek8(0x003); +    else +        dec_int = _io_iface->peek8(0x002); +    /* +     * 0 = dec/int by 1 and bypass filter +     * 1 = dec/int by 1 +     * 2 = dec/int by 2 +     * 3 = dec/int by 4 */ +    dec_int = (dec_int & 0x03); +    if(dec_int == 3) +    { +        return 4; +    } +    return dec_int; +} + +std::vector<boost::int16_t> ad9361_device_t::_get_fir_taps(direction_t direction, chain_t chain) +{ +    int base; +    size_t num_taps = _get_num_fir_taps(direction); +    boost::uint8_t config; +    boost::uint8_t reg_numtaps = (((num_taps / 16) - 1) & 0x07) << 5; +    config = reg_numtaps | 0x02; //start the programming clock + +    if(chain == CHAIN_1) +    { +        config = config | (1 << 3); +    } else if (chain == CHAIN_2){ +        config = config | (1 << 4); +    } else { +        throw uhd::runtime_error("[ad9361_device_t] Can not read both chains synchronously"); +    } + +    if(direction == RX) +    { +        base = 0xF0; +    } else { +        base = 0x60; +    } + +    _io_iface->poke8(base+5,config); + +    std::vector<boost::int16_t> taps; +    boost::uint8_t lower_val; +    boost::uint8_t higher_val; +    boost::uint16_t coeff; +    for(size_t i = 0;i < num_taps;i++) +    { +        _io_iface->poke8(base,0x00+i); +        lower_val = _io_iface->peek8(base+3); +        higher_val = _io_iface->peek8(base+4); +        coeff = ((higher_val << 8) | lower_val); +        taps.push_back(boost::int16_t(coeff)); +    } + +    config = (config & (~(1 << 1))); //disable filter clock +    _io_iface->poke8(base+5,config); +    return taps; +} + +/* + * Returns either RX TIA LPF or TX Secondary LPF + * depending on the direction. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_tia_sec(direction_t direction) +{ +    double cutoff = 0; + +    if(direction == RX) +    { +       cutoff = 2.5 * _rx_tia_lp_bw; +    } else { +       cutoff = 5 * _tx_sec_lp_bw; +    } + +    filter_info_base::sptr lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 0, "single-pole", cutoff, 20)); +    return  lp; +} + +/* + * Returns RX/TX BB LPF. + * See UG570 for details on used scaling factors. */ +filter_info_base::sptr ad9361_device_t::_get_filter_lp_bb(direction_t direction) +{ +    double cutoff = 0; +    if(direction == RX) +    { +        cutoff = 1.4 * _rx_bb_lp_bw; +    } else { +        cutoff = 1.6 * _tx_bb_lp_bw; +    } + +    filter_info_base::sptr bb_lp(new analog_filter_lp(filter_info_base::ANALOG_LOW_PASS, false, 1, "third-order Butterworth", cutoff, 60)); +    return  bb_lp; +} + +/* + * For RX direction the DEC3 is returned. + * For TX direction the INT3 is returned. */ +filter_info_base::sptr ad9361_device_t::_get_filter_dec_int_3(direction_t direction) +{ +    boost::uint8_t enable = 0; +    double rate = _adcclock_freq; +    double full_scale; +    size_t dec = 0; +    size_t interpol = 0; +    filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; +    std::string name; +    boost::int16_t taps_array_rx[] = {55, 83, 0, -393, -580, 0, 1914, 4041, 5120, 4041, 1914, 0, -580, -393, 0, 83, 55}; +    boost::int16_t taps_array_tx[] = {36, -19, 0, -156, -12, 0, 479, 233, 0, -1215, -993, 0, 3569, 6277, 8192, 6277, 3569, 0, -993, -1215, 0, 223, 479, 0, -12, -156, 0, -19, 36}; +    std::vector<boost::int16_t> taps; + +    filter_info_base::sptr ret; + +    if(direction == RX) +    { +        full_scale = 16384; +        dec = 3; +        interpol = 1; + +        enable = _io_iface->peek8(0x003); +        enable = ((enable >> 4) & 0x03); +        taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); + +    } else { +        full_scale = 8192; +        dec = 1; +        interpol = 3; + +        boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); +        use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); +        if(use_dac_clk_div == 1) +        { +            rate = rate / 2; +        } + +        enable = _io_iface->peek8(0x002); +        enable = ((enable >> 4) & 0x03); +        if(enable == 2) //0 => int. by 1, 1 => int. by 2 (HB3), 2 => int. by 3 +        { +            rate /= 3; +        } + +        taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); +    } + +    ret = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 2) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); +    return  ret; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_3(direction_t direction) +{ +    boost::uint8_t enable = 0; +    double rate = _adcclock_freq; +    double full_scale = 0; +    size_t dec = 1; +    size_t interpol = 1; +    filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; +    boost::int16_t taps_array_rx[] = {1, 4, 6, 4, 1}; +    boost::int16_t taps_array_tx[] = {1, 2, 1}; +    std::vector<boost::int16_t> taps; + +    if(direction == RX) +    { +        full_scale = 16; +        dec = 2; + +        enable = _io_iface->peek8(0x003); +        enable = ((enable >> 4) & 0x03); +        taps.assign(taps_array_rx, taps_array_rx + sizeof(taps_array_rx) / sizeof(boost::int16_t) ); +    } else { +        full_scale = 2; +        interpol = 2; + +        boost::uint8_t use_dac_clk_div = _io_iface->peek8(0x00A); +        use_dac_clk_div = ((use_dac_clk_div >> 3) & 0x01); +        if(use_dac_clk_div == 1) +        { +            rate = rate / 2; +        } + +        enable = _io_iface->peek8(0x002); +        enable = ((enable >> 4) & 0x03); +        if(enable == 1) +        { +            rate /= 2; +        } +        taps.assign(taps_array_tx, taps_array_tx + sizeof(taps_array_tx) / sizeof(boost::int16_t) ); +    } + +    filter_info_base::sptr hb = filter_info_base::sptr(new digital_filter_base<boost::int16_t>(type, (enable != 1) ? true : false, 2, rate, interpol, dec, full_scale, taps.size(), taps)); +    return  hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_2(direction_t direction) +{ +    boost::uint8_t enable = 0; +    double rate = _adcclock_freq; +    double full_scale = 0; +    size_t dec = 1; +    size_t interpol = 1; +    filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; +    boost::int16_t taps_array[] = {-9, 0, 73, 128, 73, 0, -9}; +    std::vector<boost::int16_t> taps(taps_array, taps_array + sizeof(taps_array) / sizeof(boost::int16_t) ); + +    digital_filter_base<boost::int16_t>::sptr hb_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_3(direction)); +    digital_filter_base<boost::int16_t>::sptr dec_int_3 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_dec_int_3(direction)); + +    if(direction == RX) +    { +        full_scale = 256; +        dec = 2; +        enable = _io_iface->peek8(0x003); +    } else { +        full_scale = 128; +        interpol = 2; +        enable = _io_iface->peek8(0x002); +    } + +    enable = ((enable >> 3) & 0x01); + +    if(!(hb_3->is_bypassed())) +    { +        if(direction == RX) +        { +            rate = hb_3->get_output_rate(); +        }else if (direction == TX) { +            rate = hb_3->get_input_rate(); +            if(enable) +            { +                rate /= 2; +            } +        } +    } else { //else dec3/int3 or none of them is used. +        if(direction == RX) +        { +            rate = dec_int_3->get_output_rate(); +        }else if (direction == TX) { +            rate = dec_int_3->get_input_rate(); +            if(enable) +            { +                rate /= 2; +            } +        } +    } + +    filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 3, rate, interpol, dec, full_scale, taps.size(), taps)); +    return  hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_hb_1(direction_t direction) +{ +    boost::uint8_t enable = 0; +    double rate = 0; +    double full_scale = 0; +    size_t dec = 1; +    size_t interpol = 1; +    filter_info_base::filter_type type = filter_info_base::DIGITAL_I16; + +    std::vector<boost::int16_t> taps; +    boost::int16_t taps_rx_array[] = {-8, 0, 42, 0, -147, 0, 619, 1013, 619, 0, -147, 0, 42, 0, -8}; +    boost::int16_t taps_tx_array[] = {-53, 0, 313, 0, -1155, 0, 4989, 8192, 4989, 0, -1155, 0, 313, 0, -53}; + +    digital_filter_base<boost::int16_t>::sptr hb_2 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_2(direction)); + +    if(direction == RX) +    { +        full_scale = 2048; +        dec = 2; +        enable = _io_iface->peek8(0x003); +        enable = ((enable >> 2) & 0x01); +        rate = hb_2->get_output_rate(); +        taps.assign(taps_rx_array, taps_rx_array + sizeof(taps_rx_array) / sizeof(boost::int16_t) ); +    } else if (direction == TX) { +        full_scale = 8192; +        interpol = 2; +        enable = _io_iface->peek8(0x002); +        enable = ((enable >> 2) & 0x01); +        rate = hb_2->get_input_rate(); +        if(enable) +        { +            rate /= 2; +        } +        taps.assign(taps_tx_array, taps_tx_array + sizeof(taps_tx_array) / sizeof(boost::int16_t) ); +    } + +    filter_info_base::sptr hb(new digital_filter_base<boost::int16_t>(type, (enable == 0) ? true : false, 4, rate, interpol, dec, full_scale, taps.size(), taps)); +    return  hb; +} + +filter_info_base::sptr ad9361_device_t::_get_filter_fir(direction_t direction, chain_t chain) +{ +    double rate = 0; +    size_t dec = 1; +    size_t interpol = 1; +    size_t max_num_taps = 128; +    boost::uint8_t enable = 1; + +    digital_filter_base<boost::int16_t>::sptr hb_1 = boost::dynamic_pointer_cast<digital_filter_base<boost::int16_t> >(_get_filter_hb_1(direction)); + +    if(direction == RX) +    { +        dec = _get_fir_dec_int(direction); +        if(dec == 0) +        { +            enable = 0; +            dec = 1; +        } +        interpol = 1; +        rate = hb_1->get_output_rate(); +    }else if (direction == TX) { +        interpol = _get_fir_dec_int(direction); +        if(interpol == 0) +        { +            enable = 0; +            interpol = 1; +        } +        dec = 1; +        rate = hb_1->get_input_rate(); +        if(enable) +        { +            rate /= interpol; +        } +    } +    max_num_taps = _get_num_fir_taps(direction); + +    filter_info_base::sptr fir(new digital_filter_fir<boost::int16_t>(filter_info_base::DIGITAL_FIR_I16, (enable == 0) ? true : false, 5, rate, interpol, dec, 32767, max_num_taps, _get_fir_taps(direction, chain))); + +    return fir; +} + +void ad9361_device_t::_set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter) +{ +    digital_filter_fir<boost::int16_t>::sptr fir = boost::dynamic_pointer_cast<digital_filter_fir<boost::int16_t> >(filter); +    //only write taps. Ignore everything else for now +    _set_fir_taps(direction, channel, fir->get_taps()); +} + +/* + * If BW of one of the analog filters gets overwritten manually, + * _tx_analog_bw and _rx_analog_bw are not valid any more! + * For useful data in those variables set_bw_filter method should be used + */ +void ad9361_device_t::_set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter) +{ +    analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); +    double bw = lpf->get_cutoff(); +    if(direction == RX) +    { +        //remember: this function takes rf bw as its input and calibrated to 1.4 x the given value +        _rx_bb_lp_bw = _calibrate_baseband_rx_analog_filter(2 * bw / 1.4); //returns bb bw + +    } else { +        //remember: this function takes rf bw as its input and calibrates to 1.6 x the given value +        _tx_bb_lp_bw = _calibrate_baseband_tx_analog_filter(2 * bw / 1.6); +    } +} + +void ad9361_device_t::_set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter) +{ +    analog_filter_lp::sptr lpf = boost::dynamic_pointer_cast<analog_filter_lp>(filter); +    double bw = lpf->get_cutoff(); +    if(direction == RX) +    { +        //remember: this function takes rf bw as its input and calibrated to 2.5 x the given value +        _rx_tia_lp_bw = _calibrate_rx_TIAs(2 * bw / 2.5); //returns bb bw + +    } else { +        //remember: this function takes rf bw as its input and calibrates to 5 x the given value +        _tx_sec_lp_bw = _calibrate_secondary_tx_filter(2 * bw / 5); +    } +} +  }} diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index 71ce78da7..98369c2fc 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -1,5 +1,18 @@  // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 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_DEVICE_H @@ -8,6 +21,14 @@  #include <ad9361_client.h>  #include <boost/noncopyable.hpp>  #include <boost/thread/recursive_mutex.hpp> +#include <uhd/types/filters.hpp> +#include <uhd/types/sensors.hpp> +#include <complex> +#include <vector> +#include <map> +#include "boost/assign.hpp" +#include "boost/bind.hpp" +#include "boost/function.hpp"  namespace uhd { namespace usrp { @@ -15,10 +36,41 @@ class ad9361_device_t : public boost::noncopyable  {  public:      enum direction_t { RX, TX }; -    enum chain_t { CHAIN_1, CHAIN_2 }; +    enum gain_mode_t {GAIN_MODE_MANUAL, GAIN_MODE_SLOW_AGC, GAIN_MODE_FAST_AGC}; +    enum chain_t { CHAIN_1, CHAIN_2, CHAIN_BOTH };      ad9361_device_t(ad9361_params::sptr client, ad9361_io::sptr io_iface) : -        _client_params(client), _io_iface(io_iface) {} +        _client_params(client), _io_iface(io_iface) { + +        /* +         * This Boost.Assign to_container() workaround is necessary because STL containers +         * apparently confuse newer versions of MSVC. +         * +         * Source: http://www.boost.org/doc/libs/1_55_0/libs/assign/doc/#portability +         */ + +        _rx_filters = (boost::assign::map_list_of("LPF_TIA", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), +                                                    boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) +                                            ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), +                                                    boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) +                                            ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) +                                            ("DEC_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) +                                            ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) +                                            ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) +                                            ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), +                                                    boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3)))).to_container(_rx_filters); + +        _tx_filters = (boost::assign::map_list_of("LPF_SECONDARY", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_tia_sec, this, _1), +                                                    boost::bind(&ad9361_device_t::_set_filter_lp_tia_sec, this, _1, _3))) +                                            ("LPF_BB", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_lp_bb, this, _1), +                                                    boost::bind(&ad9361_device_t::_set_filter_lp_bb, this, _1, _3))) +                                            ("HB_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_3, this, _1), 0)) +                                            ("INT_3", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_dec_int_3, this, _1), 0)) +                                            ("HB_2", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_2, this, _1), 0)) +                                            ("HB_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_hb_1, this, _1), 0)) +                                            ("FIR_1", filter_query_helper(boost::bind(&ad9361_device_t::_get_filter_fir, this, _1, _2), +                                                    boost::bind(&ad9361_device_t::_set_filter_fir, this, _1, _2, _3)))).to_container(_tx_filters); +    }      /* Initialize the AD9361 codec. */      void initialize(); @@ -69,21 +121,56 @@ public:      /* Read back the internal RSSI measurement data. */      double get_rssi(chain_t chain); +    /*! Read the internal temperature sensor +     *\param calibrate return raw sensor readings or apply calibration factor. +     *\param num_samples number of measurements to average over +     */ +    double get_average_temperature(const double cal_offset = -30.0, const size_t num_samples = 3); + +    /* Turn on/off AD9361's RX DC offset correction */ +    void set_dc_offset_auto(direction_t direction, const bool on); + +    /* Turn on/off AD9361's RX IQ imbalance correction */ +    void set_iq_balance_auto(direction_t direction, const bool on); + +    /* Configure AD9361's AGC module to use either fast or slow AGC mode. */ +    void set_agc_mode(chain_t chain, gain_mode_t gain_mode); + +    /* Enable AD9361's AGC gain mode. */ +    void set_agc(chain_t chain, bool enable); + +    /* Set bandwidth of AD9361's analog LP filters. +     * Bandwidth should be RF bandwidth */ +    double set_bw_filter(direction_t direction, const double rf_bw); + +    /* +     * Filter API implementation +     * */ +    filter_info_base::sptr get_filter(direction_t direction, chain_t chain, const std::string &name); + +    void set_filter(direction_t direction, chain_t chain, const std::string &name, filter_info_base::sptr filter); + +    std::vector<std::string> get_filter_names(direction_t direction); +      //Constants      static const double AD9361_MAX_GAIN;      static const double AD9361_MAX_CLOCK_RATE; +    static const double AD9361_CAL_VALID_WINDOW;      static const double AD9361_RECOMMENDED_MAX_BANDWIDTH;  private:    //Methods      void _program_fir_filter(direction_t direction, int num_taps, boost::uint16_t *coeffs);      void _setup_tx_fir(size_t num_taps, boost::int32_t interpolation);      void _setup_rx_fir(size_t num_taps, boost::int32_t decimation); +    void _program_fir_filter(direction_t direction, chain_t chain, int num_taps, boost::uint16_t *coeffs); +    void _setup_tx_fir(size_t num_taps); +    void _setup_rx_fir(size_t num_taps);      void _calibrate_lock_bbpll();      void _calibrate_synth_charge_pumps(); -    double _calibrate_baseband_rx_analog_filter(); -    double _calibrate_baseband_tx_analog_filter(); -    void _calibrate_secondary_tx_filter(); -    void _calibrate_rx_TIAs(); +    double _calibrate_baseband_rx_analog_filter(double rfbw); +    double _calibrate_baseband_tx_analog_filter(double rfbw); +    double _calibrate_secondary_tx_filter(double rfbw); +    double _calibrate_rx_TIAs(double rfbw);      void _setup_adc();      void _calibrate_baseband_dc_offset();      void _calibrate_rf_dc_offset(); @@ -92,12 +179,29 @@ private:    //Methods      void _calibrate_tx_quadrature();      void _program_mixer_gm_subtable();      void _program_gain_table(); -    void _setup_gain_control(); +    void _setup_gain_control(bool use_agc);      void _setup_synth(direction_t direction, double vcorate);      double _tune_bbvco(const double rate);      void _reprogram_gains();      double _tune_helper(direction_t direction, const double value);      double _setup_rates(const double rate); +    double _get_temperature(const double cal_offset, const double timeout = 0.1); +    void _configure_bb_rf_dc_tracking(const bool on); +    void _setup_agc(chain_t chain, gain_mode_t gain_mode); +    void _set_fir_taps(direction_t direction, chain_t chain, const std::vector<boost::int16_t>& taps); +    std::vector<boost::int16_t> _get_fir_taps(direction_t direction, chain_t chain); +    size_t _get_num_fir_taps(direction_t direction); +    size_t _get_fir_dec_int(direction_t direction); +    filter_info_base::sptr _get_filter_lp_tia_sec(direction_t direction); +    filter_info_base::sptr _get_filter_lp_bb(direction_t direction); +    filter_info_base::sptr _get_filter_dec_int_3(direction_t direction); +    filter_info_base::sptr _get_filter_hb_3(direction_t direction); +    filter_info_base::sptr _get_filter_hb_2(direction_t direction); +    filter_info_base::sptr _get_filter_hb_1(direction_t direction); +    filter_info_base::sptr _get_filter_fir(direction_t direction, chain_t chain); +    void _set_filter_fir(direction_t direction, chain_t channel, filter_info_base::sptr filter); +    void _set_filter_lp_bb(direction_t direction, filter_info_base::sptr filter); +    void _set_filter_lp_tia_sec(direction_t direction, filter_info_base::sptr filter);  private:    //Members      typedef struct { @@ -110,11 +214,30 @@ private:    //Members          boost::uint8_t bbftune_mode;      } chip_regs_t; +    struct filter_query_helper +    { +        filter_query_helper( +                boost::function<filter_info_base::sptr (direction_t, chain_t)> p_get, +                boost::function<void (direction_t, chain_t, filter_info_base::sptr)> p_set +                ) : get(p_get), set(p_set) {  } + +        filter_query_helper(){ } + +        boost::function<filter_info_base::sptr (direction_t, chain_t)> get; +        boost::function<void (direction_t, chain_t, filter_info_base::sptr)> set; +    }; + +    std::map<std::string, filter_query_helper> _rx_filters; +    std::map<std::string, filter_query_helper> _tx_filters; +      //Interfaces      ad9361_params::sptr _client_params;      ad9361_io::sptr     _io_iface;      //Intermediate state      double              _rx_freq, _tx_freq, _req_rx_freq, _req_tx_freq; +    double              _last_calibration_freq; +    double              _rx_analog_bw, _tx_analog_bw, _rx_bb_lp_bw, _tx_bb_lp_bw; +    double              _rx_tia_lp_bw, _tx_sec_lp_bw;      //! Current baseband sampling rate (this is the actual rate the device is      //  is running at)      double              _baseband_bw; @@ -129,10 +252,14 @@ private:    //Members      double              _rx1_gain, _rx2_gain, _tx1_gain, _tx2_gain;      boost::int32_t      _tfir_factor;      boost::int32_t      _rfir_factor; +    gain_mode_t         _rx1_agc_mode, _rx2_agc_mode; +    bool                _rx1_agc_enable, _rx2_agc_enable;      //Register soft-copies      chip_regs_t         _regs;      //Synchronization      boost::recursive_mutex  _mutex; +    bool _use_dc_offset_correction; +    bool _use_iq_balance_correction;  };  }}  //namespace diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h b/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h index a1a85a49a..97ff858fd 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_filter_taps.h @@ -1,5 +1,18 @@  // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 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_FILTER_TAPS_HPP diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h index 786029d6e..8cd958e23 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_gain_tables.h @@ -1,5 +1,18 @@  // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 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_GAIN_TABLES_HPP @@ -7,91 +20,91 @@  #include <boost/cstdint.hpp> -boost::uint8_t gain_table_sub_1300mhz[77][5] = { {0,0x00,0x00,0x20,1}, -    {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, -    {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, -    {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, -    {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, -    {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, -    {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, -    {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, -    {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, -    {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, -    {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x27,0x20,1}, -    {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, -    {34,0x04,0x2B,0x00,1}, {35,0x24,0x21,0x20,0}, {36,0x24,0x22,0x00,1}, -    {37,0x44,0x20,0x20,0}, {38,0x44,0x21,0x00,0}, {39,0x44,0x22,0x00,0}, -    {40,0x44,0x23,0x00,0}, {41,0x44,0x24,0x00,0}, {42,0x44,0x25,0x00,0}, -    {43,0x44,0x26,0x00,0}, {44,0x44,0x27,0x00,0}, {45,0x44,0x28,0x00,0}, -    {46,0x44,0x29,0x00,0}, {47,0x44,0x2A,0x00,0}, {48,0x44,0x2B,0x00,0}, -    {49,0x44,0x2C,0x00,0}, {50,0x44,0x2D,0x00,0}, {51,0x44,0x2E,0x00,0}, -    {52,0x44,0x2F,0x00,0}, {53,0x44,0x30,0x00,0}, {54,0x44,0x31,0x00,0}, -    {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, -    {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, -    {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, -    {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, -    {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, -    {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, -    {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, -    {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_sub_1300mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x28, 0x20 }, { 0x04, 0x29, 0x00 }, { 0x04, 0x2A, 0x00 }, +{ 0x04, 0x2B, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, { 0x44, 0x22, 0x00 }, +{ 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, { 0x44, 0x25, 0x00 }, +{ 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, { 0x44, 0x28, 0x00 }, +{ 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, { 0x44, 0x2B, 0x00 }, +{ 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, { 0x44, 0x2E, 0x00 }, +{ 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, { 0x44, 0x31, 0x00 }, +{ 0x44, 0x32, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_1300mhz_to_4000mhz[77][5] = {   {0,0x00,0x00,0x20,1}, -    {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x01,0x00,0}, -    {4,0x00,0x02,0x00,0}, {5,0x00,0x03,0x00,0}, {6,0x00,0x04,0x00,0}, -    {7,0x00,0x05,0x00,0}, {8,0x01,0x03,0x20,1}, {9,0x01,0x04,0x00,0}, -    {10,0x01,0x05,0x00,0}, {11,0x01,0x06,0x00,0}, {12,0x01,0x07,0x00,0}, -    {13,0x01,0x08,0x00,0}, {14,0x01,0x09,0x00,0}, {15,0x01,0x0A,0x00,0}, -    {16,0x01,0x0B,0x00,0}, {17,0x01,0x0C,0x00,0}, {18,0x01,0x0D,0x00,0}, -    {19,0x01,0x0E,0x00,0}, {20,0x02,0x09,0x20,1}, {21,0x02,0x0A,0x00,0}, -    {22,0x02,0x0B,0x00,0}, {23,0x02,0x0C,0x00,0}, {24,0x02,0x0D,0x00,0}, -    {25,0x02,0x0E,0x00,0}, {26,0x02,0x0F,0x00,0}, {27,0x02,0x10,0x00,0}, -    {28,0x02,0x2B,0x20,1}, {29,0x02,0x2C,0x00,0}, {30,0x04,0x28,0x20,1}, -    {31,0x04,0x29,0x00,0}, {32,0x04,0x2A,0x00,0}, {33,0x04,0x2B,0x00,0}, -    {34,0x24,0x20,0x20,0}, {35,0x24,0x21,0x00,1}, {36,0x44,0x20,0x20,0}, -    {37,0x44,0x21,0x00,1}, {38,0x44,0x22,0x00,0}, {39,0x44,0x23,0x00,0}, -    {40,0x44,0x24,0x00,0}, {41,0x44,0x25,0x00,0}, {42,0x44,0x26,0x00,0}, -    {43,0x44,0x27,0x00,0}, {44,0x44,0x28,0x00,0}, {45,0x44,0x29,0x00,0}, -    {46,0x44,0x2A,0x00,0}, {47,0x44,0x2B,0x00,0}, {48,0x44,0x2C,0x00,0}, -    {49,0x44,0x2D,0x00,0}, {50,0x44,0x2E,0x00,0}, {51,0x44,0x2F,0x00,0}, -    {52,0x44,0x30,0x00,0}, {53,0x44,0x31,0x00,0}, {54,0x44,0x32,0x00,0}, -    {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, -    {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, -    {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, -    {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, -    {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, -    {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, -    {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, -    {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_1300mhz_to_4000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, +{ 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, +{ 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, +{ 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, +{ 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, +{ 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, +{ 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, +{ 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, +{ 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x24, 0x21, 0x20 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, +{ 0x44, 0x31, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } }; -boost::uint8_t gain_table_4000mhz_to_6000mhz[77][5] = { {0,0x00,0x00,0x20,1}, -    {1,0x00,0x00,0x00,0}, {2,0x00,0x00,0x00,0}, {3,0x00,0x00,0x00,0}, -    {4,0x00,0x00,0x00,0}, {5,0x00,0x01,0x00,0}, {6,0x00,0x02,0x00,0}, -    {7,0x00,0x03,0x00,0}, {8,0x01,0x01,0x20,1}, {9,0x01,0x02,0x00,0}, -    {10,0x01,0x03,0x00,0}, {11,0x01,0x04,0x20,1}, {12,0x01,0x05,0x00,0}, -    {13,0x01,0x06,0x00,0}, {14,0x01,0x07,0x00,0}, {15,0x01,0x08,0x00,0}, -    {16,0x01,0x09,0x00,0}, {17,0x01,0x0A,0x00,0}, {18,0x01,0x0B,0x00,0}, -    {19,0x01,0x0C,0x00,0}, {20,0x02,0x08,0x20,1}, {21,0x02,0x09,0x00,0}, -    {22,0x02,0x0A,0x00,0}, {23,0x02,0x0B,0x20,1}, {24,0x02,0x0C,0x00,0}, -    {25,0x02,0x0D,0x00,0}, {26,0x02,0x0E,0x00,0}, {27,0x02,0x0F,0x00,0}, -    {28,0x02,0x2A,0x20,1}, {29,0x02,0x2B,0x00,0}, {30,0x04,0x27,0x20,1}, -    {31,0x04,0x28,0x00,0}, {32,0x04,0x29,0x00,0}, {33,0x04,0x2A,0x00,0}, -    {34,0x04,0x2B,0x00,0}, {35,0x04,0x2C,0x00,0}, {36,0x04,0x2D,0x00,0}, -    {37,0x24,0x20,0x20,1}, {38,0x24,0x21,0x00,0}, {39,0x24,0x22,0x00,0}, -    {40,0x44,0x20,0x20,1}, {41,0x44,0x21,0x00,0}, {42,0x44,0x22,0x00,0}, -    {43,0x44,0x23,0x00,0}, {44,0x44,0x24,0x00,0}, {45,0x44,0x25,0x00,0}, -    {46,0x44,0x26,0x00,0}, {47,0x44,0x27,0x00,0}, {48,0x44,0x28,0x00,0}, -    {49,0x44,0x29,0x00,0}, {50,0x44,0x2A,0x00,0}, {51,0x44,0x2B,0x00,0}, -    {52,0x44,0x2C,0x00,0}, {53,0x44,0x2D,0x00,0}, {54,0x44,0x2E,0x00,0}, -    {55,0x64,0x2E,0x20,1}, {56,0x64,0x2F,0x00,0}, {57,0x64,0x30,0x00,0}, -    {58,0x64,0x31,0x00,0}, {59,0x64,0x32,0x00,0}, {60,0x64,0x33,0x00,0}, -    {61,0x64,0x34,0x00,0}, {62,0x64,0x35,0x00,0}, {63,0x64,0x36,0x00,0}, -    {64,0x64,0x37,0x00,0}, {65,0x64,0x38,0x00,0}, {66,0x65,0x38,0x20,1}, -    {67,0x66,0x38,0x20,1}, {68,0x67,0x38,0x20,1}, {69,0x68,0x38,0x20,1}, -    {70,0x69,0x38,0x20,1}, {71,0x6A,0x38,0x20,1}, {72,0x6B,0x38,0x20,1}, -    {73,0x6C,0x38,0x20,1}, {74,0x6D,0x38,0x20,1}, {75,0x6E,0x38,0x20,1}, -    {76,0x6F,0x38,0x20,1}}; +boost::uint8_t gain_table_4000mhz_to_6000mhz[77][3] = { +{ 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, +{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x01, 0x00 }, +{ 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, { 0x01, 0x01, 0x20 }, +{ 0x01, 0x02, 0x00 }, { 0x01, 0x03, 0x00 }, { 0x01, 0x04, 0x20 }, +{ 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, { 0x01, 0x07, 0x00 }, +{ 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, { 0x01, 0x0A, 0x00 }, +{ 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, { 0x02, 0x08, 0x20 }, +{ 0x02, 0x09, 0x00 }, { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x20 }, +{ 0x02, 0x0C, 0x00 }, { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, +{ 0x02, 0x0F, 0x00 }, { 0x02, 0x2A, 0x20 }, { 0x02, 0x2B, 0x00 }, +{ 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, +{ 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x04, 0x2C, 0x00 }, +{ 0x04, 0x2D, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, +{ 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, +{ 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, +{ 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, +{ 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, +{ 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, +{ 0x44, 0x2E, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, +{ 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, +{ 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, +{ 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, +{ 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, +{ 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, +{ 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, +{ 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } };  #endif /* INCLUDED_AD9361_GAIN_TABLES_HPP */ diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h index cb320e1f4..8155aae7c 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_synth_lut.h @@ -1,25 +1,38 @@  // -// Copyright 2014 Ettus Research LLC +// Copyright 2014 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_SYNTH_LUT_HPP  #define INCLUDED_AD9361_SYNTH_LUT_HPP -double vco_index[53] = {12605000000, 12245000000, 11906000000, 11588000000, -                        11288000000, 11007000000, 10742000000, 10492000000, -                        10258000000, 10036000000, 9827800000, 9631100000, -                        9445300000, 9269800000, 9103600000, 8946300000, -                        8797000000, 8655300000, 8520600000, 8392300000, -                        8269900000, 8153100000, 8041400000, 7934400000, -                        7831800000, 7733200000, 7638400000, 7547100000, -                        7459000000, 7374000000, 7291900000, 7212400000, -                        7135500000, 7061000000, 6988700000, 6918600000, -                        6850600000, 6784600000, 6720500000, 6658200000, -                        6597800000, 6539200000, 6482300000, 6427000000, -                        6373400000, 6321400000, 6270900000, 6222000000, -                        6174500000, 6128400000, 6083600000, 6040100000, -                        5997700000}; +double vco_index[53] = {12605000000.0, 12245000000.0, 11906000000.0, 11588000000.0, +                        11288000000.0, 11007000000.0, 10742000000.0, 10492000000.0, +                        10258000000.0, 10036000000.0, 9827800000.0, 9631100000.0, +                        9445300000.0, 9269800000.0, 9103600000.0, 8946300000.0, +                        8797000000.0, 8655300000.0, 8520600000.0, 8392300000.0, +                        8269900000.0, 8153100000.0, 8041400000.0, 7934400000.0, +                        7831800000.0, 7733200000.0, 7638400000.0, 7547100000.0, +                        7459000000.0, 7374000000.0, 7291900000.0, 7212400000.0, +                        7135500000.0, 7061000000.0, 6988700000.0, 6918600000.0, +                        6850600000.0, 6784600000.0, 6720500000.0, 6658200000.0, +                        6597800000.0, 6539200000.0, 6482300000.0, 6427000000.0, +                        6373400000.0, 6321400000.0, 6270900000.0, 6222000000.0, +                        6174500000.0, 6128400000.0, 6083600000.0, 6040100000.0, +                        5997700000.0};  int synth_cal_lut[53][12] = {   {10, 0, 4, 0, 15, 8, 8, 13, 4, 13, 15, 9},                                  {10, 0, 4, 0, 15, 8, 9, 13, 4, 13, 15, 9}, 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/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index 164437f40..e22834fd9 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -1,5 +1,5 @@  // -// Copyright 2011,2014 Ettus Research LLC +// Copyright 2011,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 @@ -20,10 +20,34 @@  #include <uhd/config.hpp>  #include <uhd/usrp/dboard_iface.hpp> +#include <boost/assign.hpp>  #include <boost/cstdint.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp>  #include <uhd/types/wb_iface.hpp> +#include <map> + +typedef enum { +    GPIO_CTRL, +    GPIO_DDR, +    GPIO_OUT, +    GPIO_ATR_0X, +    GPIO_ATR_RX, +    GPIO_ATR_TX, +    GPIO_ATR_XX +} gpio_attr_t; + +typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t; +static const gpio_attr_map_t gpio_attr_map = +    boost::assign::map_list_of +        (GPIO_CTRL,   "CTRL") +        (GPIO_DDR,    "DDR") +        (GPIO_OUT,    "OUT") +        (GPIO_ATR_0X, "ATR_0X") +        (GPIO_ATR_RX, "ATR_RX") +        (GPIO_ATR_TX, "ATR_TX") +        (GPIO_ATR_XX, "ATR_XX") +;  class gpio_core_200 : boost::noncopyable{  public: diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index 6a36e8fa1..b899085c0 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -295,6 +295,7 @@ public:          _iface->poke32(REG_RX_CTRL_FORMAT, format_word);      } +  private:      wb_iface::sptr _iface;      const size_t _dsp_base, _ctrl_base; diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 8a131ffb4..18dabade0 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -46,6 +46,9 @@ template <class T> T ceil_log2(T num){  using namespace uhd; +const double rx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0; +const double rx_dsp_core_3000::DEFAULT_RATE = 1e6; +  rx_dsp_core_3000::~rx_dsp_core_3000(void){      /* NOP */  } @@ -263,6 +266,24 @@ public:          this->update_scalar();      } +    void populate_subtree(property_tree::sptr subtree) +    { +        subtree->create<meta_range_t>("rate/range") +            .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, this)) +        ; +        subtree->create<double>("rate/value") +            .set(DEFAULT_RATE) +            .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1)) +        ; +        subtree->create<double>("freq/value") +            .set(DEFAULT_CORDIC_FREQ) +            .coerce(boost::bind(&rx_dsp_core_3000::set_freq, this, _1)) +        ; +        subtree->create<meta_range_t>("freq/range") +            .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, this)) +        ; +    } +  private:      wb_iface::sptr _iface;      const size_t _dsp_base; diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp index 89059e953..65801de1d 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp @@ -21,14 +21,18 @@  #include <uhd/config.hpp>  #include <uhd/stream.hpp>  #include <uhd/types/ranges.hpp> -#include <boost/utility.hpp> -#include <boost/shared_ptr.hpp>  #include <uhd/types/stream_cmd.hpp>  #include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp>  #include <string>  class rx_dsp_core_3000 : boost::noncopyable{  public: +    static const double DEFAULT_CORDIC_FREQ; +    static const double DEFAULT_RATE; +      typedef boost::shared_ptr<rx_dsp_core_3000> sptr;      virtual ~rx_dsp_core_3000(void) = 0; @@ -56,6 +60,8 @@ public:      virtual double set_freq(const double freq) = 0;      virtual void setup(const uhd::stream_args_t &stream_args) = 0; + +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0;  };  #endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp index b73896b57..7ac920553 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -17,6 +17,7 @@  #include "rx_frontend_core_200.hpp"  #include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp>  using namespace uhd; @@ -38,6 +39,10 @@ rx_frontend_core_200::~rx_frontend_core_200(void){      /* NOP */  } +const std::complex<double> rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0); +const bool rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE = true; +const std::complex<double> rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0); +  class rx_frontend_core_200_impl : public rx_frontend_core_200{  public:      rx_frontend_core_200_impl(wb_iface::sptr iface, const size_t base): @@ -74,6 +79,22 @@ public:          _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18));      } +    void populate_subtree(uhd::property_tree::sptr subtree) +    { +        subtree->create<std::complex<double> >("dc_offset/value") +            .set(DEFAULT_DC_OFFSET_VALUE) +            .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1)) +        ; +        subtree->create<bool>("dc_offset/enable") +            .set(DEFAULT_DC_OFFSET_ENABLE) +            .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1)) +        ; +        subtree->create<std::complex<double> >("iq_balance/value") +            .set(DEFAULT_IQ_BALANCE_VALUE) +            .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1)) +        ; +    } +  private:      boost::int32_t _i_dc_off, _q_dc_off;      wb_iface::sptr _iface; diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp index 9b18e2089..32ce77e00 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp @@ -19,14 +19,19 @@  #define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP  #include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp>  #include <complex>  #include <string>  class rx_frontend_core_200 : boost::noncopyable{  public: +    static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; +    static const bool DEFAULT_DC_OFFSET_ENABLE; +    static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; +      typedef boost::shared_ptr<rx_frontend_core_200> sptr;      virtual ~rx_frontend_core_200(void) = 0; @@ -41,6 +46,8 @@ public:      virtual void set_iq_balance(const std::complex<double> &cor) = 0; +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; +  };  #endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp index 736205402..93b70435f 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -37,6 +37,9 @@ template <class T> T ceil_log2(T num){  using namespace uhd; +const double tx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0; +const double tx_dsp_core_3000::DEFAULT_RATE = 1e6; +  tx_dsp_core_3000::~tx_dsp_core_3000(void){      /* NOP */  } @@ -200,6 +203,24 @@ public:          this->update_scalar();      } +    void populate_subtree(property_tree::sptr subtree) +    { +        subtree->create<meta_range_t>("rate/range") +            .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, this)) +        ; +        subtree->create<double>("rate/value") +            .set(DEFAULT_RATE) +            .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1)) +        ; +        subtree->create<double>("freq/value") +            .set(DEFAULT_CORDIC_FREQ) +            .coerce(boost::bind(&tx_dsp_core_3000::set_freq, this, _1)) +        ; +        subtree->create<meta_range_t>("freq/range") +            .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, this)) +        ; +    } +  private:      wb_iface::sptr _iface;      const size_t _dsp_base; diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/usrp/cores/tx_dsp_core_3000.hpp index a51cb2803..fbc43add6 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.hpp @@ -21,12 +21,16 @@  #include <uhd/config.hpp>  #include <uhd/stream.hpp>  #include <uhd/types/ranges.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp>  class tx_dsp_core_3000 : boost::noncopyable{  public: +    static const double DEFAULT_CORDIC_FREQ; +    static const double DEFAULT_RATE; +      typedef boost::shared_ptr<tx_dsp_core_3000> sptr;      virtual ~tx_dsp_core_3000(void) = 0; @@ -51,6 +55,8 @@ public:      virtual double set_freq(const double freq) = 0;      virtual void setup(const uhd::stream_args_t &stream_args) = 0; + +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0;  };  #endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp index 7000f46bd..0fa028571 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -20,6 +20,7 @@  #include <uhd/exception.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/math/special_functions/round.hpp> +#include <boost/bind.hpp>  using namespace uhd; @@ -29,6 +30,9 @@ using namespace uhd;  #define REG_TX_FE_PHASE_CORRECTION    _base + 12 //18 bits  #define REG_TX_FE_MUX                 _base + 16 //8 bits (std output = 0x10, reversed = 0x01) +const std::complex<double> tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0); +const std::complex<double> tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0); +  static boost::uint32_t fs_to_bits(const double num, const size_t bits){      return boost::int32_t(boost::math::round(num * (1 << (bits-1))));  } @@ -71,6 +75,18 @@ public:          _iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18));      } +    void populate_subtree(uhd::property_tree::sptr subtree) +    { +        subtree->create< std::complex<double> >("dc_offset/value") +            .set(DEFAULT_DC_OFFSET_VALUE) +            .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1)) +        ; +        subtree->create< std::complex<double> >("iq_balance/value") +            .set(DEFAULT_IQ_BALANCE_VALUE) +            .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1)) +        ; +    } +  private:      wb_iface::sptr _iface;      const size_t _base; diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp index 0b89ea818..912256329 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp @@ -19,9 +19,10 @@  #define INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP  #include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp> -#include <uhd/types/wb_iface.hpp>  #include <complex>  #include <string> @@ -29,6 +30,9 @@ class tx_frontend_core_200 : boost::noncopyable{  public:      typedef boost::shared_ptr<tx_frontend_core_200> sptr; +    static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; +    static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; +      virtual ~tx_frontend_core_200(void) = 0;      static sptr make(uhd::wb_iface::sptr iface, const size_t base); @@ -39,6 +43,8 @@ public:      virtual void set_iq_balance(const std::complex<double> &cor) = 0; +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; +  };  #endif /* INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp index 8336117b8..daf9a8dfd 100644 --- a/host/lib/usrp/dboard/db_cbx.cpp +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -38,7 +38,7 @@ sbx_xcvr::cbx::~cbx(void){      /* NOP */  } -void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s) +void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s)  {      BOOST_FOREACH(boost::uint32_t reg, regs)      { diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index a08d22537..4800bbd83 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -1,5 +1,5 @@  // -// Copyright 2011-2014 Ettus Research LLC +// Copyright 2011-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 @@ -225,7 +225,7 @@ protected:          /*! This is the registered instance of the wrapper class, sbx_base. */          sbx_xcvr *self_base;      private: -        void write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s); +        void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s);          max287x_iface::sptr _txlo;          max287x_iface::sptr _rxlo;      }; diff --git a/host/lib/usrp/dboard_eeprom.cpp b/host/lib/usrp/dboard_eeprom.cpp index f2bee47a9..3b56ae19a 100644 --- a/host/lib/usrp/dboard_eeprom.cpp +++ b/host/lib/usrp/dboard_eeprom.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2011,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 @@ -15,6 +15,7 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // +#include <uhd/types/byte_vector.hpp>  #include <uhd/usrp/dboard_eeprom.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/log.hpp> @@ -27,30 +28,6 @@  using namespace uhd;  using namespace uhd::usrp; -/*********************************************************************** - * Utility functions - **********************************************************************/ - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ -    std::string out; -    BOOST_FOREACH(boost::uint8_t byte, bytes){ -        if (byte < 32 or byte > 127) return out; -        out += byte; -    } -    return out; -} - -//! create a byte vector from a string, null terminate unless max length -static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){ -    byte_vector_t bytes; -    for (size_t i = 0; i < std::min(string.size(), max_length); i++){ -        bytes.push_back(string[i]); -    } -    if (bytes.size() < max_length - 1) bytes.push_back('\0'); -    return bytes; -} -  ////////////////////////////////////////////////////////////////////////  // format of daughterboard EEPROM  // 00: 0xDB code for ``I'm a daughterboard'' diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index ac419e0e0..6d3c08534 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2012,2014 Ettus Research LLC +// Copyright 2010-2012,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 @@ -28,6 +28,7 @@  #include <boost/functional/hash.hpp>  #include <boost/assign/list_of.hpp>  #include <fstream> +#include <iostream>  #include <ctime>  using namespace uhd; @@ -45,7 +46,7 @@ namespace fs = boost::filesystem;  /***********************************************************************   * Discovery   **********************************************************************/ -static device_addrs_t e100_find(const device_addr_t &hint){ +device_addrs_t e100_find(const device_addr_t &hint){      device_addrs_t e100_addrs;      //return an empty list of addresses when type is set to non-usrp-e @@ -104,17 +105,10 @@ static const uhd::dict<std::string, std::string> model_to_fpga_file_name = boost      ("E110", "usrp_e110_fpga.bin")  ; -/*********************************************************************** - * Structors - **********************************************************************/ -e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ -    _tree = property_tree::make(); -    _type = device::USRP; -    _ignore_cal_file = device_addr.has_key("ignore-cal-file"); - +std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr){      //read the eeprom so we can determine the hardware -    _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); -    const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); +    uhd::i2c_iface::sptr dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); +    const mboard_eeprom_t mb_eeprom(*dev_i2c_iface, E100_EEPROM_MAP_KEY);      //determine the model string for this device      const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); @@ -126,7 +120,21 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      ) % model));      //extract the fpga path and compute hash -    const std::string default_fpga_file_name = model_to_fpga_file_name[model]; +    return model_to_fpga_file_name[model]; +} + +/*********************************************************************** + * Structors + **********************************************************************/ +e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ +    _tree = property_tree::make(); +    _type = device::USRP; +    _ignore_cal_file = device_addr.has_key("ignore-cal-file"); + +    _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); +    const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); +    const std::string default_fpga_file_name = get_default_e1x0_fpga_image(device_addr); +    const std::string model = device_addr["model"];      std::string e100_fpga_image;      try{          e100_fpga_image = find_image_path(device_addr.get("fpga", default_fpga_file_name)); diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 4efc21427..d00668224 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -29,6 +29,7 @@  #include "recv_packet_demuxer.hpp"  #include <uhd/device.hpp>  #include <uhd/property_tree.hpp> +#include <uhd/types/device_addr.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <uhd/usrp/dboard_eeprom.hpp>  #include <uhd/usrp/mboard_eeprom.hpp> @@ -68,6 +69,9 @@ uhd::usrp::dboard_iface::sptr make_e100_dboard_iface(      e100_codec_ctrl::sptr codec  ); +uhd::device_addrs_t e100_find(const uhd::device_addr_t &hint); +std::string get_default_e1x0_fpga_image(const uhd::device_addr_t &device_addr); +  /*!   * USRP-E100 implementation guts:   * The implementation details are encapsulated here. diff --git a/host/lib/usrp/e100/fpga_downloader.cpp b/host/lib/usrp/e100/fpga_downloader.cpp index c9d77f560..9abde32f7 100644 --- a/host/lib/usrp/e100/fpga_downloader.cpp +++ b/host/lib/usrp/e100/fpga_downloader.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2011,2014 Ettus Research LLC +// Copyright 2010-2011,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 @@ -17,8 +17,16 @@  #include <uhd/config.hpp>  #ifdef UHD_DLL_EXPORTS +#include <boost/filesystem.hpp> +#include <boost/format.hpp>  #include <uhd/exception.hpp> +#include <uhd/device.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/types/device_addr.hpp>  #include <uhd/utils/msg.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include "e100_impl.hpp"  #else //special case when this file is externally included  #include <stdexcept>  #include <iostream> @@ -270,3 +278,34 @@ void e100_load_fpga(const std::string &bin_file){  } +#ifdef UHD_DLL_EXPORTS +namespace fs = boost::filesystem; + +static bool e100_image_loader(const uhd::image_loader::image_loader_args_t &image_loader_args){ +    // Make sure this is an E1x0 +    uhd::device_addrs_t devs = e100_find(uhd::device_addr_t()); +    if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + +    std::string fpga_filename; +    if(image_loader_args.fpga_path == ""){ +        fpga_filename = uhd::find_image_path(get_default_e1x0_fpga_image(devs[0])); +    } +    else{ +        if(not fs::exists(image_loader_args.fpga_path)){ +            throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.") +                                         % image_loader_args.fpga_path)); +        } +        else fpga_filename = image_loader_args.fpga_path; +    } + +    e100_load_fpga(fpga_filename); +    return true; +} + +UHD_STATIC_BLOCK(register_e100_image_loader){ +    std::string recovery_instructions = "The default FPGA image will be loaded the next time " +                                        "UHD uses this device."; + +    uhd::image_loader::register_image_loader("e100", e100_image_loader, recovery_instructions); +} +#endif /* UHD_DLL_EXPORTS */ diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt index 9ee9b5521..26e34294a 100644 --- a/host/lib/usrp/e300/CMakeLists.txt +++ b/host/lib/usrp/e300/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2013-2014 Ettus Research LLC +# Copyright 2013-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 @@ -39,8 +39,6 @@ IF(ENABLE_E300)          ${CMAKE_CURRENT_SOURCE_DIR}/e300_i2c.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/e300_eeprom_manager.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/e300_common.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/e300_async_serial.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/e300_ublox_control_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/e300_remote_codec_ctrl.cpp      )      LIBUHD_APPEND_SOURCES(${E300_SOURCES}) @@ -52,4 +50,12 @@ IF(ENABLE_E300)              PROPERTIES COMPILE_DEFINITIONS "E300_NATIVE=1"          )      ENDIF(UDEV_FOUND) + +    IF(ENABLE_GPSD) +        SET_SOURCE_FILES_PROPERTIES( +            ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp +            ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.hpp +            PROPERTIES COMPILE_DEFINITIONS "E300_GPSD=1" +        ) +    ENDIF(ENABLE_GPSD)  ENDIF(ENABLE_E300) diff --git a/host/lib/usrp/e300/e300_async_serial.cpp b/host/lib/usrp/e300/e300_async_serial.cpp deleted file mode 100644 index cdf18f7f7..000000000 --- a/host/lib/usrp/e300/e300_async_serial.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// -// Copyright 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 -// 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 "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -async_serial::async_serial() -    : _io(), -      _port(_io), -      _background_thread(), -      _open(false), -      _error(false) -{ -} - -async_serial::async_serial( -        const std::string &node, -        const size_t baud_rate, -        boost::asio::serial_port_base::parity opt_parity, -        boost::asio::serial_port_base::character_size opt_csize, -        boost::asio::serial_port_base::flow_control opt_flow, -        boost::asio::serial_port_base::stop_bits opt_stop) -    : _io(), -      _port(_io), -      _background_thread(), -      _open(false), -      _error(false) -{ -    open(node, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop); -} - -void async_serial::open( -        const std::string &node, -        const size_t baud_rate, -        boost::asio::serial_port_base::parity opt_parity, -        boost::asio::serial_port_base::character_size opt_csize, -        boost::asio::serial_port_base::flow_control opt_flow, -        boost::asio::serial_port_base::stop_bits opt_stop) -{ -    if(is_open()) -        close(); - -    _set_error_status(true); -    _port.open(node); -    _port.set_option( -        boost::asio::serial_port_base::baud_rate(baud_rate)); -    _port.set_option(opt_parity); -    _port.set_option(opt_csize); -    _port.set_option(opt_flow); -    _port.set_option(opt_stop); - -    _io.post(boost::bind(&async_serial::_do_read, this)); - -    boost::thread t(boost::bind(&boost::asio::io_service::run, &_io)); -    _background_thread.swap(t); -    _set_error_status(false); -    _open=true; -} - -bool async_serial::is_open() const -{ -    return _open; -} - -bool async_serial::error_status() const -{ -    boost::lock_guard<boost::mutex> l(_error_mutex); -    return _error; -} - -void async_serial::close() -{ -    if(!is_open()) -        return; - -    _open=false; -    _io.post(boost::bind(&async_serial::_do_close, this)); -    _background_thread.join(); -    _io.reset(); -    if(error_status()) -        throw(boost::system::system_error(boost::system::error_code(), -                "Error while closing the device")); -} - -void async_serial::write(const char *data, size_t size) -{ -    { -        boost::lock_guard<boost::mutex> l(_write_queue_mutex); -        _write_queue.insert(_write_queue.end(), data, data+size); -    } -    _io.post(boost::bind(&async_serial::_do_write, this)); -} - -void async_serial::write(const std::vector<char> &data) -{ -    { -        boost::lock_guard<boost::mutex> l(_write_queue_mutex); -        _write_queue.insert( -            _write_queue.end(), -            data.begin(), -            data.end()); -    } -    _io.post(boost::bind(&async_serial::_do_write, this)); -} - -void async_serial::write_string(const std::string &s) -{ -    { -        boost::lock_guard<boost::mutex> l(_write_queue_mutex); -        _write_queue.insert( -            _write_queue.end(), -            s.begin(), -            s.end()); -    } -    _io.post(boost::bind(&async_serial::_do_write, this)); -} - -async_serial::~async_serial() -{ -    if(is_open()) { -        try { -            close(); -        } catch(...) { -            //Don't throw from a destructor -        } -    } -} - -void async_serial::_do_read() -{ -    _port.async_read_some(boost::asio::buffer( -        _read_buffer,READ_BUFFER_SIZE), -        boost::bind(&async_serial::_read_end, -        this, -        boost::asio::placeholders::error, -        boost::asio::placeholders::bytes_transferred)); -} - -void async_serial::_read_end( -    const boost::system::error_code& error, -    size_t bytes_transferred) -{ -    if(error) { -        if(is_open()) { -            _do_close(); -            _set_error_status(true); -        } -    } else { -        if(_callback) -            _callback( -                _read_buffer, -                bytes_transferred); -        _do_read(); -    } -} - -void async_serial::_do_write() -{ -    // if a write operation is already in progress, do nothing -    if(_write_buffer == 0) { -        boost::lock_guard<boost::mutex> l(_write_queue_mutex); -        _write_buffer_size=_write_queue.size(); -        _write_buffer.reset(new char[_write_queue.size()]); -        std::copy(_write_queue.begin(),_write_queue.end(), -                _write_buffer.get()); -        _write_queue.clear(); -        async_write( -            _port, boost::asio::buffer(_write_buffer.get(), -               _write_buffer_size), -            boost::bind( -                &async_serial::_write_end, -                this, -                boost::asio::placeholders::error)); -    } -} - -void async_serial::_write_end(const boost::system::error_code& error) -{ -    if(!error) { -        boost::lock_guard<boost::mutex> l(_write_queue_mutex); -        if(_write_queue.empty()) { -            _write_buffer.reset(); -            _write_buffer_size=0; -            return; -        } -        _write_buffer_size = _write_queue.size(); -        _write_buffer.reset(new char[_write_queue.size()]); -        std::copy(_write_queue.begin(),_write_queue.end(), -             _write_buffer.get()); -        _write_queue.clear(); -        async_write( -            _port, -            boost::asio::buffer(_write_buffer.get(), -            _write_buffer_size), -            boost::bind( -               &async_serial::_write_end, -               this, -               boost::asio::placeholders::error)); -    } else { -        _set_error_status(true); -        _do_close(); -    } -} - -void async_serial::_do_close() -{ -    boost::system::error_code ec; -    _port.cancel(ec); -    if(ec) -        _set_error_status(true); -    _port.close(ec); -    if(ec) -        _set_error_status(true); -} - -void async_serial::_set_error_status(const bool e) -{ -    boost::lock_guard<boost::mutex> l(_error_mutex); -    _error = e; -} - - -void async_serial::set_read_callback( -    const boost::function<void (const char*, size_t)> &callback) -{ -    _callback = callback; -} - - -}}} // namespace diff --git a/host/lib/usrp/e300/e300_async_serial.hpp b/host/lib/usrp/e300/e300_async_serial.hpp deleted file mode 100644 index fafc7de3d..000000000 --- a/host/lib/usrp/e300/e300_async_serial.hpp +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright 2013-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 -// 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_ASYNC_SERIAL_HPP -#define INCLUDED_ASYNC_SERIAL_HPP - -#include <boost/asio.hpp> -#include <boost/bind.hpp> -#include <boost/thread.hpp> -#include <boost/utility.hpp> -#include <boost/function.hpp> -#include <boost/shared_array.hpp> - -namespace uhd { namespace usrp { namespace gps { - -class async_serial : private boost::noncopyable -{ -public: -    async_serial(); -    ~async_serial(); - -    async_serial(const std::string &node, const size_t baud_rate, -        boost::asio::serial_port_base::parity opt_parity= -            boost::asio::serial_port_base::parity( -                boost::asio::serial_port_base::parity::none), -        boost::asio::serial_port_base::character_size opt_csize= -            boost::asio::serial_port_base::character_size(8), -        boost::asio::serial_port_base::flow_control opt_flow= -            boost::asio::serial_port_base::flow_control( -                boost::asio::serial_port_base::flow_control::none), -        boost::asio::serial_port_base::stop_bits opt_stop= -            boost::asio::serial_port_base::stop_bits( -                boost::asio::serial_port_base::stop_bits::one)); - -   void open(const std::string& node, const size_t baud_rate, -        boost::asio::serial_port_base::parity opt_parity= -            boost::asio::serial_port_base::parity( -                boost::asio::serial_port_base::parity::none), -        boost::asio::serial_port_base::character_size opt_csize= -            boost::asio::serial_port_base::character_size(8), -        boost::asio::serial_port_base::flow_control opt_flow= -            boost::asio::serial_port_base::flow_control( -                boost::asio::serial_port_base::flow_control::none), -        boost::asio::serial_port_base::stop_bits opt_stop= -            boost::asio::serial_port_base::stop_bits( -                boost::asio::serial_port_base::stop_bits::one)); - -   bool is_open(void) const; - -   bool error_status(void) const; - -   void close(void); - -   void write(const char *data, const size_t size); -   void write(const std::vector<char> &data); - -   void write_string(const std::string &s); - -   static const size_t READ_BUFFER_SIZE=512; - -   void set_read_callback( -       const boost::function<void (const char*, size_t)>& callback); - -   void clear_callback(); - -private: // methods -   void _do_read(); - -   void _read_end( -       const boost::system::error_code &error, -       size_t bytes_transferred); - -   void _do_write(); - -   void _write_end(const boost::system::error_code &error); - -   void _do_close(); - -   void _set_error_status(const bool e); -private: // members -    boost::asio::io_service         _io; -    boost::asio::serial_port        _port; -    boost::thread                   _background_thread; -    bool                            _open; -    bool                            _error; -    mutable boost::mutex            _error_mutex; - -    std::vector<char>              _write_queue; -    boost::shared_array<char>      _write_buffer; -    size_t                         _write_buffer_size; -    boost::mutex                   _write_queue_mutex; -    char                           _read_buffer[READ_BUFFER_SIZE]; - -    boost::function<void (const char*, size_t)> _callback; -}; - -}}} // namespace - -#endif //INCLUDED_ASYNC_SERIAL_HPP diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp index db5b37055..29117e21f 100644 --- a/host/lib/usrp/e300/e300_common.cpp +++ b/host/lib/usrp/e300/e300_common.cpp @@ -1,5 +1,5 @@  // -// Copyright 2014 Ettus Research LLC +// 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 @@ -14,8 +14,12 @@  // 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 <uhd/image_loader.hpp>  #include <uhd/utils/msg.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include "e300_impl.hpp"  #include "e300_fifo_config.hpp"  #include "e300_fifo_config.hpp" @@ -23,6 +27,7 @@  #include <boost/filesystem.hpp>  #include <fstream> +#include <string>  namespace uhd { namespace usrp { namespace e300 { @@ -54,6 +59,34 @@ void load_fpga_image(const std::string &path)      UHD_MSG(status) << " done" << std::endl;  } +static bool e300_image_loader(const image_loader::image_loader_args_t &image_loader_args) { +    // Make sure this is an E3x0 and we don't want to use anything connected +    uhd::device_addrs_t devs = e300_find(image_loader_args.args); +    if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + +    std::string fpga_filename, idle_image; // idle_image never used, just needed for function +    if(image_loader_args.fpga_path == "") { +        get_e3x0_fpga_images(devs[0], fpga_filename, idle_image); +    } +    else { +        if(not boost::filesystem::exists(image_loader_args.fpga_path)) { +            throw uhd::runtime_error(str(boost::format("The path \"%s\" does not exist.") +                                         % image_loader_args.fpga_path)); +        } +        else fpga_filename = image_loader_args.fpga_path; +    } + +    load_fpga_image(fpga_filename); +    return true; +} + +UHD_STATIC_BLOCK(register_e300_image_loader) { +    std::string recovery_instructions = "The default FPGA image will be loaded the next " +                                        "time UHD uses this device."; + +    image_loader::register_image_loader("e3x0", e300_image_loader, recovery_instructions); +} +  }  }}} diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp index d409062c5..267897e03 100644 --- a/host/lib/usrp/e300/e300_defaults.hpp +++ b/host/lib/usrp/e300/e300_defaults.hpp @@ -23,18 +23,13 @@  namespace uhd { namespace usrp { namespace e300 {  static const double DEFAULT_TICK_RATE       = 32e6; -static const double MAX_TICK_RATE           = 50e6; -static const double MIN_TICK_RATE           = 1e6; +static const double MIN_TICK_RATE           = 10e6;  static const double DEFAULT_TX_SAMP_RATE    = 1.0e6;  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"; @@ -73,7 +68,7 @@ public:      digital_interface_delays_t get_digital_interface_timing() {          digital_interface_delays_t delays;          delays.rx_clk_delay = 0; -        delays.rx_data_delay = 0xF; +        delays.rx_data_delay = 0x8;          delays.tx_clk_delay = 0;          delays.tx_data_delay = 0xF;          return delays; diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp index eea4d7f63..fbbca329a 100644 --- a/host/lib/usrp/e300/e300_fpga_defs.hpp +++ b/host/lib/usrp/e300/e300_fpga_defs.hpp @@ -21,7 +21,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga {  static const size_t NUM_RADIOS = 2; -static const boost::uint32_t COMPAT_MAJOR = 8; +static const boost::uint32_t COMPAT_MAJOR = 9;  static const boost::uint32_t COMPAT_MINOR = 0;  }}}} // namespace diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp index a08168eab..6eb63c786 100644 --- a/host/lib/usrp/e300/e300_impl.cpp +++ b/host/lib/usrp/e300/e300_impl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-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 @@ -121,7 +121,7 @@ static bool is_loopback(const if_addrs_t &if_addrs)         return if_addrs.inet == asio::ip::address_v4::loopback().to_string();  } -static device_addrs_t e300_find(const device_addr_t &multi_dev_hint) +device_addrs_t e300_find(const device_addr_t &multi_dev_hint)  {      // handle multi device discovery      device_addrs_t hints = separate_device_addr(multi_dev_hint); @@ -268,6 +268,36 @@ static device::sptr e300_make(const device_addr_t &device_addr)          return device::sptr(new e300_impl(device_addr));  } +// Common code used by e300_impl and e300_image_loader +void get_e3x0_fpga_images(const uhd::device_addr_t &device_addr, +                          std::string &fpga_image, +                          std::string &idle_image){ +    const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>( +            device_addr["product"]); + +    //extract the FPGA path for the e300 +    switch(e300_eeprom_manager::get_mb_type(pid)) { +    case e300_eeprom_manager::USRP_E310_MB: +        fpga_image = device_addr.cast<std::string>("fpga", +            find_image_path(E310_FPGA_FILE_NAME)); +        idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME); +        break; +    case e300_eeprom_manager::USRP_E300_MB: +        fpga_image = device_addr.cast<std::string>("fpga", +            find_image_path(E300_FPGA_FILE_NAME)); +        idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); +        break; +    case e300_eeprom_manager::UNKNOWN: +    default: +        UHD_MSG(warning) << "Unknown motherboard type, loading e300 image." +                             << std::endl; +        fpga_image = device_addr.cast<std::string>("fpga", +            find_image_path(E300_FPGA_FILE_NAME)); +        idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); +        break; +    } +} +  /***********************************************************************   * Structors   **********************************************************************/ @@ -286,33 +316,10 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      if (_xport_path == AXI) {          _do_not_reload = device_addr.has_key("no_reload_fpga");          if (not _do_not_reload) { -            // Load FPGA image if provided via args -            const boost::uint16_t pid = boost::lexical_cast<boost::uint16_t>( -                    device_addr["product"]); -              std::string fpga_image; - -            //extract the FPGA path for the e300 -            switch(e300_eeprom_manager::get_mb_type(pid)) { -            case e300_eeprom_manager::USRP_E310_MB: -                fpga_image = device_addr.cast<std::string>("fpga", -                    find_image_path(E310_FPGA_FILE_NAME)); -                _idle_image = find_image_path(E310_FPGA_IDLE_FILE_NAME); -                break; -            case e300_eeprom_manager::USRP_E300_MB: -                fpga_image = device_addr.cast<std::string>("fpga", -                    find_image_path(E300_FPGA_FILE_NAME)); -                _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); -                break; -            case e300_eeprom_manager::UNKNOWN: -            default: -                UHD_MSG(warning) << "Unknown motherboard type, loading e300 image." -                                     << std::endl; -                fpga_image = device_addr.cast<std::string>("fpga", -                    find_image_path(E300_FPGA_FILE_NAME)); -                _idle_image = find_image_path(E300_FPGA_IDLE_FILE_NAME); -                break; -            } +            get_e3x0_fpga_images(device_addr, +                                 fpga_image, +                                 _idle_image);              common::load_fpga_image(fpga_image);          }      } @@ -387,18 +394,35 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)          // This is horrible ... why do I have to sleep here?          boost::this_thread::sleep(boost::posix_time::milliseconds(100));          _eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE)); +        _sensor_manager = e300_sensor_manager::make_local(_global_regs);      } +    _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS); -    UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; -    if (_xport_path == AXI) { -        try { -            _gps = gps::ublox::ubx::control::make("/dev/ttyPS1", 9600); -        } catch (std::exception &e) { -            UHD_MSG(error) << "An error occured making GPSDO control: " << e.what() << std::endl; +#ifdef E300_GPSD +    UHD_MSG(status) << "Detecting internal GPSDO " << std::flush; +    try { +        if (_xport_path == AXI) +            _gps = gpsd_iface::make("localhost", 2947); +        else +            _gps = gpsd_iface::make(device_addr["addr"], 2947); +    } catch (std::exception &e) { +        UHD_MSG(error) << "An error occured making GPSDd interface: " << e.what() << std::endl; +    } + +    if (_gps) { +        for (size_t i = 0; i < _GPS_TIMEOUT; i++) +        { +            boost::this_thread::sleep(boost::posix_time::seconds(1)); +            if (!_gps->gps_detected()) +                std::cout << "." << std::flush; +            else { +                std::cout << ".... " << std::flush; +                break; +            }          } -        _sensor_manager = e300_sensor_manager::make_local(_gps, _global_regs); +        UHD_MSG(status) << (_gps->gps_detected() ? "found" : "not found") << std::endl;      } -    UHD_MSG(status) << (_sensor_manager->get_gps_found() ? "found" : "not found")  << std::endl; +#endif      // Verify we can talk to the e300 core control registers ...      UHD_MSG(status) << "Initializing core control..." << std::endl; @@ -443,6 +467,15 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)          _tree->create<sensor_value_t>(mb_path / "sensors" / name)              .publish(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name));      } +#ifdef E300_GPSD +    if (_gps) { +        BOOST_FOREACH(const std::string &name, _gps->get_sensors()) +        { +            _tree->create<sensor_value_t>(mb_path / "sensors" / name) +                .publish(boost::bind(&gpsd_iface::get_sensor, _gps, name)); +        } +    } +#endif      ////////////////////////////////////////////////////////////////////      // setup the mboard eeprom @@ -471,28 +504,23 @@ 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      ////////////////////////////////////////////////////////////////////      gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); -    const std::vector<std::string> gpio_attrs = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); -    BOOST_FOREACH(const std::string &attr, gpio_attrs) +    BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)      { -        _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr) -            .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr, _1)) +        _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr.second) +            .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr.first, _1))              .set(0);      }      _tree->create<boost::uint8_t>(mb_path / "gpio" / "INT0" / "READBACK") -        .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio, "READBACK")); +        .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio));      //////////////////////////////////////////////////////////////////// @@ -510,7 +538,11 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      _tree->create<std::string>(mb_path / "time_source" / "value")          .subscribe(boost::bind(&e300_impl::_update_time_source, this, _1))          .set(e300::DEFAULT_TIME_SRC); +#ifdef E300_GPSD      static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo"); +#else +    static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external"); +#endif      _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources);      //setup reference source props      _tree->create<std::string>(mb_path / "clock_source" / "value") @@ -575,7 +607,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; @@ -590,40 +622,56 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec);      _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec); -    UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; -    const time_t tp = time_t(_sensor_manager->get_sensor("gps_time").to_int()+1); -    _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); +#ifdef E300_GPSD +    //GPS installed: use external ref, time, and init time spec +    if (_gps and _gps->gps_detected()) { +        UHD_MSG(status) << "Setting references to the internal GPSDO" +                        << std::endl; +        _tree->access<std::string>(mb_path / "time_source" / "value").set("gpsdo"); +        UHD_MSG(status) << "Initializing time to the internal GPSDO" +                        << std::endl; +        const time_t tp = time_t(_gps->get_sensor("gps_time").to_int()+1); +        _tree->access<time_spec_t>(mb_path / "time" / "pps").set(time_spec_t(tp)); + +        // wait for time to be actually set +        boost::this_thread::sleep(boost::posix_time::seconds(1)); +    } +#else +    //init to internal clock and time source +    _tree->access<std::string>(mb_path / "time_source/value").set("internal"); +#endif// E300_GPSD -    // wait for time to be actually set -    boost::this_thread::sleep(boost::posix_time::seconds(1));  } -boost::uint8_t e300_impl::_get_internal_gpio( -    gpio_core_200::sptr gpio, -    const std::string &) +boost::uint8_t e300_impl::_get_internal_gpio(gpio_core_200::sptr gpio)  {      return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX));  }  void e300_impl::_set_internal_gpio(      gpio_core_200::sptr gpio, -    const std::string &attr, +    const gpio_attr_t attr,      const boost::uint32_t value)  { -    if (attr == "CTRL") +    switch (attr) +    { +    case GPIO_CTRL:          return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); -    else if (attr == "DDR") +    case GPIO_DDR:          return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); -    else if (attr == "OUT") +    case GPIO_OUT:          return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); -    else if (attr == "ATR_0X") +    case GPIO_ATR_0X:          return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); -    else if (attr == "ATR_RX") +    case GPIO_ATR_RX:          return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); -    else if (attr == "ATR_TX") +    case GPIO_ATR_TX:          return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); -    else if (attr == "ATR_XX") +    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(); +    }  }  uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx) @@ -665,6 +713,17 @@ void e300_impl::_enforce_tick_rate_limits(                      % direction              ));          } +        // Minimum rate restriction due to MMCM used in capture interface to AD9361. +        // Xilinx Artix-7 FPGA MMCM minimum input frequency is 10 MHz. +        const double min_tick_rate = uhd::usrp::e300::MIN_TICK_RATE / ((chan_count <= 1) ? 1 : 2); +        if (tick_rate - min_tick_rate < 0.0) +        { +            throw uhd::value_error(boost::str( +                boost::format("current master clock rate (%.6f MHz) set below minimum possible master clock rate (%.6f MHz)") +                    % (tick_rate/1e6) +                    % (min_tick_rate/1e6) +            )); +        }      }  } @@ -721,30 +780,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; @@ -799,8 +834,10 @@ void e300_impl::_update_time_source(const std::string &source)      UHD_MSG(status) << boost::format("Setting time source to %s") % source << std::endl;      if (source == "none" or source == "internal") {          _misc.pps_sel = global_regs::PPS_INT; +#ifdef E300_GPSD      } else if (source == "gpsdo") {          _misc.pps_sel = global_regs::PPS_GPS; +#endif      } else if (source == "external") {          _misc.pps_sel = global_regs::PPS_EXT;      } else { @@ -928,6 +965,7 @@ void e300_impl::_setup_radio(const size_t dspno)  {      radio_perifs_t &perif = _radio_perifs[dspno];      const fs_path mb_path = "/mboards/0"; +    std::string slot_name = (dspno == 0) ? "A" : "B";      ////////////////////////////////////////////////////////////////////      // crossbar config for ctrl xports @@ -956,137 +994,102 @@ void e300_impl::_setup_radio(const size_t dspno)          ctrl_sid,          dspno ? "1" : "0");      this->_register_loopback_self_test(perif.ctrl); -    perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_GPIO));      //////////////////////////////////////////////////////////////////// -    // front end corrections +    // Set up peripherals      //////////////////////////////////////////////////////////////////// -    std::string slot_name = (dspno == 0) ? "A" : "B"; +    perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_GPIO));      perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); -    const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name; -    _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") -        .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1)) -        .set(std::complex<double>(0.0, 0.0)); -    _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") -        .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1)) -        .set(true); -    _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") -        .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1)) -        .set(std::complex<double>(0.0, 0.0)); - +    perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); +    perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE); +    perif.rx_fe->set_iq_balance(rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);      perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); -    const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name; -    _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") -        .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1)) -        .set(std::complex<double>(0.0, 0.0)); -    _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") -        .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1)) -        .set(std::complex<double>(0.0, 0.0)); - -    //////////////////////////////////////////////////////////////////// -    // create rx dsp control objects -    //////////////////////////////////////////////////////////////////// +    perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); +    perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);      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));      perif.ddc->set_link_rate(10e9/8); //whatever +    perif.ddc->set_freq(e300::DEFAULT_DDC_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(e300::DEFAULT_DUC_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); + +    //////////////////////////////////////////////////////////////////// +    // front end corrections +    //////////////////////////////////////////////////////////////////// +    perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name)); +    perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name)); + +    //////////////////////////////////////////////////////////////////// +    // 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" / str(boost::format("%u") % 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") -        .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")          .subscribe(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1)) -        .set(e300::DEFAULT_RX_SAMP_RATE); -    _tree->create<double>(rx_dsp_path / "freq" / "value") -        .coerce(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) -        .set(e300::DEFAULT_DDC_FREQ); -    _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" / str(boost::format("%u") % 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") -        .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")          .subscribe(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1)) -        .set(e300::DEFAULT_TX_SAMP_RATE); -    _tree->create<double>(tx_dsp_path / "freq" / "value") -        .coerce(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) -        .set(e300::DEFAULT_DUC_FREQ); -    _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      //////////////////////////////////////////////////////////////////// -    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"); +            = mb_path / "dboards" / "A" / (x + "_frontends") / ((dspno == 0) ? "A" : "B"); + +        // This will connect all the AD936x-specific items +        _codec_mgr->populate_frontend_subtree( +            _tree->subtree(rf_fe_path), key, dir +        ); -        _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 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")); -        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, dir == TX_DIRECTION)) +        ; -            _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); +        // 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");          } -        _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)); - -        //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') { +        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"); diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp index 7f83c16ed..8aff51466 100644 --- a/host/lib/usrp/e300/e300_impl.hpp +++ b/host/lib/usrp/e300/e300_impl.hpp @@ -1,5 +1,5 @@  // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-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 @@ -20,6 +20,7 @@  #include <uhd/device.hpp>  #include <uhd/property_tree.hpp> +#include <uhd/types/device_addr.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <uhd/usrp/mboard_eeprom.hpp>  #include <uhd/usrp/dboard_eeprom.hpp> @@ -28,6 +29,7 @@  #include <uhd/types/sensors.hpp>  #include <boost/weak_ptr.hpp>  #include <boost/thread/mutex.hpp> +#include <string>  #include "e300_fifo_config.hpp"  #include "radio_ctrl_core_3000.hpp"  #include "rx_frontend_core_200.hpp" @@ -38,13 +40,18 @@  #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"  #include "e300_i2c.hpp"  #include "e300_eeprom_manager.hpp"  #include "e300_sensor_manager.hpp" -#include "e300_ublox_control.hpp" + +/* if we don't compile with gpsd support, don't bother */ +#ifdef E300_GPSD +#include "gpsd_iface.hpp" +#endif  namespace uhd { namespace usrp { namespace e300 { @@ -98,6 +105,10 @@ static const size_t E300_R1_CTRL_STREAM    = (1 << 2) | E300_RADIO_DEST_PREFIX_C  static const size_t E300_R1_TX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_TX;  static const size_t E300_R1_RX_DATA_STREAM = (1 << 2) | E300_RADIO_DEST_PREFIX_RX; +uhd::device_addrs_t e300_find(const uhd::device_addr_t &multi_dev_hint); +void get_e3x0_fpga_images(const uhd::device_addr_t &device_args, +                          std::string &fpga_image, +                          std::string &idle_image);  /*!   * USRP-E300 implementation guts: @@ -267,13 +278,11 @@ private: // methods      uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx);      // internal gpios -    boost::uint8_t _get_internal_gpio( -        gpio_core_200::sptr, -        const std::string &); +    boost::uint8_t _get_internal_gpio(gpio_core_200::sptr);      void _set_internal_gpio(          gpio_core_200::sptr gpio, -        const std::string &attr, +        const gpio_attr_t attr,          const boost::uint32_t value);  private: // members @@ -284,6 +293,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; @@ -293,7 +303,10 @@ private: // members      std::string                            _idle_image;      bool                                   _do_not_reload;      gpio_t                                 _misc; -    gps::ublox::ubx::control::sptr _gps; +#ifdef E300_GPSD +    gpsd_iface::sptr                       _gps; +    static const size_t                    _GPS_TIMEOUT = 5; +#endif  };  }}} // namespace 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");  }  /*********************************************************************** diff --git a/host/lib/usrp/e300/e300_network.cpp b/host/lib/usrp/e300/e300_network.cpp index 408f9e62d..96387d6f7 100644 --- a/host/lib/usrp/e300/e300_network.cpp +++ b/host/lib/usrp/e300/e300_network.cpp @@ -230,6 +230,27 @@ static void e300_codec_ctrl_tunnel(              case codec_xact_t::ACTION_GET_RSSI:                  out->rssi = _codec_ctrl->get_rssi(which_str).to_real();                  break; +            case codec_xact_t::ACTION_GET_TEMPERATURE: +                out->temp = _codec_ctrl->get_temperature().to_real(); +                break; +            case codec_xact_t::ACTION_SET_DC_OFFSET_AUTO: +                _codec_ctrl->set_dc_offset_auto(which_str, in->use_dc_correction == 1); +                break; +            case codec_xact_t::ACTION_SET_IQ_BALANCE_AUTO: +                _codec_ctrl->set_iq_balance_auto(which_str, in->use_iq_correction == 1); +            case codec_xact_t::ACTION_SET_AGC: +                _codec_ctrl->set_agc(which_str, in->use_agc == 1); +                break; +            case codec_xact_t::ACTION_SET_AGC_MODE: +                if(in->agc_mode == 0) { +                    _codec_ctrl->set_agc_mode(which_str, "slow"); +                } else if (in->agc_mode == 1) { +                    _codec_ctrl->set_agc_mode(which_str, "fast"); +                } +                break; +            case codec_xact_t::ACTION_SET_BW: +                out->bw = _codec_ctrl->set_bw_filter(which_str, in->bw); +                break;              default:                  UHD_MSG(status) << "Got unknown request?!" << std::endl;                  //Zero out actions to fail this request on client @@ -328,19 +349,9 @@ static void e300_sensor_tunnel(                  // TODO: This is ugly ... use proper serialization                  in->value = uhd::htonx<boost::uint32_t>(                      e300_sensor_manager::pack_float_in_uint32_t(temp.to_real())); -            } else if (uhd::ntohx(in->which) == GPS_FOUND) { -                in->value = uhd::htonx<boost::uint32_t>( -                    sensor_manager->get_gps_found() ? 1 : 0); - -            } else if (uhd::ntohx(in->which) == GPS_LOCK) { -                in->value = uhd::htonx<boost::uint32_t>( -                    sensor_manager->get_gps_lock().to_bool() ? 1 : 0);              } else if (uhd::ntohx(in->which) == REF_LOCK) {                  in->value = uhd::htonx<boost::uint32_t>(                      sensor_manager->get_ref_lock().to_bool() ? 1 : 0); -            } else if (uhd::ntohx(in->which) == GPS_TIME) { -                in->value = uhd::htonx<boost::uint32_t>( -                    sensor_manager->get_gps_time().to_int());              } else                  UHD_MSG(status) << "Got unknown request?!" << std::endl; @@ -627,8 +638,7 @@ network_server_impl::network_server_impl(const uhd::device_addr_t &device_addr)      _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1);      // This is horrible ... why do I have to sleep here?      boost::this_thread::sleep(boost::posix_time::milliseconds(100)); -    _sensor_manager = e300_sensor_manager::make_local( -        gps::ublox::ubx::control::make("/dev/ttyPS1", 9600), _global_regs); +    _sensor_manager = e300_sensor_manager::make_local(_global_regs);  }  }}} // namespace diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp index 6742f5f86..9708634dd 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp @@ -130,10 +130,118 @@ public:          _args.bits = uhd::htonx<boost::uint32_t>(0);          _transact(); -          return sensor_value_t("RSSI", _retval.rssi, "dB");      } +    sensor_value_t get_temperature() +    { +        _clear(); +        _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_GET_TEMPERATURE); +        _args.which  = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_NONE);  /*Unused*/ +        _args.bits = uhd::htonx<boost::uint32_t>(0); + +        _transact(); +        return sensor_value_t("temp", _retval.temp, "C"); +    } + +    void set_dc_offset_auto(const std::string &which, const bool on) +    { +        _clear(); +        _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_DC_OFFSET_AUTO); +        if (which == "TX1")      _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); +        else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); +        else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); +        else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); +        else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); +        _args.use_dc_correction = on ? 1 : 0; + +        _transact(); +    } + +    void set_iq_balance_auto(const std::string &which, const bool on) +    { +        _clear(); +        _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_IQ_BALANCE_AUTO); +        if (which == "TX1")     _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); +        else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); +        else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); +        else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); +        else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); +        _args.use_iq_correction = on ? 1 : 0; + +        _transact(); +    } + +    void set_agc(const std::string &which, bool enable) +    { +        _clear(); +       _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC); +       if (which == "TX1")      _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); +       else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); +       else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); +       else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); +       else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); +       _args.use_agc = enable ? 1 : 0; + +       _transact(); +    } + +    void set_agc_mode(const std::string &which, const std::string &mode) +    { +        _clear(); +       _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_AGC_MODE); + +       if (which == "TX1")      _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); +       else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); +       else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); +       else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); +       else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); + +       if(mode == "slow") { +           _args.agc_mode = 0; +       } else if (mode == "fast") { +           _args.agc_mode = 1; +       } else { +           throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect agc mode."); +       } + +       _transact(); +    } + +    //! set the filter bandwidth for the frontend's analog low pass +    double set_bw_filter(const std::string &which, const double bw) +    { +        _clear(); +        _args.action = uhd::htonx<boost::uint32_t>(transaction_t::ACTION_SET_BW); +        if (which == "TX1")      _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX1); +        else if (which == "TX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_TX2); +        else if (which == "RX1") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX1); +        else if (which == "RX2") _args.which = uhd::htonx<boost::uint32_t>(transaction_t::CHAIN_RX2); +        else throw std::runtime_error("e300_remote_codec_ctrl_impl incorrect chain string."); +        _args.bw = bw; + +        _transact(); +        return _retval.bw; +    } + +    //! List all available filters by name +    std::vector<std::string> get_filter_names(const std::string &) +    { +        UHD_THROW_INVALID_CODE_PATH(); +    } + +    //! Return a list of all filters +    filter_info_base::sptr get_filter(const std::string &, const std::string &) +    { +        UHD_THROW_INVALID_CODE_PATH(); +    } + +    //! Write back a filter +    void set_filter(const std::string &, const std::string &, const filter_info_base::sptr) +    { +        UHD_THROW_INVALID_CODE_PATH(); +    } +  private:      void _transact() {          { diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp index e21f2ef95..43723e0d5 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.hpp @@ -34,6 +34,12 @@ public:              double          gain;              double          freq;              double          rssi; +            double          temp; +            double          bw; +            boost::uint32_t use_dc_correction; +            boost::uint32_t use_iq_correction; +            boost::uint32_t use_agc; +            boost::uint32_t agc_mode;              boost::uint64_t bits;          }; @@ -44,7 +50,13 @@ public:          static const boost::uint32_t ACTION_TUNE                = 13;          static const boost::uint32_t ACTION_SET_LOOPBACK        = 14;          static const boost::uint32_t ACTION_GET_RSSI            = 15; -        static const boost::uint32_t ACTION_GET_FREQ            = 16; +        static const boost::uint32_t ACTION_GET_TEMPERATURE     = 16; +        static const boost::uint32_t ACTION_SET_DC_OFFSET_AUTO  = 17; +        static const boost::uint32_t ACTION_SET_IQ_BALANCE_AUTO = 18; +        static const boost::uint32_t ACTION_SET_AGC             = 19; +        static const boost::uint32_t ACTION_SET_AGC_MODE        = 20; +        static const boost::uint32_t ACTION_SET_BW              = 21; +        static const boost::uint32_t ACTION_GET_FREQ            = 22;          //Values for "which"          static const boost::uint32_t CHAIN_NONE = 0; diff --git a/host/lib/usrp/e300/e300_sensor_manager.cpp b/host/lib/usrp/e300/e300_sensor_manager.cpp index 527cfb91a..a4319fa4b 100644 --- a/host/lib/usrp/e300/e300_sensor_manager.cpp +++ b/host/lib/usrp/e300/e300_sensor_manager.cpp @@ -24,7 +24,6 @@  #include <cstring>  #include <uhd/exception.hpp>  #include <uhd/utils/byteswap.hpp> -#include <uhd/usrp/gps_ctrl.hpp>  namespace uhd { namespace usrp { namespace e300 { @@ -38,17 +37,13 @@ public:      std::vector<std::string> get_sensors()      { -        return boost::assign::list_of("temp")("gps_locked")("gps_time")("ref_locked"); +        return boost::assign::list_of("temp")("ref_locked");      }      uhd::sensor_value_t get_sensor(const std::string &key)      {          if (key == "temp")              return get_mb_temp(); -        else if (key == "gps_locked") -            return get_gps_lock(); -        else if (key == "gps_time") -            return get_gps_time();          else if (key == "ref_locked")              return get_ref_lock();          else @@ -94,108 +89,6 @@ public:              "C");      } -    uhd::sensor_value_t get_gps_time(void) -    { -        boost::mutex::scoped_lock(_mutex); -        sensor_transaction_t transaction; -        transaction.which = uhd::htonx<boost::uint32_t>(GPS_TIME); -        { -            uhd::transport::managed_send_buffer::sptr buff -                = _xport->get_send_buff(1.0); -            if (not buff or buff->size() < sizeof(transaction)) { -                throw uhd::runtime_error("sensor proxy send timeout"); -            } -            std::memcpy( -                buff->cast<void *>(), -                &transaction, -                sizeof(transaction)); -            buff->commit(sizeof(transaction)); -        } -        { -            uhd::transport::managed_recv_buffer::sptr buff -                = _xport->get_recv_buff(1.0); - -            if (not buff or buff->size() < sizeof(transaction)) -                throw uhd::runtime_error("sensor proxy recv timeout"); - -            std::memcpy( -                &transaction, -                buff->cast<const void *>(), -                sizeof(transaction)); -        } -        UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_TIME); -        // TODO: Use proper serialization here ... -        return sensor_value_t("GPS epoch time", int(uhd::ntohx<boost::uint32_t>(transaction.value)), "seconds"); -    } - -    bool get_gps_found(void) -    { -        boost::mutex::scoped_lock(_mutex); -        sensor_transaction_t transaction; -        transaction.which = uhd::htonx<boost::uint32_t>(GPS_FOUND); -        { -            uhd::transport::managed_send_buffer::sptr buff -                = _xport->get_send_buff(1.0); -            if (not buff or buff->size() < sizeof(transaction)) { -                throw uhd::runtime_error("sensor proxy send timeout"); -            } -            std::memcpy( -                buff->cast<void *>(), -                &transaction, -                sizeof(transaction)); -            buff->commit(sizeof(transaction)); -        } -        { -            uhd::transport::managed_recv_buffer::sptr buff -                = _xport->get_recv_buff(1.0); - -            if (not buff or buff->size() < sizeof(transaction)) -                throw uhd::runtime_error("sensor proxy recv timeout"); - -            std::memcpy( -                &transaction, -                buff->cast<const void *>(), -                sizeof(transaction)); -        } -        UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_FOUND); -        // TODO: Use proper serialization here ... -        return (uhd::ntohx(transaction.value) > 0); -    } - -    uhd::sensor_value_t get_gps_lock(void) -    { -        boost::mutex::scoped_lock(_mutex); -        sensor_transaction_t transaction; -        transaction.which = uhd::htonx<boost::uint32_t>(GPS_LOCK); -        { -            uhd::transport::managed_send_buffer::sptr buff -                = _xport->get_send_buff(1.0); -            if (not buff or buff->size() < sizeof(transaction)) { -                throw uhd::runtime_error("sensor proxy send timeout"); -            } -            std::memcpy( -                buff->cast<void *>(), -                &transaction, -                sizeof(transaction)); -            buff->commit(sizeof(transaction)); -        } -        { -            uhd::transport::managed_recv_buffer::sptr buff -                = _xport->get_recv_buff(1.0); - -            if (not buff or buff->size() < sizeof(transaction)) -                throw uhd::runtime_error("sensor proxy recv timeout"); - -            std::memcpy( -                &transaction, -                buff->cast<const void *>(), -                sizeof(transaction)); -        } -        UHD_ASSERT_THROW(uhd::ntohx<boost::uint32_t>(transaction.which) == GPS_LOCK); -        // TODO: Use proper serialization here ... -        return sensor_value_t("GPS lock status", (uhd::ntohx(transaction.value) > 0), "locked", "unlocked"); -    } -      uhd::sensor_value_t get_ref_lock(void)      {          boost::mutex::scoped_lock(_mutex); @@ -255,24 +148,20 @@ static const std::string E300_TEMP_SYSFS = "iio:device0";  class e300_sensor_local : public e300_sensor_manager  {  public: -    e300_sensor_local(uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs) : -        _gps_ctrl(gps_ctrl), _global_regs(global_regs) +    e300_sensor_local(global_regs::sptr global_regs) : +        _global_regs(global_regs)      {      }      std::vector<std::string> get_sensors()      { -        return boost::assign::list_of("temp")("gps_locked")("gps_time")("ref_locked"); +        return boost::assign::list_of("temp")("ref_locked");      }      uhd::sensor_value_t get_sensor(const std::string &key)      {          if (key == "temp")              return get_mb_temp(); -        else if (key == "gps_locked") -            return get_gps_lock(); -        else if (key == "gps_time") -            return get_gps_time();          else if (key == "ref_locked")              return get_ref_lock();          else @@ -291,21 +180,6 @@ public:          return sensor_value_t("temp", (raw + offset) * scale / 1000, "C");      } -    bool get_gps_found(void) -    { -        return _gps_ctrl->gps_detected(); -    } - -    uhd::sensor_value_t get_gps_lock(void) -    { -        return _gps_ctrl->get_sensor("gps_locked"); -    } - -    uhd::sensor_value_t get_gps_time(void) -    { -        return _gps_ctrl->get_sensor("gps_time"); -    } -      uhd::sensor_value_t get_ref_lock(void)      {          //PPSLOOP_LOCKED_MASK is asserted in the following cases: @@ -322,22 +196,21 @@ public:      }  private: -    gps_ctrl::sptr      _gps_ctrl;      global_regs::sptr   _global_regs;  };  }}}  using namespace uhd::usrp::e300;  e300_sensor_manager::sptr e300_sensor_manager::make_local( -    uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs) +    global_regs::sptr global_regs)  { -    return sptr(new e300_sensor_local(gps_ctrl, global_regs)); +    return sptr(new e300_sensor_local(global_regs));  }  #else  using namespace uhd::usrp::e300;  e300_sensor_manager::sptr e300_sensor_manager::make_local( -    uhd::gps_ctrl::sptr, global_regs::sptr) +    global_regs::sptr)  {      throw uhd::assertion_error("e300_sensor_manager::make_local() !E300_NATIVE");  } diff --git a/host/lib/usrp/e300/e300_sensor_manager.hpp b/host/lib/usrp/e300/e300_sensor_manager.hpp index 09f889251..bfaf8e90c 100644 --- a/host/lib/usrp/e300/e300_sensor_manager.hpp +++ b/host/lib/usrp/e300/e300_sensor_manager.hpp @@ -39,25 +39,22 @@ struct sensor_transaction_t { -enum sensor {ZYNQ_TEMP=0, GPS_FOUND=1, GPS_TIME=2, -             GPS_LOCK=3, REF_LOCK=4}; +enum sensor {ZYNQ_TEMP=0, REF_LOCK=4};  class e300_sensor_manager : boost::noncopyable  {  public:      typedef boost::shared_ptr<e300_sensor_manager> sptr; -    virtual bool get_gps_found(void) = 0;      virtual uhd::sensor_value_t get_sensor(const std::string &key) = 0;      virtual std::vector<std::string> get_sensors(void) = 0;      virtual uhd::sensor_value_t get_mb_temp(void) = 0; -    virtual uhd::sensor_value_t get_gps_lock(void) = 0; -    virtual uhd::sensor_value_t get_gps_time(void) = 0;      virtual uhd::sensor_value_t get_ref_lock(void) = 0; +      static sptr make_proxy(uhd::transport::zero_copy_if::sptr xport); -    static sptr make_local(uhd::gps_ctrl::sptr gps_ctrl, global_regs::sptr global_regs); +    static sptr make_local(global_regs::sptr global_regs);      // Note: This is a hack      static boost::uint32_t pack_float_in_uint32_t(const float &v) diff --git a/host/lib/usrp/e300/e300_ublox_control.hpp b/host/lib/usrp/e300/e300_ublox_control.hpp deleted file mode 100644 index 8705d6c52..000000000 --- a/host/lib/usrp/e300/e300_ublox_control.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP -#define INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP - -#include <boost/cstdint.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/asio.hpp> -#include <uhd/config.hpp> -#include <uhd/usrp/gps_ctrl.hpp> -#include <uhd/types/sensors.hpp> - -#include "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { - -class control : public virtual uhd::gps_ctrl -{ -public: -    typedef boost::shared_ptr<control> sptr; - -    static sptr make(const std::string &node, const size_t baud_rate); - -    virtual void configure_message_rate( -        const boost::uint16_t msg, -        const boost::uint8_t rate) = 0; - -    virtual void configure_antenna( -        const boost::uint16_t flags, -        const boost::uint16_t pins) = 0; - -    virtual void configure_pps( -        const boost::uint32_t interval, -        const boost::uint32_t length, -        const boost::int8_t status, -        const boost::uint8_t time_ref, -        const boost::uint8_t flags, -        const boost::int16_t antenna_delay, -        const boost::int16_t rf_group_delay, -        const boost::int32_t user_delay) = 0; - -    virtual void configure_rates( -        boost::uint16_t meas_rate, -        boost::uint16_t nav_rate, -        boost::uint16_t time_ref) = 0; -}; -}} // namespace ublox::ubx - -}}} // namespace -#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_HPP diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.cpp b/host/lib/usrp/e300/e300_ublox_control_impl.cpp deleted file mode 100644 index 389bf79fa..000000000 --- a/host/lib/usrp/e300/e300_ublox_control_impl.cpp +++ /dev/null @@ -1,505 +0,0 @@ -#include <boost/format.hpp> -#include <boost/foreach.hpp> -#include <boost/bind.hpp> -#include <boost/make_shared.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/assign/list_of.hpp> -#include "boost/date_time/posix_time/posix_time.hpp" - -#include <iostream> - - -#include <uhd/utils/byteswap.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/exception.hpp> - -#include "e300_ublox_control.hpp" - -#ifdef E300_NATIVE -#include "e300_ublox_control_impl.hpp" - - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { - -control_impl::control_impl(const std::string &node, const size_t baud_rate) -{ -    _decode_init(); -    _serial = boost::make_shared<async_serial>(node, baud_rate); -    _serial->set_read_callback(boost::bind(&control_impl::_rx_callback, this, _1, _2)); - -    _detect(); - -    configure_message_rate(MSG_GLL, 0); -    configure_message_rate(MSG_GSV, 0); -    configure_message_rate(MSG_GGA, 0); -    configure_message_rate(MSG_GSA, 0); -    configure_message_rate(MSG_RMC, 0); -    configure_message_rate(MSG_VTG, 0); -    configure_message_rate(MSG_NAV_TIMEUTC, 1); -    configure_message_rate(MSG_NAV_SOL, 1); - -    configure_antenna(0x001b, 0x8251); - -    configure_pps(0xf4240, 0x3d090, 1, 0 /* utc */, 1, 0, 0, 0); - -    _sensors = boost::assign::list_of("gps_locked")("gps_time"); -} - -bool control_impl::gps_detected(void) -{ -    return _detected; -} - -void control_impl::_detect(void) -{ -    _send_message(MSG_MON_VER, NULL, 0); -} - -std::vector<std::string> control_impl::get_sensors(void) -{ -    return _sensors; -} - -uhd::sensor_value_t control_impl::get_sensor(std::string key) -{ -    if (key == "gps_time") { -        bool lock; -        _locked.wait_and_see(lock); -        return sensor_value_t("GPS epoch time", -            lock ? int(_get_epoch_time()) : 0, "seconds"); -    } else if (key == "gps_locked") { -        bool lock; -        _locked.wait_and_see(lock); -        return sensor_value_t("GPS lock status", lock, "locked", "unlocked"); -    } else -        throw uhd::key_error(str(boost::format("sensor %s unknown.") % key)); -} - -std::time_t control_impl::_get_epoch_time(void) -{ -    boost::posix_time::ptime ptime; -    _ptime.wait_and_see(ptime); -    return (ptime - boost::posix_time::from_time_t(0)).total_seconds(); -} - -control_impl::~control_impl(void) -{ -} - -void control_impl::_decode_init(void) -{ -    _decode_state = DECODE_SYNC1; -    _rx_ck_a = 0; -    _rx_ck_b = 0; -    _rx_payload_length = 0; -    _rx_payload_index  = 0; -} - -void control_impl::_add_byte_to_checksum(const boost::uint8_t b) -{ -    _rx_ck_a = _rx_ck_a + b; -    _rx_ck_b = _rx_ck_b + _rx_ck_a; -} - -void control_impl::_calc_checksum( -    const boost::uint8_t *buffer, -    const boost::uint16_t length, -    checksum_t &checksum) -{ -    for (size_t i = 0; i < length; i++) -    { -        checksum.ck_a = checksum.ck_a + buffer[i]; -        checksum.ck_b = checksum.ck_b + checksum.ck_a; -    } -} - -void control_impl::configure_rates( -    boost::uint16_t meas_rate, -    boost::uint16_t nav_rate, -    boost::uint16_t time_ref) -{ -    payload_tx_cfg_rate_t cfg_rate; -    cfg_rate.meas_rate = uhd::htowx<boost::uint16_t>(meas_rate); -    cfg_rate.nav_rate = uhd::htowx<boost::uint16_t>(nav_rate); -    cfg_rate.time_ref = uhd::htowx<boost::uint16_t>(time_ref); - -    _send_message( -        MSG_CFG_RATE, -        reinterpret_cast<const uint8_t*>(&cfg_rate), -        sizeof(cfg_rate)); - -    _wait_for_ack(MSG_CFG_RATE, 1.0); -} - -void control_impl::configure_message_rate( -    const boost::uint16_t msg, -    const uint8_t rate) -{ -    payload_tx_cfg_msg_t cfg_msg; -    cfg_msg.msg  = uhd::htowx<boost::uint16_t>(msg); -    cfg_msg.rate[0] = 0;//rate; -    cfg_msg.rate[1] = rate; -    cfg_msg.rate[2] = 0;//rate; -    cfg_msg.rate[3] = 0;//rate; -    cfg_msg.rate[4] = 0;//rate; -    cfg_msg.rate[5] = 0;//rate; -    _send_message( -        MSG_CFG_MSG, -        reinterpret_cast<const uint8_t*>(&cfg_msg), -        sizeof(cfg_msg)); - -    _wait_for_ack(MSG_CFG_MSG, 1.0); -} - -void control_impl::configure_antenna( -    const boost::uint16_t flags, -    const boost::uint16_t pins) -{ -    payload_tx_cfg_ant_t cfg_ant; -    cfg_ant.pins = uhd::htowx<boost::uint16_t>(pins); -    cfg_ant.flags = uhd::htowx<boost::uint16_t>(flags); -    _send_message( -        MSG_CFG_ANT, -        reinterpret_cast<const uint8_t*>(&cfg_ant), -        sizeof(cfg_ant)); -    if (_wait_for_ack(MSG_CFG_ANT, 1.0) < 0) { -        throw uhd::runtime_error("Didn't get an ACK for antenna configuration."); -    } - -} - -void control_impl::configure_pps( -    const boost::uint32_t interval, -    const boost::uint32_t length, -    const boost::int8_t status, -    const boost::uint8_t time_ref, -    const boost::uint8_t flags, -    const boost::int16_t antenna_delay, -    const boost::int16_t rf_group_delay, -    const boost::int32_t user_delay) -{ -    payload_tx_cfg_tp_t cfg_tp; -    cfg_tp.interval = uhd::htowx<boost::uint32_t>(interval); -    cfg_tp.length = uhd::htowx<boost::uint32_t>(length); -    cfg_tp.status = status; -    cfg_tp.time_ref = time_ref; -    cfg_tp.flags = flags; -    cfg_tp.antenna_delay = uhd::htowx<boost::int16_t>(antenna_delay); -    cfg_tp.rf_group_delay = uhd::htowx<boost::int16_t>(rf_group_delay); -    cfg_tp.user_delay = uhd::htowx<boost::int32_t>(user_delay); -    _send_message( -        MSG_CFG_TP, -        reinterpret_cast<const uint8_t*>(&cfg_tp), -        sizeof(cfg_tp)); -    if (_wait_for_ack(MSG_CFG_TP, 1.0) < 0) { -        throw uhd::runtime_error("Didn't get an ACK for PPS configuration."); -    } -} - - -void control_impl::_rx_callback(const char *data, unsigned int len) -{ -    //std::cout << "IN RX CALLBACK" << std::flush << std::endl; -    std::vector<char> v(data, data+len); -    BOOST_FOREACH(const char &c, v) -    { -        _parse_char(c); -    } -} - -void control_impl::_parse_char(const boost::uint8_t b) -{ -    int ret = 0; - -    switch (_decode_state) { - -    // we're expecting the first sync byte -    case DECODE_SYNC1: -        if (b == SYNC1) { // sync1 found goto next step -            _decode_state = DECODE_SYNC2; -        } // else stay around -        break; - -    // we're expecting the second sync byte -    case DECODE_SYNC2: -        if (b == SYNC2) { // sync2 found goto next step -            _decode_state = DECODE_CLASS; -        } else { -            // failed, reset -            _decode_init(); -        } -        break; - -    // we're expecting the class byte -    case DECODE_CLASS: -        _add_byte_to_checksum(b); -        _rx_msg = b; -        _decode_state = DECODE_ID; -        break; - -    // we're expecting the id byte -    case DECODE_ID: -        _add_byte_to_checksum(b); -        _rx_msg |= (b << 8); -        _decode_state = DECODE_LENGTH1; -        break; - -    // we're expecting the first length byte -    case DECODE_LENGTH1: -        _add_byte_to_checksum(b); -        _rx_payload_length = b; -        _decode_state = DECODE_LENGTH2; -        break; - -    // we're expecting the second length byte -    case DECODE_LENGTH2: -        _add_byte_to_checksum(b); -        _rx_payload_length |= (b << 8); -        if(_payload_rx_init()) { -            _decode_init(); // we failed, give up for this one -        } else { -            _decode_state = _rx_payload_length ? -                DECODE_PAYLOAD : DECODE_CHKSUM1; -        } -        break; - -    // we're expecting payload -    case DECODE_PAYLOAD: -        _add_byte_to_checksum(b); -        switch(_rx_msg) { -        default: -            ret = _payload_rx_add(b); -            break; -        }; -        if (ret < 0) { -            // we couldn't deal with the payload, discard the whole thing -            _decode_init(); -        } else if (ret > 0) { -            // payload was complete, let's check the checksum; -            _decode_state = DECODE_CHKSUM1; -        } else { -            // more payload expected, don't move -        } -        ret = 0; -        break; - -    case DECODE_CHKSUM1: -        if (_rx_ck_a != b) { -            // checksum didn't match, barf -            std::cout << boost::format("Failed checksum byte1 %lx != %lx") -                % int(_rx_ck_a) % int(b) << std::endl; -            _decode_init(); -        } else { -            _decode_state = DECODE_CHKSUM2; -        } -        break; - -    case DECODE_CHKSUM2: -        if (_rx_ck_b != b) { -            // checksum didn't match, barf -            std::cout << boost::format("Failed checksum byte2 %lx != %lx") -                % int(_rx_ck_b) % int(b) << std::endl; - -        } else { -            ret = _payload_rx_done(); // payload done -        } -        _decode_init(); -        break; - -    default: -        break; -    }; -} - -int control_impl::_payload_rx_init(void) -{ -    int ret = 0; - -    _rx_state = RXMSG_HANDLE; // by default handle -    switch(_rx_msg) { - -    case MSG_NAV_SOL: -        if (not (_rx_payload_length == sizeof(payload_rx_nav_sol_t))) -            _rx_state = RXMSG_ERROR_LENGTH; -        break; - -    case MSG_NAV_TIMEUTC: -        if (not (_rx_payload_length == sizeof(payload_rx_nav_timeutc_t))) -            _rx_state = RXMSG_ERROR_LENGTH; -        break; - -    case MSG_MON_VER: -        break; // always take this one - -    case MSG_ACK_ACK: -        if (not (_rx_payload_length == sizeof(payload_rx_ack_ack_t))) -            _rx_state = RXMSG_ERROR_LENGTH; -        break; - -    case MSG_ACK_NAK: -        if (not (_rx_payload_length == sizeof(payload_rx_ack_nak_t))) -            _rx_state = RXMSG_ERROR_LENGTH; -        break; - -    default: -        _rx_state = RXMSG_DISABLE; -        break; -    }; - -    switch (_rx_state) { -    case RXMSG_HANDLE: // handle message -    case RXMSG_IGNORE: // ignore message but don't report error -        ret = 0; -        break; -    case RXMSG_DISABLE: // ignore message but don't report error -    case RXMSG_ERROR_LENGTH: // the length doesn't match -        ret = -1; -        break; -    default: // invalid, error -        ret = -1; -        break; -    }; - -    return ret; -} - -int control_impl::_payload_rx_add(const boost::uint8_t b) -{ -    int ret = 0; -    _buf.raw[_rx_payload_index] = b; -    if (++_rx_payload_index >= _rx_payload_length) -        ret = 1; -    return ret; -} - -int control_impl::_payload_rx_done(void) -{ -    int ret = 0; -    if (_rx_state != RXMSG_HANDLE) { -        return 0; -    } - -    switch (_rx_msg) { -    case MSG_MON_VER: -        _detected = true; -        break; - -    case MSG_MON_HW: -        std::cout << "MON-HW" << std::endl; -        break; - -    case MSG_ACK_ACK: -        if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_ack.msg == _ack_waiting_msg)) -            _ack_state = ACK_GOT_ACK; -        break; - -    case MSG_ACK_NAK: -        if ((_ack_state == ACK_WAITING) and (_buf.payload_rx_ack_nak.msg == _ack_waiting_msg)) -            _ack_state = ACK_GOT_NAK; - -        break; - -    case MSG_CFG_ANT: -        break; - -    case MSG_NAV_TIMEUTC: -        _ptime.update(boost::posix_time::ptime( -            boost::gregorian::date( -                boost::gregorian::greg_year(uhd::wtohx<boost::uint16_t>( -                    _buf.payload_rx_nav_timeutc.year)), -                boost::gregorian::greg_month(_buf.payload_rx_nav_timeutc.month), -                boost::gregorian::greg_day(_buf.payload_rx_nav_timeutc.day)), -            (boost::posix_time::hours(_buf.payload_rx_nav_timeutc.hour) -            + boost::posix_time::minutes(_buf.payload_rx_nav_timeutc.min) -            + boost::posix_time::seconds(_buf.payload_rx_nav_timeutc.sec)))); -        break; - -    case MSG_NAV_SOL: -        _locked.update(_buf.payload_rx_nav_sol.gps_fix > 0); -        break; - -    default: -        std::cout << boost::format("Got unknown message %lx , with good checksum [") % int(_rx_msg); -        for(size_t i = 0; i < _rx_payload_length; i++) -            std::cout << boost::format("%lx, ") % int(_buf.raw[i]); -        std::cout << "]"<< std::endl; -        break; -    }; -    return ret; -} - -void control_impl::_send_message( -    const boost::uint16_t msg, -    const boost::uint8_t *payload, -    const boost::uint16_t len) -{ -    header_t header = {SYNC1, SYNC2, msg, len}; -    checksum_t checksum = {0, 0}; - -    // calculate checksums, first header without sync -    // then payload -    _calc_checksum( -        reinterpret_cast<boost::uint8_t*>(&header) + 2, -        sizeof(header) - 2, checksum); -    if (payload) -        _calc_checksum(payload, len, checksum); - -    _serial->write( -        reinterpret_cast<const char*>(&header), -        sizeof(header)); - -    if (payload) -        _serial->write((const char *) payload, len); - -    _serial->write( -        reinterpret_cast<const char*>(&checksum), -        sizeof(checksum)); -} - -int control_impl::_wait_for_ack( -    const boost::uint16_t msg, -    const double timeout) -{ -    int ret = -1; - -    _ack_state = ACK_WAITING; -    _ack_waiting_msg = msg; - -    boost::system_time timeout_time = -        boost::get_system_time() + -        boost::posix_time::milliseconds(timeout * 1000.0); - -    do { -        if(_ack_state == ACK_GOT_ACK) -            return 0; -        else if (_ack_state == ACK_GOT_NAK) { -            return -1; -        } -        boost::this_thread::sleep(boost::posix_time::milliseconds(20)); -    } while (boost::get_system_time() < timeout_time); - -    // we get here ... it's a timeout -    _ack_state = ACK_IDLE; -    return ret; -} - - -}} // namespace ublox::ubx -}}} // namespace - -using namespace uhd::usrp::gps::ublox::ubx; - -control::sptr control::make(const std::string &node, const size_t baud_rate) -{ -    return control::sptr(new control_impl(node, baud_rate)); -} -#else -using namespace uhd::usrp::gps::ublox::ubx; - -control::sptr control::make(const std::string& /* node */, const size_t /* baud_rate */) -{ -    throw uhd::assertion_error("control::sptr::make: !E300_NATIVE"); -} -#endif // E300_NATIVE diff --git a/host/lib/usrp/e300/e300_ublox_control_impl.hpp b/host/lib/usrp/e300/e300_ublox_control_impl.hpp deleted file mode 100644 index a1dcbfe6c..000000000 --- a/host/lib/usrp/e300/e300_ublox_control_impl.hpp +++ /dev/null @@ -1,457 +0,0 @@ -#ifndef INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP -#define INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP - -#include <boost/cstdint.hpp> -#include <boost/noncopyable.hpp> -#include <boost/asio.hpp> -#include <uhd/config.hpp> -#include <uhd/usrp/gps_ctrl.hpp> -#include <uhd/types/sensors.hpp> - -#include "e300_async_serial.hpp" - -namespace uhd { namespace usrp { namespace gps { - -namespace ublox { namespace ubx { -// ublox binary sync words -static const boost::uint8_t SYNC1 = 0xB5; -static const boost::uint8_t SYNC2 = 0x62; - -// message classes -static const boost::uint8_t CLASS_NAV  = 0x01; -static const boost::uint8_t CLASS_ACK  = 0x05; -static const boost::uint8_t CLASS_CFG  = 0x06; -static const boost::uint8_t CLASS_MON  = 0x0a; -static const boost::uint8_t CLASS_NMEA = 0xf0; - -// Message IDs -static const boost::uint8_t ID_NAV_POSLLH  = 0x02; -static const boost::uint8_t ID_NAV_SOL     = 0x06; -static const boost::uint8_t ID_NAV_PVT     = 0x07; -static const boost::uint8_t ID_NAV_VELNED  = 0x12; -static const boost::uint8_t ID_NAV_TIMEUTC = 0x21; -static const boost::uint8_t ID_NAV_SVINFO  = 0x30; -static const boost::uint8_t ID_ACK_NAK     = 0x00; -static const boost::uint8_t ID_ACK_ACK     = 0x01; -static const boost::uint8_t ID_CFG_PRT     = 0x00; -static const boost::uint8_t ID_CFG_ANT     = 0x13; -static const boost::uint8_t ID_CFG_TP      = 0x07; -static const boost::uint8_t ID_CFG_MSG     = 0x01; -static const boost::uint8_t ID_CFG_RATE    = 0x08; -static const boost::uint8_t ID_CFG_NAV5    = 0x24; -static const boost::uint8_t ID_MON_VER     = 0x04; -static const boost::uint8_t ID_MON_HW      = 0x09; -static const boost::uint8_t ID_GGA         = 0x00; -static const boost::uint8_t ID_GLL         = 0x01; -static const boost::uint8_t ID_GSA         = 0x02; -static const boost::uint8_t ID_GSV         = 0x03; -static const boost::uint8_t ID_RMC         = 0x04; -static const boost::uint8_t ID_VTG         = 0x05; -static const boost::uint8_t ID_GST         = 0x07; - -// Message Classes & IDs // -static const boost::uint16_t MSG_NAV_POSLLH -    = CLASS_NAV | (ID_NAV_POSLLH << 8); -static const boost::uint16_t MSG_NAV_SOL -    = CLASS_NAV | (ID_NAV_SOL << 8); -static const boost::uint16_t MSG_NAV_PVT -    = CLASS_NAV | (ID_NAV_PVT << 8); -static const boost::uint16_t MSG_NAV_VELNED -    = CLASS_NAV | (ID_NAV_VELNED << 8); -static const boost::uint16_t MSG_NAV_TIMEUTC -    = CLASS_NAV | (ID_NAV_TIMEUTC << 8); -static const boost::uint16_t MSG_NAV_SVINFO -    = CLASS_NAV | (ID_NAV_SVINFO << 8); -static const boost::uint16_t MSG_ACK_NAK -    = CLASS_ACK | (ID_ACK_NAK << 8); -static const boost::uint16_t MSG_ACK_ACK -    = CLASS_ACK | (ID_ACK_ACK << 8); -static const boost::uint16_t MSG_CFG_PRT -    = CLASS_CFG | (ID_CFG_PRT << 8); -static const boost::uint16_t MSG_CFG_ANT -    = CLASS_CFG | (ID_CFG_ANT << 8); -static const boost::uint16_t MSG_CFG_TP -    = CLASS_CFG | (ID_CFG_TP << 8); -static const boost::uint16_t MSG_CFG_MSG -    = CLASS_CFG | (ID_CFG_MSG << 8); -static const boost::uint16_t MSG_CFG_RATE -    = CLASS_CFG | (ID_CFG_RATE << 8); -static const boost::uint16_t MSG_CFG_NAV5 -    = CLASS_CFG | (ID_CFG_NAV5 << 8); -static const boost::uint16_t MSG_MON_HW -    = CLASS_MON | (ID_MON_HW << 8); -static const boost::uint16_t MSG_MON_VER -    = CLASS_MON | (ID_MON_VER << 8); - -// NMEA ones -static const boost::uint16_t MSG_GGA -    = CLASS_NMEA | (ID_GGA << 8); -static const boost::uint16_t MSG_GLL -    = CLASS_NMEA | (ID_GLL << 8); -static const boost::uint16_t MSG_GSA -    = CLASS_NMEA | (ID_GSA << 8); -static const boost::uint16_t MSG_GSV -    = CLASS_NMEA | (ID_GSV << 8); -static const boost::uint16_t MSG_RMC -    = CLASS_NMEA | (ID_RMC << 8); -static const boost::uint16_t MSG_VTG -    = CLASS_NMEA | (ID_VTG << 8); - -// header -struct header_t -{ -    boost::uint8_t sync1; -    boost::uint8_t sync2; -    boost::uint16_t msg; -    boost::uint16_t length; -}; - -// checksum -struct checksum_t -{ -    boost::uint8_t ck_a; -    boost::uint8_t ck_b; -}; - -// rx rx mon-hw (ubx6) -struct payload_rx_mon_hw_t -{ -    boost::uint32_t pin_sel; -    boost::uint32_t pin_bank; -    boost::uint32_t pin_dir; -    boost::uint32_t pin_val; -    boost::uint16_t noise_per_ms; -    boost::uint16_t agc_cnt; -    boost::uint8_t  a_status; -    boost::uint8_t  a_power; -    boost::uint8_t  flags; -    boost::uint8_t  reserved1; -    boost::uint32_t used_mask; -    boost::uint8_t  vp[25]; -    boost::uint8_t  jam_ind; -    boost::uint16_t reserved3; -    boost::uint32_t pin_irq; -    boost::uint32_t pullh; -    boost::uint32_t pulll; -}; - -// rx mon-ver -struct payload_rx_mon_ver_part1_t -{ -    char sw_version[30]; -    char hw_version[10]; -}; - -struct payload_rx_mon_ver_part2_t -{ -    boost::uint8_t extension[30]; -}; - -// rx ack-ack -typedef union { -    boost::uint16_t msg; -    struct { -        boost::uint8_t cls_id; -        boost::uint8_t msg_id; -    }; -} payload_rx_ack_ack_t; - -// rx ack-nak -typedef union { -    boost::uint16_t msg; -    struct { -        boost::uint8_t cls_id; -        boost::uint8_t msg_id; -    }; -} payload_rx_ack_nak_t; - -// tx cfg-prt (uart) -struct payload_tx_cfg_prt_t -{ -    boost::uint8_t  port_id; -    boost::uint8_t  reserved0; -    boost::uint16_t tx_ready; -    boost::uint32_t mode; -    boost::uint32_t baud_rate; -    boost::uint16_t in_proto_mask; -    boost::uint16_t out_proto_mask; -    boost::uint16_t flags; -    boost::uint16_t reserved5; -}; - -// tx cfg-rate -struct payload_tx_cfg_rate_t -{ -    boost::uint16_t meas_rate; -    boost::uint16_t nav_rate; -    boost::uint16_t time_ref; -}; - -// tx cfg-msg -struct payload_tx_cfg_msg_t -{ -    boost::uint16_t msg; -    boost::uint8_t rate[6]; -}; - - -// tx cfg-ant -struct payload_tx_cfg_ant_t -{ -    boost::uint16_t flags; -    boost::uint16_t pins; -}; - -// tx cfg-tp -struct payload_tx_cfg_tp_t -{ -    boost::uint32_t interval; -    boost::uint32_t length; -    boost::int8_t status; -    boost::uint8_t time_ref; -    boost::uint8_t flags; -    boost::uint8_t reserved1; -    boost::int16_t antenna_delay; -    boost::int16_t rf_group_delay; -    boost::int32_t user_delay; -}; - -struct payload_rx_nav_sol_t -{ -    boost::uint32_t i_tow; -    boost::int32_t f_tow; -    boost::int16_t week; -    boost::uint8_t gps_fix; -    boost::uint8_t flags; -    boost::int32_t ecef_x; -    boost::int32_t ecef_y; -    boost::int32_t ecef_z; -    boost::uint32_t p_acc; -    boost::int32_t ecef_vx; -    boost::int32_t ecef_vy; -    boost::int32_t ecef_vz; -    boost::uint32_t s_acc; -    boost::uint16_t p_dop; -    boost::uint8_t reserved1; -    boost::uint8_t num_sv; -    boost::uint32_t reserved2; -}; - -struct payload_rx_nav_timeutc_t -{ -    boost::uint32_t i_tow; -    boost::uint32_t t_acc; -    boost::int32_t nano; -    boost::uint16_t year; -    boost::uint8_t month; -    boost::uint8_t day; -    boost::uint8_t hour; -    boost::uint8_t min; -    boost::uint8_t sec; -    boost::uint8_t valid; -}; - -typedef union { -    payload_rx_mon_hw_t        payload_rx_mon_hw; - -    payload_rx_mon_ver_part1_t payload_rx_mon_ver_part1; -    payload_rx_mon_ver_part2_t payload_rx_mon_ver_part2; - -    payload_rx_ack_ack_t       payload_rx_ack_ack; -    payload_rx_ack_nak_t       payload_rx_ack_nak; - -    payload_tx_cfg_prt_t       payload_tx_cfg_prt; -    payload_tx_cfg_ant_t       payload_tx_cfg_ant; -    payload_tx_cfg_rate_t      payload_tx_cfg_rate; - -    payload_tx_cfg_msg_t       payload_tx_cfg_msg; - -    payload_rx_nav_timeutc_t   payload_rx_nav_timeutc; -    payload_rx_nav_sol_t   payload_rx_nav_sol; -    boost::uint8_t             raw[]; -} buf_t; - - -template <typename T> -class sensor_entry -{ -public: -    sensor_entry() : _seen(false) -    { -    } - -    void update(const T &val) -    { -        boost::mutex::scoped_lock l(_mutex); -        _value = val; -        _seen = false; -        l.unlock(); -        _cond.notify_one(); -    } - -    bool seen() const -    { -        boost::mutex::scoped_lock l(_mutex); -        return _seen; -    } - -    bool try_and_see(T &val) -    { -        boost::mutex::scoped_lock l(_mutex); -        if (_seen) -            return false; - -        val = _value; -        _seen = true; -        return true; -    } - -    void wait_and_see(T &val) -    { -        boost::mutex::scoped_lock l(_mutex); -        while(_seen) -        { -            _cond.wait(l); -            //std::cout << "Already seen ... " << std::endl; -        } -        val = _value; -        _seen = true; -    } - -private: // members -    T                         _value; -    boost::mutex              _mutex; -    boost::condition_variable _cond; -    bool                _seen; -}; - -class control_impl : public control -{ -public: -    control_impl(const std::string &node, const size_t baud_rate); - -    virtual ~control_impl(void); - -    void configure_message_rate( -        const boost::uint16_t msg, -        const boost::uint8_t rate); - -    void configure_antenna( -        const boost::uint16_t flags, -        const boost::uint16_t pins); - -    void configure_pps( -        const boost::uint32_t interval, -        const boost::uint32_t length, -        const boost::int8_t status, -        const boost::uint8_t time_ref, -        const boost::uint8_t flags, -        const boost::int16_t antenna_delay, -        const boost::int16_t rf_group_delay, -        const boost::int32_t user_delay); - -    void configure_rates( -        boost::uint16_t meas_rate, -        boost::uint16_t nav_rate, -        boost::uint16_t time_ref); - -    // gps_ctrl interface -    bool gps_detected(void); -    std::vector<std::string> get_sensors(void); -    uhd::sensor_value_t get_sensor(std::string key); - -private: // types -    enum decoder_state_t { -        DECODE_SYNC1 = 0, -        DECODE_SYNC2, -        DECODE_CLASS, -        DECODE_ID, -        DECODE_LENGTH1, -        DECODE_LENGTH2, -        DECODE_PAYLOAD, -        DECODE_CHKSUM1, -        DECODE_CHKSUM2, -    }; - -    enum rxmsg_state_t { -        RXMSG_IGNORE = 0, -        RXMSG_HANDLE, -        RXMSG_DISABLE, -        RXMSG_ERROR_LENGTH -    }; - -    enum ack_state_t { -        ACK_IDLE = 0, -        ACK_WAITING, -        ACK_GOT_ACK, -        ACK_GOT_NAK -    }; - -private: // methods -    std::time_t _get_epoch_time(void); - -    void _decode_init(void); - -    void _add_byte_to_checksum(const boost::uint8_t b); - -    void _detect(void); - -    void _send_message( -        const boost::uint16_t msg, -        const boost::uint8_t *payload, -        const boost::uint16_t len); - -    int _wait_for_ack( -        const boost::uint16_t msg, -        const double timeout); - -    void _calc_checksum( -        const boost::uint8_t *buffer, -        const boost::uint16_t length, -        checksum_t &checksum); - -    void _rx_callback(const char *data, unsigned len); - -    void _parse_char(const boost::uint8_t b); - -    int _payload_rx_init(void); - -    int _payload_rx_add(const boost::uint8_t b); - -    int _payload_rx_done(void); - -private: // members -    // gps_ctrl stuff -    bool                                   _detected; -    std::vector<std::string>               _sensors; - -    sensor_entry<bool>                     _locked; -    sensor_entry<boost::posix_time::ptime> _ptime; - -    // decoder state -    decoder_state_t                        _decode_state; -    rxmsg_state_t                          _rxmsg_state; - -    ack_state_t                            _ack_state; -    boost::uint16_t                        _ack_waiting_msg; - -    boost::uint8_t                         _rx_ck_a; -    boost::uint8_t                         _rx_ck_b; - -    boost::uint16_t                        _rx_payload_length; -    size_t                                 _rx_payload_index; -    boost::uint16_t                        _rx_msg; - -    rxmsg_state_t                          _rx_state; - -    boost::shared_ptr<async_serial>        _serial; - -    // this has to be at the end of the -    // class to be valid C++ -    buf_t                                  _buf; -}; - -}} // namespace ublox::ubx - -}}} // namespace -#endif // INCLUDED_UHD_USRP_UBLOX_CONTROL_IMPL_HPP diff --git a/host/lib/usrp/gpsd_iface.cpp b/host/lib/usrp/gpsd_iface.cpp new file mode 100644 index 000000000..e0a1dea05 --- /dev/null +++ b/host/lib/usrp/gpsd_iface.cpp @@ -0,0 +1,309 @@ +// +// Copyright 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 <cmath> + +#include <gps.h> + +#include <boost/assign/list_of.hpp> +#include <boost/bind.hpp> +#include <boost/cstdint.hpp> +#include "boost/date_time/gregorian/gregorian.hpp" +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/thread/shared_mutex.hpp> +#include <boost/thread.hpp> +#include <boost/math/special_functions/fpclassify.hpp> + +#include <uhd/exception.hpp> +#include <uhd/usrp/gps_ctrl.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/dict.hpp> + +#include "gpsd_iface.hpp" + +namespace uhd { namespace usrp { + +static const size_t TIMEOUT = 240; +static const size_t CLICK_RATE = 250000; + +class gpsd_iface_impl : public virtual gpsd_iface { +public: +    gpsd_iface_impl(const std::string &addr, boost::uint16_t port) +        : _detected(false), _bthread(), _timeout_cnt(0) +    { +        boost::unique_lock<boost::shared_mutex> l(_d_mutex); + +        if (gps_open(addr.c_str(), +            str(boost::format("%u") % port).c_str(), +            &_gps_data) < 0) { +            throw uhd::runtime_error( +                str((boost::format("Failed to connect to gpsd: %s") +                    % gps_errstr(errno)))); +        } + +        // register for updates, we don't specify a specific device, +        // therefore no WATCH_DEVICE +        gps_stream(&_gps_data, WATCH_ENABLE, NULL); + +        // create background thread talking to gpsd +        boost::thread t(boost::bind(&gpsd_iface_impl::_thread_fcn ,this)); +        _bthread.swap(t); + + +        _sensors = boost::assign::list_of("gps_locked")("gps_time") \ +            ("gps_position")("gps_gpgga")("gps_gprmc"); +    } + +    virtual ~gpsd_iface_impl(void) +    { +        // interrupt the background thread and wait for it to finish +        _bthread.interrupt(); +        _bthread.join(); + +        // clean up ... +        { +            boost::unique_lock<boost::shared_mutex> l(_d_mutex); + +            gps_stream(&_gps_data, WATCH_DISABLE, NULL); +            gps_close(&_gps_data); +        } +    } + +    uhd::sensor_value_t get_sensor(std::string key) +    { +        if (key == "gps_locked") { +            return sensor_value_t( +                "GPS lock status", _gps_locked(), "locked", "unlocked"); +        } else if (key == "gps_time") { +            return sensor_value_t( +                "GPS epoch time", int(_epoch_time()), "seconds"); +        } else if (key == "gps_gpgga") { +            return sensor_value_t( +                "GPGGA", _gps_gpgga(), ""); +        } else if (key == "gps_gprmc") { +            return sensor_value_t( +                "GPRMC", _gps_gprmc(), ""); +        } else if (key == "gps_position") { +            return sensor_value_t( +                "GPS Position", str( +                    boost::format("%s %s %s") +                    % _gps_position()["lat"] +                    % _gps_position()["lon"] +                    % _gps_position()["alt"]), "lat/lon/alt"); +        } else +            throw uhd::key_error( +                str(boost::format("sensor %s unknown.") % key)); +    } + +    bool gps_detected(void) { return _detected; }; + +    std::vector<std::string> get_sensors(void) { return _sensors; }; + +private: // member functions +    void _thread_fcn() +    { +        while (not boost::this_thread::interruption_requested()) { +            if (!gps_waiting(&_gps_data, CLICK_RATE)) { +                if (TIMEOUT < _timeout_cnt++) +                    _detected = false; +            } else { +                boost::unique_lock<boost::shared_mutex> l(_d_mutex); + +                _timeout_cnt = 0; +                _detected = true; + +                if (gps_read(&_gps_data) < 0) +                    throw std::runtime_error("error while reading"); +            } +        } +    } + +    bool _gps_locked(void) +    { +        boost::shared_lock<boost::shared_mutex> l(_d_mutex); +        return _gps_data.fix.mode >= MODE_2D; +    } + +    std::time_t _epoch_time(void) +    { +        boost::shared_lock<boost::shared_mutex> l(_d_mutex); +        return (boost::posix_time::from_time_t(_gps_data.fix.time) +            - boost::posix_time::from_time_t(0)).total_seconds(); +    } + +    boost::gregorian::date _gregorian_date(void) +    { +        boost::shared_lock<boost::shared_mutex> l(_d_mutex); +        return boost::posix_time::from_time_t(_gps_data.fix.time).date(); +    } + +    uhd::dict<std::string, std::string> _gps_position(void) +    { +        boost::shared_lock<boost::shared_mutex> l(_d_mutex); + +        uhd::dict<std::string, std::string> tmp; +        if (_gps_data.fix.mode >= MODE_2D) { +            tmp["lon"] = str(boost::format("%f deg") +                    % _gps_data.fix.longitude); +            tmp["lat"] = str(boost::format("%f deg") +                    % _gps_data.fix.latitude); +            tmp["alt"] = str(boost::format("%fm") +                    % _gps_data.fix.altitude); +        } else { +            tmp["lon"] = "n/a"; +            tmp["lat"] = "n/a"; +            tmp["alt"] = "n/a"; +        } +        return tmp; +    } + +    float _zeroize(float x) +    { +      return boost::math::isnan(x) ? 0.0 : x; +    } + +    int _nmea_checksum(const std::string &s) +    { +        if ((s.at(0) != '$')) +            return 0; + +        boost::uint8_t sum = '\0'; +        for (size_t i = 1; i < s.size(); i++) +            sum ^= static_cast<boost::uint8_t>(s.at(i)); + +        return sum; +    } + +    double _deg_to_dm(double angle) +    { +        double fraction, integer; +        fraction = std::modf(angle, &integer); +        return std::floor(angle) * 100 + fraction * 60; +    } + +    std::string _gps_gprmc(void) +    { +        struct tm tm; +        time_t intfixtime; + +        boost::shared_lock<boost::shared_mutex> l(_d_mutex); + +        tm.tm_mday = tm.tm_mon = tm.tm_year = 0; +        tm.tm_hour = tm.tm_min = tm.tm_sec = 0; + +        if (boost::math::isnan(_gps_data.fix.time) == 0) { +            intfixtime = (time_t) _gps_data.fix.time; +            (void)gmtime_r(&intfixtime, &tm); +            tm.tm_mon++; +            tm.tm_year %= 100; +        } +        std::string string = str(boost::format( +           "$GPRMC,%02d%02d%02d,%c,%09.4f,%c,%010.4f,%c,%.4f,%.3f,%02d%02d%02d,,") +        % tm.tm_hour +        % tm.tm_min +        % tm.tm_sec +        % (_gps_data.status ? 'A' : 'V') +        % _zeroize(_deg_to_dm(std::fabs(_gps_data.fix.latitude))) +        % ((_gps_data.fix.latitude > 0) ? 'N' : 'S') +        % _zeroize(_deg_to_dm(std::fabs(_gps_data.fix.longitude))) +        % ((_gps_data.fix.longitude > 0) ? 'E' : 'W') +        % _zeroize(_gps_data.fix.speed * MPS_TO_KNOTS) +        % _zeroize(_gps_data.fix.track) +        % tm.tm_mday % tm.tm_mon % tm.tm_year); + +        string.append(str( +            boost::format("*%02X") % _nmea_checksum(string))); + +        return string; +    } + +    std::string _gps_gpgga(void) +    { +        struct tm tm; +        time_t intfixtime; + +        // currently not supported, make it blank +        float mag_var = NAN; + +        boost::shared_lock<boost::shared_mutex> l(_d_mutex); + +        intfixtime = (time_t) _gps_data.fix.time; +        (void) gmtime_r(&intfixtime, &tm); + +        std::string string = str(boost::format( +            "$GPGGA,%02d%02d%02d,%09.4f,%c,%010.4f,%c,%d,%02d,") +            % tm.tm_hour +            % tm.tm_min +            % tm.tm_sec +            % _deg_to_dm(std::fabs(_gps_data.fix.latitude)) +            % ((_gps_data.fix.latitude > 0) ? 'N' : 'S') +            % _deg_to_dm(std::fabs(_gps_data.fix.longitude)) +            % ((_gps_data.fix.longitude > 0) ? 'E' : 'W') +            % _gps_data.status +            % _gps_data.satellites_used); + +        if (boost::math::isnan(_gps_data.dop.hdop)) +            string.append(","); +        else +            string.append( +                str(boost::format("%.2f,") % _gps_data.dop.hdop)); + +        if (boost::math::isnan(_gps_data.fix.altitude)) +            string.append(","); +        else +            string.append( +                str(boost::format("%.2f,M,") % _gps_data.fix.altitude)); + +        if (boost::math::isnan(_gps_data.separation)) +            string.append(","); +        else +            string.append( +                str(boost::format("%.3f,M,") % _gps_data.separation)); + +        if (boost::math::isnan(mag_var)) +            string.append(","); +        else { +            string.append( +                str(boost::format("%3.2f,%s") % std::fabs(mag_var) +                % (mag_var > 0 ? "E" : "W"))); +        } + +        string.append(str( +            boost::format("*%02X") % _nmea_checksum(string))); + +        return string; +    } + +private: // members +    std::vector<std::string> _sensors; +    bool                     _detected; + +    gps_data_t               _gps_data; +    boost::shared_mutex      _d_mutex; +    boost::thread            _bthread; +    size_t                   _timeout_cnt; +}; + +}} //namespace + +using namespace uhd::usrp; + +gpsd_iface::sptr gpsd_iface::make(const std::string &addr, const boost::uint16_t port) +{ +    return gpsd_iface::sptr(new gpsd_iface_impl(addr, port)); +} diff --git a/host/lib/usrp/gpsd_iface.hpp b/host/lib/usrp/gpsd_iface.hpp new file mode 100644 index 000000000..7d934ae5c --- /dev/null +++ b/host/lib/usrp/gpsd_iface.hpp @@ -0,0 +1,36 @@ +// +// Copyright 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/>. +// + +#ifndef INCLUDED_GPSD_IFACE_HPP +#define INCLUDED_GPSD_IFACE_HPP + +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> + +#include <uhd/usrp/gps_ctrl.hpp> + +namespace uhd { namespace usrp { + +class gpsd_iface : public virtual uhd::gps_ctrl { +public: +    typedef boost::shared_ptr<gpsd_iface> sptr; +    static sptr make(const std::string &addr, boost::uint16_t port); +}; + +}}; + +#endif /* INCLUDED_GPSD_IFACE_HPP */ diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 68c084589..f60182c76 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2013,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 @@ -16,6 +16,7 @@  //  #include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/byte_vector.hpp>  #include <uhd/types/mac_addr.hpp>  #include <uhd/utils/byteswap.hpp>  #include <boost/asio/ip/address_v4.hpp> @@ -39,32 +40,6 @@ static const size_t NAME_MAX_LEN = 32 - SERIAL_LEN;   * Utility functions   **********************************************************************/ -//! A wrapper around std::copy that takes ranges instead of iterators. -template<typename RangeSrc, typename RangeDst> inline -void byte_copy(const RangeSrc &src, RangeDst &dst){ -    std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); -} - -//! create a string from a byte vector, return empty if invalid ascii -static const std::string bytes_to_string(const byte_vector_t &bytes){ -    std::string out; -    BOOST_FOREACH(boost::uint8_t byte, bytes){ -        if (byte < 32 or byte > 127) return out; -        out += byte; -    } -    return out; -} - -//! create a byte vector from a string, null terminate unless max length -static const byte_vector_t string_to_bytes(const std::string &string, size_t max_length){ -    byte_vector_t bytes; -    for (size_t i = 0; i < std::min(string.size(), max_length); i++){ -        bytes.push_back(string[i]); -    } -    if (bytes.size() < max_length - 1) bytes.push_back('\0'); -    return bytes; -} -  //! convert a string to a byte vector to write to eeprom  static byte_vector_t string_to_uint16_bytes(const std::string &num_str){      const boost::uint16_t num = boost::lexical_cast<boost::uint16_t>(num_str); @@ -238,7 +213,8 @@ struct x300_eeprom_map      //indentifying numbers      unsigned char revision[2];      unsigned char product[2]; -    boost::uint8_t _pad0[4]; +    unsigned char revision_compat[2]; +    boost::uint8_t _pad0[2];      //all the mac addrs      boost::uint8_t mac_addr0[6]; @@ -264,6 +240,11 @@ static void load_x300(mboard_eeprom_t &mb_eeprom, i2c_iface &iface)          iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision), 2)      ); +    //extract the revision compat number +    mb_eeprom["revision_compat"] = uint16_bytes_to_string( +        iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), 2) +    ); +      //extract the product code      mb_eeprom["product"] = uint16_bytes_to_string(          iface.read_eeprom(X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), 2) @@ -310,6 +291,12 @@ static void store_x300(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface)          string_to_uint16_bytes(mb_eeprom["revision"])      ); +    //parse the revision compat number +    if (mb_eeprom.has_key("revision_compat")) iface.write_eeprom( +        X300_EEPROM_ADDR, offsetof(x300_eeprom_map, revision_compat), +        string_to_uint16_bytes(mb_eeprom["revision_compat"]) +    ); +      //parse the product code      if (mb_eeprom.has_key("product")) iface.write_eeprom(          X300_EEPROM_ADDR, offsetof(x300_eeprom_map, product), diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 794438b90..1866255c9 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -30,6 +30,8 @@  #include <boost/thread.hpp>  #include <boost/foreach.hpp>  #include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <algorithm>  #include <cmath>  using namespace uhd; @@ -431,6 +433,9 @@ public:       ******************************************************************/      void set_master_clock_rate(double rate, size_t mboard){          if (mboard != ALL_MBOARDS){ +            if (_tree->exists(mb_root(mboard) / "auto_tick_rate")) { +                _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").set(false); +            }              _tree->access<double>(mb_root(mboard) / "tick_rate").set(rate);              return;          } @@ -821,6 +826,26 @@ public:      }      void set_rx_gain(double gain, const std::string &name, size_t chan){ +        /* Check if any AGC mode is enable and if so warn the user */ +        if (chan != ALL_CHANS) { +            if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc")) { +                bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get(); +                if(agc) { +                    UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl; +                } +            } +        } else { +            for (size_t c = 0; c < get_rx_num_channels(); c++){ +                if (_tree->exists(rx_rf_fe_root(c) / "gain" / "agc")) { +                    bool agc = _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").get(); +                    if(agc) { +                        UHD_MSG(warning) << "AGC enabled for this channel. Setting will be ignored." << std::endl; +                    } +                } +            } +        } +        /* Apply gain setting. +         * If device is in AGC mode it will ignore the setting. */          try {              return rx_gain_group(chan)->set_value(gain, name);          } catch (uhd::key_error &) { @@ -828,6 +853,32 @@ public:          }      } +    void set_normalized_rx_gain(double gain, size_t chan = 0) +    { +      if (gain > 1.0 || gain < 0.0) { +        throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1]."); +      } +      gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan); +      double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start(); +      set_rx_gain(abs_gain, ALL_GAINS, chan); +    } + +    void set_rx_agc(bool enable, size_t chan = 0) +    { +        if (chan != ALL_CHANS){ +            if (_tree->exists(rx_rf_fe_root(chan) / "gain" / "agc" / "enable")) { +                _tree->access<bool>(rx_rf_fe_root(chan) / "gain" / "agc" / "enable").set(enable); +            } else { +                UHD_MSG(warning) << "AGC is not available on this device." << std::endl; +            } +            return; +        } +        for (size_t c = 0; c < get_rx_num_channels(); c++){ +            this->set_rx_agc(enable, c); +        } + +    } +      double get_rx_gain(const std::string &name, size_t chan){          try {              return rx_gain_group(chan)->get_value(name); @@ -836,6 +887,21 @@ public:          }      } +    double get_normalized_rx_gain(size_t chan) +    { +      gain_range_t gain_range = get_rx_gain_range(ALL_GAINS, chan); +      double gain_range_width = gain_range.stop() - gain_range.start(); +      // In case we have a device without a range of gains: +      if (gain_range_width == 0.0) { +          return 0; +      } +      double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width; +      // Avoid rounding errors: +      if (norm_gain > 1.0) return 1.0; +      if (norm_gain < 0.0) return 0.0; +      return norm_gain; +    } +      gain_range_t get_rx_gain_range(const std::string &name, size_t chan){          try {              return rx_gain_group(chan)->get_range(name); @@ -888,6 +954,9 @@ public:          if (chan != ALL_CHANS){              if (_tree->exists(rx_fe_root(chan) / "dc_offset" / "enable")) {                  _tree->access<bool>(rx_fe_root(chan) / "dc_offset" / "enable").set(enb); +            } else if (_tree->exists(rx_rf_fe_root(chan) / "dc_offset" / "enable")) { +                /*For B2xx devices the dc-offset correction is implemented in the rf front-end*/ +                _tree->access<bool>(rx_rf_fe_root(chan) / "dc_offset" / "enable").set(enb);              } else {                  UHD_MSG(warning) << "Setting DC offset compensation is not possible on this device." << std::endl;              } @@ -912,6 +981,20 @@ public:          }      } +    void set_rx_iq_balance(const bool enb, size_t chan){ +        if (chan != ALL_CHANS){ +            if (_tree->exists(rx_rf_fe_root(chan) / "iq_balance" / "enable")) { +                _tree->access<bool>(rx_rf_fe_root(chan) / "iq_balance" / "enable").set(enb); +            } else { +                UHD_MSG(warning) << "Setting IQ imbalance compensation is not possible on this device." << std::endl; +            } +            return; +        } +        for (size_t c = 0; c < get_rx_num_channels(); c++){ +            this->set_rx_iq_balance(enb, c); +        } +    } +      void set_rx_iq_balance(const std::complex<double> &offset, size_t chan){          if (chan != ALL_CHANS){              if (_tree->exists(rx_fe_root(chan) / "iq_balance" / "value")) { @@ -926,6 +1009,87 @@ public:          }      } +    std::vector<std::string> get_filter_names(const std::string &search_mask) +    { +        std::vector<std::string> ret; + +        for (size_t chan = 0; chan < get_rx_num_channels(); chan++){ + +            if (_tree->exists(rx_rf_fe_root(chan) / "filters")) { +                std::vector<std::string> names = _tree->list(rx_rf_fe_root(chan) / "filters"); +                for(size_t i = 0; i < names.size(); i++) +                { +                    std::string name = rx_rf_fe_root(chan) / "filters" / names[i]; +                    if((search_mask.empty()) or boost::contains(name, search_mask)) { +                        ret.push_back(name); +                    } +                } +            } +            if (_tree->exists(rx_dsp_root(chan) / "filters")) { +                std::vector<std::string> names = _tree->list(rx_dsp_root(chan) / "filters"); +                for(size_t i = 0; i < names.size(); i++) +                { +                    std::string name = rx_dsp_root(chan) / "filters" / names[i]; +                    if((search_mask.empty()) or (boost::contains(name, search_mask))) { +                        ret.push_back(name); +                    } +                } +            } + +        } + +        for (size_t chan = 0; chan < get_tx_num_channels(); chan++){ + +            if (_tree->exists(tx_rf_fe_root(chan) / "filters")) { +                std::vector<std::string> names = _tree->list(tx_rf_fe_root(chan) / "filters"); +                for(size_t i = 0; i < names.size(); i++) +                { +                    std::string name = tx_rf_fe_root(chan) / "filters" / names[i]; +                    if((search_mask.empty()) or (boost::contains(name, search_mask))) { +                        ret.push_back(name); +                    } +                } +            } +            if (_tree->exists(rx_dsp_root(chan) / "filters")) { +                std::vector<std::string> names = _tree->list(tx_dsp_root(chan) / "filters"); +                for(size_t i = 0; i < names.size(); i++) +                { +                    std::string name = tx_dsp_root(chan) / "filters" / names[i]; +                    if((search_mask.empty()) or (boost::contains(name, search_mask))) { +                        ret.push_back(name); +                    } +                } +            } + +        } + +        return ret; +    } + +    filter_info_base::sptr get_filter(const std::string &path) +    { +        std::vector<std::string> possible_names = get_filter_names(""); +        std::vector<std::string>::iterator it; +        it = find(possible_names.begin(), possible_names.end(), path); +        if (it == possible_names.end()) { +            throw uhd::runtime_error("Attempting to get non-existing filter: "+path); +        } + +        return _tree->access<filter_info_base::sptr>(path / "value").get(); +    } + +    void set_filter(const std::string &path, filter_info_base::sptr filter) +    { +        std::vector<std::string> possible_names = get_filter_names(""); +        std::vector<std::string>::iterator it; +        it = find(possible_names.begin(), possible_names.end(), path); +        if (it == possible_names.end()) { +            throw uhd::runtime_error("Attempting to set non-existing filter: "+path); +        } + +        _tree->access<filter_info_base::sptr>(path / "value").set(filter); +    } +      /*******************************************************************       * TX methods       ******************************************************************/ @@ -1029,6 +1193,17 @@ public:          }      } +    void set_normalized_tx_gain(double gain, size_t chan = 0) +    { +      if (gain > 1.0 || gain < 0.0) { +        throw uhd::runtime_error("Normalized gain out of range, must be in [0, 1]."); +      } +      gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan); +      double abs_gain = (gain * (gain_range.stop() - gain_range.start())) + gain_range.start(); +      set_tx_gain(abs_gain, ALL_GAINS, chan); +    } + +      double get_tx_gain(const std::string &name, size_t chan){          try {              return tx_gain_group(chan)->get_value(name); @@ -1037,6 +1212,21 @@ public:          }      } +    double get_normalized_tx_gain(size_t chan) +    { +      gain_range_t gain_range = get_tx_gain_range(ALL_GAINS, chan); +      double gain_range_width = gain_range.stop() - gain_range.start(); +      // In case we have a device without a range of gains: +      if (gain_range_width == 0.0) { +          return 0.0; +      } +      double norm_gain = (get_rx_gain(ALL_GAINS, chan) - gain_range.start()) / gain_range_width; +      // Avoid rounding errors: +      if (norm_gain > 1.0) return 1.0; +      if (norm_gain < 0.0) return 0.0; +      return norm_gain; +    } +      gain_range_t get_tx_gain_range(const std::string &name, size_t chan){          try {              return tx_gain_group(chan)->get_range(name); diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index c6257c7fe..bd302895b 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2011-2012,2014 Ettus Research LLC +# Copyright 2011-2012,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 @@ -25,18 +25,6 @@  LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF)  IF(ENABLE_USRP2) -    ######################################################################## -    # Define UHD_PKG_DATA_PATH for usrp2_iface.cpp -    ######################################################################## -    FILE(TO_NATIVE_PATH ${CMAKE_INSTALL_PREFIX} UHD_PKG_PATH) -    STRING(REPLACE "\\" "\\\\" UHD_PKG_PATH ${UHD_PKG_PATH}) - -    SET_SOURCE_FILES_PROPERTIES( -        ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp -        PROPERTIES COMPILE_DEFINITIONS -        "UHD_LIB_DIR=\"lib${LIB_SUFFIX}\"" -    ) -      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp @@ -45,5 +33,6 @@ IF(ENABLE_USRP2)          ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n200_image_loader.cpp      )  ENDIF(ENABLE_USRP2) diff --git a/host/lib/usrp/usrp2/n200_image_loader.cpp b/host/lib/usrp/usrp2/n200_image_loader.cpp new file mode 100644 index 000000000..ce956c22c --- /dev/null +++ b/host/lib/usrp/usrp2/n200_image_loader.cpp @@ -0,0 +1,616 @@ +// +// Copyright 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 <cstring> +#include <iostream> +#include <fstream> + +#include <boost/asio/ip/address_v4.hpp> +#include <boost/assign.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/algorithm/string/erase.hpp> + +#include <uhd/config.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/exception.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/types/dict.hpp> + +#include "fw_common.h" +#include "usrp2_iface.hpp" +#include "usrp2_impl.hpp" + +typedef boost::asio::ip::address_v4 ip_v4; + +namespace fs = boost::filesystem; +using namespace boost::algorithm; + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +/* + * Constants + */ + +#define N200_FLASH_DATA_PACKET_SIZE 256 +#define N200_UDP_FW_UPDATE_PORT 49154 +#define UDP_TIMEOUT 0.5 + +#define N200_FW_MAX_SIZE_BYTES 31744 +#define N200_PROD_FW_IMAGE_ADDR  0x00300000 +#define N200_SAFE_FW_IMAGE_ADDR  0x003F0000 + +#define N200_FPGA_MAX_SIZE_BYTES 1572864 +#define N200_PROD_FPGA_IMAGE_ADDR  0x00180000 +#define N200_SAFE_FPGA_IMAGE_ADDR  0x00000000 + +/* + * Packet codes + */ +typedef enum { +    UNKNOWN = ' ', + +    N200_QUERY = 'a', +    N200_ACK = 'A', + +    GET_FLASH_INFO_CMD = 'f', +    GET_FLASH_INFO_ACK = 'F', + +    ERASE_FLASH_CMD = 'e', +    ERASE_FLASH_ACK = 'E', + +    CHECK_ERASING_DONE_CMD = 'd', +    DONE_ERASING_ACK = 'D', +    NOT_DONE_ERASING_ACK = 'B', + +    WRITE_FLASH_CMD = 'w', +    WRITE_FLASH_ACK = 'W', + +    READ_FLASH_CMD = 'r', +    READ_FLASH_ACK = 'R', + +    RESET_CMD = 's', +    RESET_ACK = 'S', + +    GET_HW_REV_CMD = 'v', +    GET_HW_REV_ACK = 'V', +} n200_fw_update_id_t; + +/* + * Mapping revision numbers to names + */ +static const uhd::dict<boost::uint32_t, std::string> n200_filename_map = boost::assign::map_list_of +    (0,      "n2xx")    // Is an N-Series, but the EEPROM value is invalid +    (0xa,    "n200_r3") +    (0x100a, "n200_r4") +    (0x10a,  "n210_r3") +    (0x110a, "n210_r4") +; + +/* + * Packet structure + */ +typedef struct { +    boost::uint32_t proto_ver; +    boost::uint32_t id; +    boost::uint32_t seq; +    union { +        boost::uint32_t ip_addr; +        boost::uint32_t hw_rev; +        struct { +            boost::uint32_t flash_addr; +            boost::uint32_t length; +            boost::uint8_t  data[256]; +        } flash_args; +        struct { +            boost::uint32_t sector_size_bytes; +            boost::uint32_t memory_size_bytes; +        } flash_info_args; +    } data; +} n200_fw_update_data_t; + +/* + * N-Series burn session + */ +typedef struct { +    bool               fw; +    bool               overwrite_safe; +    bool               reset; +    uhd::device_addr_t dev_addr; +    std::string        burn_type; +    std::string        filepath; +    boost::uint8_t     data_in[udp_simple::mtu]; +    boost::uint32_t    size; +    boost::uint32_t    max_size; +    boost::uint32_t    flash_addr; +    udp_simple::sptr   xport; +} n200_session_t; + +/*********************************************************************** + * uhd::image_loader functionality + **********************************************************************/ + +static void print_usrp2_error(const image_loader::image_loader_args_t &image_loader_args){ +    #ifdef UHD_PLATFORM_WIN32 +    std::string usrp2_card_burner_gui = "\""; +    const std::string nl = " ^\n    "; +    #else +    std::string usrp2_card_burner_gui = "sudo \""; +    const std::string nl = " \\\n    "; +    #endif + +    usrp2_card_burner_gui += find_utility("usrp2_card_burner_gui.py"); +    usrp2_card_burner_gui += "\""; + +    if(image_loader_args.load_firmware){ +        usrp2_card_burner_gui += str(boost::format("%s--fw=\"%s\"") +                                     % nl +                                     % ((image_loader_args.firmware_path == "") +                                             ? find_image_path("usrp2_fw.bin") +                                             : image_loader_args.firmware_path)); +    } +    if(image_loader_args.load_fpga){ +        usrp2_card_burner_gui += str(boost::format("%s--fpga=\"%s\"") +                                     % nl +                                     % ((image_loader_args.fpga_path == "") +                                             ? find_image_path("usrp2_fpga.bin") +                                             : image_loader_args.fpga_path)); +    } + +    throw uhd::runtime_error(str(boost::format("The specified device is a USRP2, which is not supported by this utility.\n" +                                               "Instead, plug the device's SD card into your machine and run this command:\n\n" +                                               "%s" +                                              ) % usrp2_card_burner_gui)); +} + +/* + * Ethernet communication functions + */ +static UHD_INLINE size_t n200_send_and_recv(udp_simple::sptr xport, +                                            n200_fw_update_id_t pkt_code, +                                            n200_fw_update_data_t *pkt_out, +                                            boost::uint8_t* data){ +    pkt_out->proto_ver = htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM); +    pkt_out->id = htonx<boost::uint32_t>(pkt_code); +    xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out))); +    return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT); +} + +static UHD_INLINE bool n200_response_matches(const n200_fw_update_data_t *pkt_in, +                                             n200_fw_update_id_t pkt_code, +                                             size_t len){ +    return (len > offsetof(n200_fw_update_data_t, data) and +            ntohl(pkt_in->id) == pkt_code); +} + +static uhd::device_addr_t n200_find(const image_loader::image_loader_args_t &image_loader_args){ +    bool user_specified = image_loader_args.args.has_key("addr") or +                          image_loader_args.args.has_key("serial") or +                          image_loader_args.args.has_key("name"); + +    uhd::device_addrs_t found = usrp2_find(image_loader_args.args); +    if(found.size() > 0){ +        uhd::device_addr_t ret = found[0]; + +        /* +         * Make sure the device found is an N-Series and not a USRP2. A USRP2 +         * will not respond to this query. If the user supplied specific +         * arguments that led to a USRP2, throw an error. +         */ +        udp_simple::sptr rev_xport = udp_simple::make_connected( +                                         ret["addr"], +                                         BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT) +                                     ); + +        n200_fw_update_data_t pkt_out; +        boost::uint8_t data_in[udp_simple::mtu]; +        const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(data_in); + +        size_t len = n200_send_and_recv(rev_xport, GET_HW_REV_CMD, &pkt_out, data_in); +        if(n200_response_matches(pkt_in, GET_HW_REV_ACK, len)){ +            boost::uint32_t rev = ntohl(pkt_in->data.hw_rev); +            ret["hw_rev"] = n200_filename_map.get(rev, "n2xx"); +            return ret; +        } +        else if(len > offsetof(n200_fw_update_data_t, data) and ntohl(pkt_in->id) != GET_HW_REV_ACK){ +            throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.") +                                         % ntohl(pkt_in->id))); +        } +        else if(user_specified){ +            // At this point, we haven't received any response, so assume it's a USRP2 +            print_usrp2_error(image_loader_args); +        } +    } + +    return uhd::device_addr_t(); +} + +/* + * Validate and read firmware image + */ +static void n200_validate_firmware_image(n200_session_t &session){ +    if(not fs::exists(session.filepath)){ +        throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") +                                     % session.filepath)); +    } + +    session.size     = fs::file_size(session.filepath); +    session.max_size = N200_FW_MAX_SIZE_BYTES; + +    if(session.size > session.max_size){ +        throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") +                                     % session.size % session.max_size)); +    } + +    // File must have proper header +    std::ifstream image_file(session.filepath.c_str(), std::ios::binary); +    boost::uint8_t test_bytes[4]; +    image_file.seekg(0, std::ios::beg); +    image_file.read((char*)test_bytes,4); +    image_file.close(); +    for(int i = 0; i < 4; i++) if(test_bytes[i] != 11){ +        throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid firmware image.") +                                     % session.filepath)); +    } +} + +/* + * Validate and validate FPGA image + */ +static void n200_validate_fpga_image(n200_session_t &session){ +    if(not fs::exists(session.filepath)){ +        throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") +                                     % session.filepath)); +    } + +    session.size     = fs::file_size(session.filepath); +    session.max_size = N200_FPGA_MAX_SIZE_BYTES; + +    if(session.size > session.max_size){ +        throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") +                                     % session.size % session.max_size)); +    } + +    // File must have proper header +    std::ifstream image_file(session.filepath.c_str(), std::ios::binary); +    boost::uint8_t test_bytes[63]; +    image_file.seekg(0, std::ios::beg); +    image_file.read((char*)test_bytes, 63); +    bool is_good = false; +    for(int i = 0; i < 63; i++){ +        if(test_bytes[i] == 255) continue; +        else if(test_bytes[i] == 170 and +                test_bytes[i+1] == 153){ +            is_good = true; +            break; +        } +    } +    image_file.close(); +    if(not is_good){ +        throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid FPGA image.") +                                     % session.filepath)); +    } +} + +/* + * Set up a session for burning an N-Series image. This session info + * will be passed into the erase, burn, and verify functions. + */ +static void n200_setup_session(n200_session_t &session, +                               const image_loader::image_loader_args_t &image_loader_args, +                               bool fw){ + + +    session.fw = fw; +    session.reset = image_loader_args.args.has_key("reset"); + +    /* +     * If no filepath is given, attempt to determine the default image by +     * querying the device for its revision. If the device has a corrupt +     * EEPROM or is otherwise unable to provide its revision, this is +     * impossible, and the user must manually provide a firmware file. +     */ +    if((session.fw and image_loader_args.firmware_path == "") or +       image_loader_args.fpga_path == ""){ +        if(session.dev_addr["hw_rev"] == "n2xx"){ +            throw uhd::runtime_error("This device's revision cannot be determined. " +                                     "You must manually specify a filepath."); +        } +        else{ +            session.filepath = session.fw ? find_image_path(str(boost::format("usrp_%s_fw.bin") +                                                                % erase_tail_copy(session.dev_addr["hw_rev"],3))) +                                          : find_image_path(str(boost::format("usrp_%s_fpga.bin") +                                                                % session.dev_addr["hw_rev"])); +        } +    } +    else{ +        session.filepath = session.fw ? image_loader_args.firmware_path +                                      : image_loader_args.fpga_path; +    } +    if(session.fw) n200_validate_firmware_image(session); +    else           n200_validate_fpga_image(session); + +    session.overwrite_safe = image_loader_args.args.has_key("overwrite-safe"); +    if(session.overwrite_safe){ +        session.flash_addr = session.fw ? N200_SAFE_FW_IMAGE_ADDR +                                        : N200_SAFE_FPGA_IMAGE_ADDR; +        session.burn_type = session.fw ? "firmware safe" +                                       : "FPGA safe"; +    } +    else{ +        session.flash_addr = session.fw ? N200_PROD_FW_IMAGE_ADDR +                                        : N200_PROD_FPGA_IMAGE_ADDR; +        session.burn_type = session.fw ? "firmware" +                                       : "FPGA"; +    } + +    session.xport = udp_simple::make_connected(session.dev_addr["addr"], +                                               BOOST_STRINGIZE(N200_UDP_FW_UPDATE_PORT)); +} + +static void n200_erase_image(n200_session_t &session){ + +    // UDP receive buffer +    n200_fw_update_data_t pkt_out; +    const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); + +    // Setting up UDP packet +    pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(session.flash_addr); +    pkt_out.data.flash_args.length = htonx<boost::uint32_t>(session.size); + +    // Begin erasing +    size_t len = n200_send_and_recv(session.xport, ERASE_FLASH_CMD, &pkt_out, session.data_in); +    if(n200_response_matches(pkt_in, ERASE_FLASH_ACK, len)){ +        std::cout << boost::format("-- Erasing %s image...") % session.burn_type << std::flush; +    } +    else if(len < offsetof(n200_fw_update_data_t, data)){ +        std::cout << "failed." << std::endl; +        throw uhd::runtime_error("Timed out waiting for reply from device."); +    } +    else if(ntohl(pkt_in->id) != ERASE_FLASH_ACK){ +        std::cout << "failed." << std::endl; +        throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") +                                     % ntohl(pkt_in->id))); +    } +    else{ +        std::cout << "failed." << std::endl; +        throw uhd::runtime_error("Did not receive response from device."); +    } + +    // Check for erase completion +    while(true){ +        len = n200_send_and_recv(session.xport, CHECK_ERASING_DONE_CMD, &pkt_out, session.data_in); +        if(n200_response_matches(pkt_in, DONE_ERASING_ACK, len)){ +            std::cout << "successful." << std::endl; +            break; +        } +        else if(len < offsetof(n200_fw_update_data_t, data)){ +            std::cout << "failed." << std::endl; +            throw uhd::runtime_error("Timed out waiting for reply from device."); +        } +        else if(ntohl(pkt_in->id) != NOT_DONE_ERASING_ACK){ +            std::cout << "failed." << std::endl; +            throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") +                                         % ntohl(pkt_in->id))); +        } +    } +} + +static void n200_write_image(n200_session_t &session){ + +    // UDP receive buffer +    n200_fw_update_data_t pkt_out; +    const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); +    size_t len = 0; + +    // Write image +    std::ifstream image(session.filepath.c_str(), std::ios::binary); +    boost::uint32_t current_addr = session.flash_addr; +    pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE); +    for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){ +        pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); +        memset(pkt_out.data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE); +        image.read((char*)pkt_out.data.flash_args.data, N200_FLASH_DATA_PACKET_SIZE); + +        len = n200_send_and_recv(session.xport, WRITE_FLASH_CMD, &pkt_out, session.data_in); +        if(n200_response_matches(pkt_in, WRITE_FLASH_ACK, len)){ +            std::cout << boost::format("\r-- Writing %s image (%d%%)") +                             % session.burn_type +                             % int((double(current_addr-session.flash_addr)/double(session.size))*100) +                      << std::flush; +        } +        else if(len < offsetof(n200_fw_update_data_t, data)){ +            image.close(); +            std::cout << boost::format("\r--Writing %s image..failed at %d%%.") +                             % session.burn_type +                             % int((double(current_addr-session.flash_addr)/double(session.size))*100) +                      << std::endl; +            throw uhd::runtime_error("Timed out waiting for reply from device."); +        } +        else if(ntohl(pkt_in->id) != WRITE_FLASH_ACK){ +            image.close(); +            std::cout << boost::format("\r--Writing %s image..failed at %d%%.") +                             % session.burn_type +                             % int((double(current_addr-session.flash_addr)/double(session.size))*100) +                      << std::endl; +            throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") +                                             % ntohl(pkt_in->id))); +        } + +        current_addr += N200_FLASH_DATA_PACKET_SIZE; +    } +    std::cout << boost::format("\r-- Writing %s image...successful.") +                     % session.burn_type +              << std::endl; + +    image.close(); +} + +static void n200_verify_image(n200_session_t &session){ + +    // UDP receive buffer +    n200_fw_update_data_t pkt_out; +    const n200_fw_update_data_t *pkt_in = reinterpret_cast<const n200_fw_update_data_t*>(session.data_in); +    size_t len = 0; + +    // Read and verify image +    std::ifstream image(session.filepath.c_str(), std::ios::binary); +    boost::uint8_t image_part[N200_FLASH_DATA_PACKET_SIZE]; +    boost::uint32_t current_addr = session.flash_addr; +    pkt_out.data.flash_args.length = htonx<boost::uint32_t>(N200_FLASH_DATA_PACKET_SIZE); +    boost::uint16_t cmp_len = 0; +    for(size_t i = 0; i < ((session.size/N200_FLASH_DATA_PACKET_SIZE)+1); i++){ +        memset(image_part, 0x0, N200_FLASH_DATA_PACKET_SIZE); +        memset((void*)pkt_in->data.flash_args.data, 0x0, N200_FLASH_DATA_PACKET_SIZE); + +        pkt_out.data.flash_args.flash_addr = htonx<boost::uint32_t>(current_addr); +        image.read((char*)image_part, N200_FLASH_DATA_PACKET_SIZE); +        cmp_len = image.gcount(); + +        len = n200_send_and_recv(session.xport, READ_FLASH_CMD, &pkt_out, session.data_in); +        if(n200_response_matches(pkt_in, READ_FLASH_ACK, len)){ +            std::cout << boost::format("\r-- Verifying %s image (%d%%)") +                             % session.burn_type +                             % int((double(current_addr-session.flash_addr)/double(session.size))*100) +                      << std::flush; + +            if(memcmp(image_part, pkt_in->data.flash_args.data, cmp_len)){ +                std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") +                                 % session.burn_type +                                 % int((double(current_addr-session.flash_addr)/double(session.size))*100) +                          << std::endl; +                throw uhd::runtime_error(str(boost::format("Failed to verify %s image.") +                                                 % session.burn_type)); +            } +        } +        else if(len < offsetof(n200_fw_update_data_t, data)){ +            image.close(); +            std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") +                             % session.burn_type +                             % int((double(current_addr-session.flash_addr)/double(session.size))*100) +                      << std::endl; +            throw uhd::runtime_error("Timed out waiting for reply from device."); +        } +        else if(ntohl(pkt_in->id) != READ_FLASH_ACK){ +            image.close(); +            std::cout << boost::format("\r-- Verifying %s image...failed at %d%%.") +                             % session.burn_type +                             % int((double(current_addr-session.flash_addr)/double(session.size))*100) +                      << std::endl; +            throw uhd::runtime_error(str(boost::format("Received invalid reply %d from device.\n") +                                         % ntohl(pkt_in->id))); +        } + +        current_addr += N200_FLASH_DATA_PACKET_SIZE; +    } +    std::cout << boost::format("\r-- Verifying %s image...successful.") % session.burn_type +              << std::endl; + +    image.close(); +} + +static void n200_reset(n200_session_t &session){ + +    // UDP receive buffer +    n200_fw_update_data_t pkt_out; + +    // There should be no response +    std::cout << "-- Resetting device..." << std::flush; +    size_t len = n200_send_and_recv(session.xport, RESET_CMD, &pkt_out, session.data_in); +    if(len > 0){ +        std::cout << "failed." << std::endl; +        throw uhd::runtime_error("Failed to reset N200."); +    } +    std::cout << "successful." << std::endl; +} + +// n210_r4 -> N210 r4 +static std::string nice_name(const std::string &fw_rev){ +    std::string ret = fw_rev; +    ret[0] = ::toupper(ret[0]); + +    size_t pos = 0; +    if((pos = fw_rev.find("_")) != std::string::npos){ +        ret[pos] = ' '; +    } + +    return ret; +} + +static bool n200_image_loader(const image_loader::image_loader_args_t &image_loader_args){ +    // See if any N2x0 with the given args is found +    // This will throw if specific args lead to a USRP2 +    n200_session_t session; +    session.dev_addr = n200_find(image_loader_args); +    if(session.dev_addr.size() == 0 or (!image_loader_args.load_firmware and !image_loader_args.load_fpga)){ +        return false; +    } + +    std::cout << boost::format("Unit: USRP %s (%s, %s)") +                 % nice_name(session.dev_addr.get("hw_rev")) +                 % session.dev_addr.get("serial") +                 % session.dev_addr.get("addr") +             << std::endl; + +    if(image_loader_args.load_firmware){ +        n200_setup_session(session, +                           image_loader_args, +                           true +                          ); + +        std::cout << "Firmware image: " << session.filepath << std::endl; + +        n200_erase_image(session); +        n200_write_image(session); +        n200_verify_image(session); +        if(session.reset and !image_loader_args.load_fpga){ +            n200_reset(session); +        } +    } +    if(image_loader_args.load_fpga){ +        n200_setup_session(session, +                           image_loader_args, +                           false +                          ); + +        std::cout << "FPGA image: " << session.filepath << std::endl; + +        n200_erase_image(session); +        n200_write_image(session); +        n200_verify_image(session); +        if(session.reset){ +            n200_reset(session); +        } +    } + +    return true; +} + +UHD_STATIC_BLOCK(register_n200_image_loader){ +    std::string recovery_instructions = "Aborting. Your USRP-N Series unit will likely be unusable.\n" +                                        "Refer to http://files.ettus.com/manual/page_usrp2.html#usrp2_loadflash_brick\n" +                                        "for details on restoring your device."; + +    image_loader::register_image_loader("usrp2", n200_image_loader, recovery_instructions); +} diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 1d41173f8..2b382ae38 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -387,15 +387,15 @@ public:          //create the burner commands          if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){ -            const std::string card_burner = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp2_card_burner_gui.py").string(); -            const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); +            const std::string card_burner = uhd::find_utility("usrp2_card_burner_gui.py"); +            const std::string card_burner_cmd = str(boost::format(" %s\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path);              return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % card_burner_cmd);          }          else{              const std::string addr = _ctrl_transport->get_recv_addr(); -            const std::string net_burner_path = (fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "utils" / "usrp_n2xx_simple_net_burner").string(); -            const std::string net_burner_cmd = str(boost::format("\"%s\" %s--addr=\"%s\"") % net_burner_path % ml % addr); -            return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % net_burner_cmd); +            const std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string(); +            const std::string image_loader_cmd = str(boost::format(" \"%s\" %s--args=\"type=usrp2,addr=%s\"") % image_loader_path % ml % addr); +            return str(boost::format("%s\n%s") % print_utility_error("uhd_images_downloader.py") % image_loader_cmd);          }      } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 1acc1dad3..6073ec1c0 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -48,7 +48,7 @@ static const size_t DEFAULT_NUM_FRAMES = 32;  /***********************************************************************   * Discovery over the udp transport   **********************************************************************/ -static device_addrs_t usrp2_find(const device_addr_t &hint_){ +device_addrs_t usrp2_find(const device_addr_t &hint_){      //handle the multi-device discovery      device_addrs_t hints = separate_device_addr(hint_);      if (hints.size() > 1){ diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 701403029..07cd98b4c 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -42,6 +42,7 @@  #include <uhd/transport/vrt_if_packet.hpp>  #include <uhd/transport/udp_simple.hpp>  #include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/types/device_addr.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <boost/weak_ptr.hpp> @@ -55,6 +56,8 @@ static const boost::uint32_t USRP2_TX_ASYNC_SID = 2;  static const boost::uint32_t USRP2_RX_SID_BASE = 3;  static const std::string USRP2_EEPROM_MAP_KEY = "N100"; +uhd::device_addrs_t usrp2_find(const uhd::device_addr_t &hint_); +  //! Make a usrp2 dboard interface.  uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface(      uhd::timed_wb_iface::sptr wb_iface, diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt index a588f901b..9a8601452 100644 --- a/host/lib/usrp/x300/CMakeLists.txt +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -1,5 +1,5 @@  # -# Copyright 2013 Ettus Research LLC +# Copyright 2013,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 @@ -34,5 +34,8 @@ IF(ENABLE_X300)          ${CMAKE_CURRENT_SOURCE_DIR}/x300_io_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_dac_utils.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c      )  ENDIF(ENABLE_X300) diff --git a/host/lib/usrp/x300/cdecode.c b/host/lib/usrp/x300/cdecode.c new file mode 100644 index 000000000..1d09cbe22 --- /dev/null +++ b/host/lib/usrp/x300/cdecode.c @@ -0,0 +1,80 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include "cdecode.h" + +int base64_decode_value(char value_in){ +    static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; +    static const char decoding_size = sizeof(decoding); +    value_in -= 43; +    if ((signed char)value_in < 0 || value_in > decoding_size) return -1; +    return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in){ +    state_in->step = step_a; +    state_in->plainchar = 0; +} + +size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in){ +    const char* codechar = code_in; +    char* plainchar = plaintext_out; +    char fragment; +     +    *plainchar = state_in->plainchar; +     +    switch (state_in->step){ +        while (1){ +            case step_a: +                do{ +                    if (codechar == code_in+length_in){ +                        state_in->step = step_a; +                        state_in->plainchar = *plainchar; +                        return plainchar - plaintext_out; +                    } +                    fragment = (char)base64_decode_value(*codechar++); +                } while ((signed char)fragment < 0); +                *plainchar = (fragment & 0x03f) << 2; + +            case step_b: +                do{ +                    if (codechar == code_in+length_in){ +                        state_in->step = step_b; +                        state_in->plainchar = *plainchar; +                        return plainchar - plaintext_out; +                    } +                    fragment = (char)base64_decode_value(*codechar++); +                } while ((signed char)fragment < 0); +                *plainchar++ |= (fragment & 0x030) >> 4; +                *plainchar    = (fragment & 0x00f) << 4; +            case step_c: +                do{ +                    if (codechar == code_in+length_in) +                    { +                        state_in->step = step_c; +                        state_in->plainchar = *plainchar; +                        return plainchar - plaintext_out; +                    } +                    fragment = (char)base64_decode_value(*codechar++); +                } while ((signed char)fragment < 0); +                *plainchar++ |= (fragment & 0x03c) >> 2; +                *plainchar    = (fragment & 0x003) << 6; +            case step_d: +                do{ +                    if (codechar == code_in+length_in){ +                        state_in->step = step_d; +                        state_in->plainchar = *plainchar; +                        return plainchar - plaintext_out; +                    } +                    fragment = (char)base64_decode_value(*codechar++); +                } while ((signed char)fragment < 0); +                *plainchar++   |= (fragment & 0x03f); +        } +    } +    /* control should not reach here */ +    return plainchar - plaintext_out; +} diff --git a/host/lib/usrp/x300/cdecode.h b/host/lib/usrp/x300/cdecode.h new file mode 100644 index 000000000..b8da55aa1 --- /dev/null +++ b/host/lib/usrp/x300/cdecode.h @@ -0,0 +1,36 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> + +typedef enum{ +    step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct{ +    base64_decodestep step; +    char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +size_t base64_decode_block(const char* code_in, const size_t length_in, char* plaintext_out, base64_decodestate* state_in); + +#ifdef __cplusplus +} +#endif + +#endif /* BASE64_CDECODE_H */ diff --git a/host/lib/usrp/x300/x300_adc_ctrl.cpp b/host/lib/usrp/x300/x300_adc_ctrl.cpp index b0e4e4b95..ce6102b35 100644 --- a/host/lib/usrp/x300/x300_adc_ctrl.cpp +++ b/host/lib/usrp/x300/x300_adc_ctrl.cpp @@ -55,8 +55,8 @@ public:          _ads62p48_regs.lvds_cmos = ads62p48_regs_t::LVDS_CMOS_DDR_LVDS;          _ads62p48_regs.channel_control = ads62p48_regs_t::CHANNEL_CONTROL_INDEPENDENT;          _ads62p48_regs.data_format = ads62p48_regs_t::DATA_FORMAT_2S_COMPLIMENT; -        _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS7_26; -        _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS7_26; +        _ads62p48_regs.clk_out_pos_edge = ads62p48_regs_t::CLK_OUT_POS_EDGE_MINUS4_26; +        _ads62p48_regs.clk_out_neg_edge = ads62p48_regs_t::CLK_OUT_NEG_EDGE_MINUS4_26;          this->send_ads62p48_reg(0); diff --git a/host/lib/usrp/x300/x300_adc_dac_utils.cpp b/host/lib/usrp/x300/x300_adc_dac_utils.cpp new file mode 100644 index 000000000..2dadea26e --- /dev/null +++ b/host/lib/usrp/x300/x300_adc_dac_utils.cpp @@ -0,0 +1,412 @@ +// +// Copyright 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 "x300_impl.hpp" +#include <boost/date_time/posix_time/posix_time_io.hpp> + +/*********************************************************************** + * DAC: Reset and synchronization operations + **********************************************************************/ + +void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) +{ +    if (radios.size() < 2) return;  //Nothing to synchronize + +    //**PRECONDITION** +    //This function assumes that all the VITA times in "radios" are synchronized +    //to a common reference. Currently, this function is called in get_tx_stream +    //which also has the same precondition. + +    //Reinitialize and resync all DACs +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->dac->reset_and_resync(); +    } + +    //Get a rough estimate of the cumulative command latency +    boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call +    } +    boost::posix_time::time_duration t_elapsed = +        boost::posix_time::microsec_clock::local_time() - t_start; + +    //Add 100% of headroom + uncertaintly to the command time +    boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; + +    //Pick radios[0] as the time reference. +    uhd::time_spec_t sync_time = +        radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); + +    //Send the sync command +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->ctrl->set_time(sync_time); +        radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1);    //Arm FRAMEP/N sync pulse +        radios[i]->ctrl->set_time(uhd::time_spec_t(0.0));   //Clear command time +    } + +    //Wait and check status +    boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->dac->verify_sync(); +    } +} + +/*********************************************************************** + * ADC: Self-test operations + **********************************************************************/ + +static void check_adc(uhd::wb_iface::sptr iface, const boost::uint32_t val, const boost::uint32_t i) +{ +    boost::uint32_t adc_rb = iface->peek32(RB32_RX); +    adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA +    if (val != adc_rb) { +        throw uhd::runtime_error( +            (boost::format("ADC self-test failed for Radio%d. (Exp=0x%x, Got=0x%x)")%i%val%adc_rb).str()); +    } +} + +void x300_impl::self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms) { +    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +        radio_perifs_t &perif = mb.radio_perifs[r]; + +        //First test basic patterns +        perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc,r); +        perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000,r); +        perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000,r); +        perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc,r); +        for (size_t k = 0; k < 14; k++) +        { +            perif.adc->set_test_word("zeros", "custom", 1 << k); +            check_adc(perif.ctrl, 1 << (k+2),r); +        } +        for (size_t k = 0; k < 14; k++) +        { +            perif.adc->set_test_word("custom", "zeros", 1 << k); +            check_adc(perif.ctrl, 1 << (k+18),r); +        } + +        //Turn on ramp pattern test +        perif.adc->set_test_word("ramp", "ramp"); +        perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +        perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +    } +    boost::this_thread::sleep(boost::posix_time::milliseconds(ramp_time_ms)); + +    bool passed = true; +    std::string status_str; +    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +        radio_perifs_t &perif = mb.radio_perifs[r]; +        perif.misc_ins->refresh(); + +        std::string i_status, q_status; +        if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) +            if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR)) +                i_status = "Bit Errors!"; +            else +                i_status = "Good"; +        else +            i_status = "Not Locked!"; + +        if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) +            if (perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR)) +                q_status = "Bit Errors!"; +            else +                q_status = "Good"; +        else +            q_status = "Not Locked!"; + +        passed = passed && (i_status == "Good") && (q_status == "Good"); +        status_str += (boost::format(", ADC%d_I=%s, ADC%d_Q=%s")%r%i_status%r%q_status).str(); + +        //Return to normal mode +        perif.adc->set_test_word("normal", "normal"); +    } + +    if (not passed) { +        throw uhd::runtime_error( +            (boost::format("ADC self-test failed! Ramp checker status: {%s}")%status_str.substr(2)).str()); +    } +} + +void x300_impl::extended_adc_test(mboard_members_t& mb, double duration_s) +{ +    static const size_t SECS_PER_ITER = 5; +    UHD_MSG(status) << boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...\n") +        % duration_s % SECS_PER_ITER; + +    size_t num_iters = static_cast<size_t>(ceil(duration_s/SECS_PER_ITER)); +    size_t num_failures = 0; +    for (size_t iter = 0; iter < num_iters; iter++) { +        //Print date and time +        boost::posix_time::time_facet *facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S"); +        std::ostringstream time_strm; +        time_strm.imbue(std::locale(std::locale::classic(), facet)); +        time_strm << boost::posix_time::second_clock::local_time(); +        //Run self-test +        UHD_MSG(status) << boost::format("-- [%s] Iteration %06d... ") % time_strm.str() % (iter+1); +        try { +            self_test_adcs(mb, SECS_PER_ITER*1000); +            UHD_MSG(status) << "passed" << std::endl; +        } catch(std::exception &e) { +            num_failures++; +            UHD_MSG(status) << e.what() << std::endl; +        } + +    } +    if (num_failures == 0) { +        UHD_MSG(status) << "Extended ADC Self-Test PASSED\n"; +    } else { +        throw uhd::runtime_error( +                (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)\n") % num_failures % num_iters).str()); +    } +} + +/*********************************************************************** + * ADC: Self-calibration operations + **********************************************************************/ + +void x300_impl::self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status) +{ +    radio_perifs_t& perif = mb.radio_perifs[radio_i]; +    if (print_status) UHD_MSG(status) << "Running ADC capture delay self-cal..." << std::flush; + +    static const boost::uint32_t NUM_DELAY_STEPS = 32;   //The IDELAYE2 element has 32 steps +    static const boost::uint32_t NUM_RETRIES     = 2;    //Retry self-cal if it fails in warmup situations +    static const boost::int32_t  MIN_WINDOW_LEN  = 4; + +    boost::int32_t win_start = -1, win_stop = -1; +    boost::uint32_t iter = 0; +    while (iter++ < NUM_RETRIES) { +        for (boost::uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) { +            //Apply delay +            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, dly_tap); +            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1); +            perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0); + +            boost::uint32_t err_code = 0; + +            // -- Test I Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            perif.adc->set_test_word("ramp", "ones"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            //10ms @ 200MHz = 2 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +            if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_I_LOCKED)) { +                err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_I_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            // -- Test Q Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            perif.adc->set_test_word("ones", "ramp"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +            perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            //10ms @ 200MHz = 2 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +            if (perif.misc_ins->read(radio_misc_ins_reg::ADC_CHECKER0_Q_LOCKED)) { +                err_code += perif.misc_ins->get(radio_misc_ins_reg::ADC_CHECKER0_Q_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            if (err_code == 0) { +                if (win_start == -1) {      //This is the first window +                    win_start = dly_tap; +                    win_stop = dly_tap; +                } else {                    //We are extending the window +                    win_stop = dly_tap; +                } +            } else { +                if (win_start != -1) {      //A valid window turned invalid +                    if (win_stop - win_start >= MIN_WINDOW_LEN) { +                        break;              //Valid window found +                    } else { +                        win_start = -1;     //Reset window +                    } +                } +            } +            //UHD_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % dly_tap % err_code); +        } + +        //Retry the self-cal if it fails +        if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN) && iter < NUM_RETRIES /*not last iteration*/) { +            win_start = -1; +            win_stop = -1; +            boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); +        } else { +            break; +        } +    } +    perif.adc->set_test_word("normal", "normal"); +    perif.misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); + +    if (win_start == -1) { +        throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Convergence error."); +    } + +    if (win_stop-win_start < MIN_WINDOW_LEN) { +        throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Valid window too narrow."); +    } + +    boost::uint32_t ideal_tap = (win_stop + win_start) / 2; +    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_VAL, ideal_tap); +    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 1); +    perif.misc_outs->write(radio_misc_outs_reg::ADC_DATA_DLY_STB, 0); + +    if (print_status) { +        double tap_delay = (1.0e12 / mb.clock->get_master_clock_rate()) / (2*32); //in ps +        UHD_MSG(status) << boost::format(" done (Tap=%d, Window=%d, TapDelay=%.3fps, Iter=%d)\n") % ideal_tap % (win_stop-win_start) % tap_delay % iter; +    } +} + +double x300_impl::self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay) +{ +    UHD_MSG(status) << "Running ADC transfer delay self-cal: " << std::flush; + +    //Effective resolution of the self-cal. +    static const size_t NUM_DELAY_STEPS = 100; + +    double master_clk_period = (1.0e9 / mb.clock->get_master_clock_rate()); //in ns +    double delay_start = 0.0; +    double delay_range = 2 * master_clk_period; +    double delay_incr = delay_range / NUM_DELAY_STEPS; + +    UHD_MSG(status) << "Measuring..." << std::flush; +    double cached_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_ADC0); +    double fpga_clk_delay = mb.clock->get_clock_delay(X300_CLOCK_WHICH_FPGA); + +    //Iterate through several values of delays and measure ADC data integrity +    std::vector< std::pair<double,bool> > results; +    for (size_t i = 0; i < NUM_DELAY_STEPS; i++) { +        //Delay the ADC clock (will set both Ch0 and Ch1 delays) +        double delay = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr*i + delay_start); +        wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1); + +        boost::uint32_t err_code = 0; +        for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +            //Test each channel (I and Q) individually so as to not accidentally trigger +            //on the data from the other channel if there is a swap + +            // -- Test I Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            mb.radio_perifs[r].adc->set_test_word("ramp", "ones"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            //50ms @ 200MHz = 10 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(50)); +            if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_I_LOCKED)) { +                err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_I_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            // -- Test Q Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            mb.radio_perifs[r].adc->set_test_word("ones", "ramp"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +            mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 1); +            //50ms @ 200MHz = 10 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(50)); +            if (mb.radio_perifs[r].misc_ins->read(radio_misc_ins_reg::ADC_CHECKER1_Q_LOCKED)) { +                err_code += mb.radio_perifs[r].misc_ins->get(radio_misc_ins_reg::ADC_CHECKER1_Q_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } +        } +        //UHD_MSG(status) << (boost::format("XferDelay=%fns, Error=%d\n") % delay % err_code); +        results.push_back(std::pair<double,bool>(delay, err_code==0)); +    } + +    //Calculate the valid window +    int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1; +    for (size_t i = 0; i < results.size(); i++) { +        std::pair<double,bool>& item = results[i]; +        if (item.second) {  //If data is stable +            if (cur_start_idx == -1) {  //This is the first window +                cur_start_idx = i; +                cur_stop_idx = i; +            } else {                    //We are extending the window +                cur_stop_idx = i; +            } +        } else { +            if (cur_start_idx == -1) {  //We haven't yet seen valid data +                //Do nothing +            } else if (win_start_idx == -1) {   //We passed the first valid window +                win_start_idx = cur_start_idx; +                win_stop_idx = cur_stop_idx; +            } else {                    //Update cached window if current window is larger +                double cur_win_len = results[cur_stop_idx].first - results[cur_start_idx].first; +                double cached_win_len = results[win_stop_idx].first - results[win_start_idx].first; +                if (cur_win_len > cached_win_len) { +                    win_start_idx = cur_start_idx; +                    win_stop_idx = cur_stop_idx; +                } +            } +            //Reset current window +            cur_start_idx = -1; +            cur_stop_idx = -1; +        } +    } +    if (win_start_idx == -1) { +        throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Convergence error."); +    } + +    double win_center = (results[win_stop_idx].first + results[win_start_idx].first) / 2.0; +    double win_length = results[win_stop_idx].first - results[win_start_idx].first; +    if (win_length < master_clk_period/4) { +        throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Valid window too narrow."); +    } + +    //Cycle slip the relative delay by a clock cycle to prevent sample misalignment +    //fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need +    bool cycle_slip = (win_center-fpga_clk_delay >= master_clk_period); +    if (cycle_slip) { +        win_center -= master_clk_period; +    } + +    if (apply_delay) { +        UHD_MSG(status) << "Validating..." << std::flush; +        //Apply delay +        win_center = mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, win_center);  //Sets ADC0 and ADC1 +        wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, 0.1); +        //Validate +        self_test_adcs(mb, 2000); +    } else { +        //Restore delay +        mb.clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, cached_clk_delay);  //Sets ADC0 and ADC1 +    } + +    //Teardown +    for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +        mb.radio_perifs[r].adc->set_test_word("normal", "normal"); +        mb.radio_perifs[r].misc_outs->write(radio_misc_outs_reg::ADC_CHECKER_ENABLED, 0); +    } +    UHD_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") % +        (win_center-fpga_clk_delay) % (cycle_slip?" +cyc":"") % win_length); + +    return win_center; +} diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index 6450686dd..d5687f5cc 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -21,6 +21,7 @@  #include <uhd/utils/math.hpp>  #include <boost/cstdint.hpp>  #include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp>  #include <stdexcept>  #include <cmath>  #include <cstdlib> @@ -29,6 +30,30 @@ static const double X300_REF_CLK_OUT_RATE  = 10e6;  static const boost::uint16_t X300_MAX_CLKOUT_DIV = 1045;  static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6; +struct x300_clk_delays { +    x300_clk_delays() : +        fpga_dly_ns(0.0),adc_dly_ns(0.0),dac_dly_ns(0.0),db_rx_dly_ns(0.0),db_tx_dly_ns(0.0) +    {} +    x300_clk_delays(double fpga, double adc, double dac, double db_rx, double db_tx) : +        fpga_dly_ns(fpga),adc_dly_ns(adc),dac_dly_ns(dac),db_rx_dly_ns(db_rx),db_tx_dly_ns(db_tx) +    {} + +    double fpga_dly_ns; +    double adc_dly_ns; +    double dac_dly_ns; +    double db_rx_dly_ns; +    double db_tx_dly_ns; +}; + +// Tune the FPGA->ADC clock delay to ensure a safe ADC_SSCLK -> RADIO_CLK crossing. +// If the FPGA_CLK is delayed, we also need to delay the reference clocks going to the DAC +// because the data interface clock is generated from FPGA_CLK. +static const x300_clk_delays X300_REV0_6_CLK_DELAYS = x300_clk_delays( +    /*fpga=*/0.000, /*adc=*/2.200, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000); + +static const x300_clk_delays X300_REV7_CLK_DELAYS = x300_clk_delays( +    /*fpga=*/0.000, /*adc=*/0.000, /*dac=*/0.000, /*db_rx=*/0.000, /*db_tx=*/0.000); +  using namespace uhd;  x300_clock_ctrl::~x300_clock_ctrl(void){ @@ -213,6 +238,187 @@ public:          _spiface->write_spi(_slaveno, spi_config_t::EDGE_RISE, data,32);      } +    double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) { +        //All dividers have are delayed by 5 taps by default. The delay +        //set by this function is relative to the 5 tap delay +        static const boost::uint16_t DDLY_MIN_TAPS  = 5; +        static const boost::uint16_t DDLY_MAX_TAPS  = 522;  //Extended mode + +        //The resolution and range of the analog delay is fixed +        static const double ADLY_RES_NS = 0.025; +        static const double ADLY_MIN_NS = 0.500; +        static const double ADLY_MAX_NS = 0.975; + +        //Each digital tap delays the clock by one VCO period +        double vco_period_ns = 1.0e9/_vco_freq; +        double half_vco_period_ns = vco_period_ns/2.0; + +        //Implement as much of the requested delay using digital taps. Whatever is leftover +        //will be made up using the analog delay element and the half-cycle digital tap. +        //A caveat here is that the analog delay starts at ADLY_MIN_NS, so we need to back off +        //by that much when coming up with the digital taps so that the difference can be made +        //up using the analog delay. +        boost::uint16_t ddly_taps = 0; +        if (delay_ns < ADLY_MIN_NS) { +            ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns)/vco_period_ns)); +        } else { +            ddly_taps = static_cast<boost::uint16_t>(std::floor((delay_ns-ADLY_MIN_NS)/vco_period_ns)); +        } +        double leftover_delay = delay_ns - (vco_period_ns * ddly_taps); + +        //Compute settings +        boost::uint16_t ddly_value    = ddly_taps + DDLY_MIN_TAPS; +        bool            adly_en       = false; +        boost::uint8_t  adly_value    = 0; +        boost::uint8_t  half_shift_en = 0; + +        if (ddly_value > DDLY_MAX_TAPS) { +            throw uhd::value_error("set_clock_delay: Requested delay is out of range."); +        } + +        double coerced_delay = (vco_period_ns * ddly_taps); +        if (leftover_delay > ADLY_MAX_NS) { +            //The VCO is running too slowly for us to compensate the digital delay difference using +            //analog delay. Do the best we can. +            adly_en = true; +            adly_value = static_cast<boost::uint8_t>(boost::math::round((ADLY_MAX_NS-ADLY_MIN_NS)/ADLY_RES_NS)); +            coerced_delay += ADLY_MAX_NS; +        } else if (leftover_delay >= ADLY_MIN_NS && leftover_delay <= ADLY_MAX_NS) { +            //The leftover delay can be compensated by the analog delay up to the analog delay resolution +            adly_en = true; +            adly_value = static_cast<boost::uint8_t>(boost::math::round((leftover_delay-ADLY_MIN_NS)/ADLY_RES_NS)); +            coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value); +        } else if (leftover_delay >= (ADLY_MIN_NS - half_vco_period_ns) && leftover_delay < ADLY_MIN_NS) { +            //The leftover delay if less than the minimum supported analog delay but if we move the digital +            //delay back by half a VCO cycle then it will be in the range of the analog delay. So do that! +            adly_en = true; +            adly_value = static_cast<boost::uint8_t>(boost::math::round((leftover_delay+half_vco_period_ns-ADLY_MIN_NS)/ADLY_RES_NS)); +            half_shift_en = 1; +            coerced_delay += ADLY_MIN_NS+(ADLY_RES_NS*adly_value)-half_vco_period_ns; +        } else { +            //Even after moving the digital delay back by half a cycle, we cannot make up the difference +            //so give up on compensating for the difference from the digital delay tap. +            //If control reaches here then the value of leftover_delay is possible very small and will still +            //be close to what the client requested. +        } + +        UHD_LOGV(often) +            << boost::format("x300_clock_ctrl::set_clock_delay: Which=%d, Requested=%f, Digital Taps=%d, Half Shift=%d, Analog Delay=%d (%s), Coerced Delay=%fns" +            ) % which % delay_ns % ddly_value % (half_shift_en?"ON":"OFF") % ((int)adly_value) % (adly_en?"ON":"OFF") % coerced_delay << std::endl; + +        //Apply settings +        switch (which) +        { +        case X300_CLOCK_WHICH_FPGA: +            _lmk04816_regs.CLKout0_1_DDLY = ddly_value; +            _lmk04816_regs.CLKout0_1_HS = half_shift_en; +            if (adly_en) { +                _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout0_1_ADLY = adly_value; +            } else { +                _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_PD; +                _lmk04816_regs.CLKout1_ADLY_SEL = lmk04816_regs_t::CLKOUT1_ADLY_SEL_D_PD; +            } +            write_regs(0); +            write_regs(6); +            _delays.fpga_dly_ns = coerced_delay; +            break; +        case X300_CLOCK_WHICH_DB0_RX: +        case X300_CLOCK_WHICH_DB1_RX: +            _lmk04816_regs.CLKout2_3_DDLY = ddly_value; +            _lmk04816_regs.CLKout2_3_HS = half_shift_en; +            if (adly_en) { +                _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout2_3_ADLY = adly_value; +            } else { +                _lmk04816_regs.CLKout2_ADLY_SEL = lmk04816_regs_t::CLKOUT2_ADLY_SEL_D_PD; +                _lmk04816_regs.CLKout3_ADLY_SEL = lmk04816_regs_t::CLKOUT3_ADLY_SEL_D_PD; +            } +            write_regs(1); +            write_regs(6); +            _delays.db_rx_dly_ns = coerced_delay; +            break; +        case X300_CLOCK_WHICH_DB0_TX: +        case X300_CLOCK_WHICH_DB1_TX: +            _lmk04816_regs.CLKout4_5_DDLY = ddly_value; +            _lmk04816_regs.CLKout4_5_HS = half_shift_en; +            if (adly_en) { +                _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout4_5_ADLY = adly_value; +            } else { +                _lmk04816_regs.CLKout4_ADLY_SEL = lmk04816_regs_t::CLKOUT4_ADLY_SEL_D_PD; +                _lmk04816_regs.CLKout5_ADLY_SEL = lmk04816_regs_t::CLKOUT5_ADLY_SEL_D_PD; +            } +            write_regs(2); +            write_regs(7); +            _delays.db_tx_dly_ns = coerced_delay; +            break; +        case X300_CLOCK_WHICH_DAC0: +        case X300_CLOCK_WHICH_DAC1: +            _lmk04816_regs.CLKout6_7_DDLY = ddly_value; +            _lmk04816_regs.CLKout6_7_HS = half_shift_en; +            if (adly_en) { +                _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout6_7_ADLY = adly_value; +            } else { +                _lmk04816_regs.CLKout6_ADLY_SEL = lmk04816_regs_t::CLKOUT6_ADLY_SEL_D_PD; +                _lmk04816_regs.CLKout7_ADLY_SEL = lmk04816_regs_t::CLKOUT7_ADLY_SEL_D_PD; +            } +            write_regs(3); +            write_regs(7); +            _delays.dac_dly_ns = coerced_delay; +            break; +        case X300_CLOCK_WHICH_ADC0: +        case X300_CLOCK_WHICH_ADC1: +            _lmk04816_regs.CLKout8_9_DDLY = ddly_value; +            _lmk04816_regs.CLKout8_9_HS = half_shift_en; +            if (adly_en) { +                _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_BOTH; +                _lmk04816_regs.CLKout8_9_ADLY = adly_value; +            } else { +                _lmk04816_regs.CLKout8_ADLY_SEL = lmk04816_regs_t::CLKOUT8_ADLY_SEL_D_PD; +                _lmk04816_regs.CLKout9_ADLY_SEL = lmk04816_regs_t::CLKOUT9_ADLY_SEL_D_PD; +            } +            write_regs(4); +            write_regs(8); +            _delays.adc_dly_ns = coerced_delay; +            break; +        default: +            throw uhd::value_error("set_clock_delay: Requested source is invalid."); +        } + +        //Delays are applied only on a sync event +        if (resync) sync_clocks(); + +        return coerced_delay; +    } + +    double get_clock_delay(const x300_clock_which_t which) { +        switch (which) +        { +        case X300_CLOCK_WHICH_FPGA: +            return _delays.fpga_dly_ns; +        case X300_CLOCK_WHICH_DB0_RX: +        case X300_CLOCK_WHICH_DB1_RX: +            return _delays.db_rx_dly_ns; +        case X300_CLOCK_WHICH_DB0_TX: +        case X300_CLOCK_WHICH_DB1_TX: +            return _delays.db_tx_dly_ns; +        case X300_CLOCK_WHICH_DAC0: +        case X300_CLOCK_WHICH_DAC1: +            return _delays.dac_dly_ns; +        case X300_CLOCK_WHICH_ADC0: +        case X300_CLOCK_WHICH_ADC1: +            return _delays.adc_dly_ns; +        default: +            throw uhd::value_error("get_clock_delay: Requested source is invalid."); +        } +    }  private: @@ -409,7 +615,6 @@ private:          _lmk04816_regs.CLKout0_1_PD = lmk04816_regs_t::CLKOUT0_1_PD_POWER_UP;          this->write_regs(0);          _lmk04816_regs.CLKout0_1_DIV = master_clock_div; -        _lmk04816_regs.CLKout0_ADLY_SEL = lmk04816_regs_t::CLKOUT0_ADLY_SEL_D_EV_X;          this->write_regs(0);          // Register 1 @@ -433,9 +638,6 @@ private:          _lmk04816_regs.CLKout1_TYPE = lmk04816_regs_t::CLKOUT1_TYPE_P_DOWN; //CPRI feedback clock, use LVDS          _lmk04816_regs.CLKout2_TYPE = lmk04816_regs_t::CLKOUT2_TYPE_LVPECL_700MVPP; //DB_0_RX          _lmk04816_regs.CLKout3_TYPE = lmk04816_regs_t::CLKOUT3_TYPE_LVPECL_700MVPP; //DB_1_RX -        // Analog delay of 900ps to synchronize the radio clock with the source synchronous ADC clocks. -        // This delay may need to vary due to temperature.  Tested and verified at room temperature only. -        _lmk04816_regs.CLKout0_1_ADLY = 0x10;          // Register 7          _lmk04816_regs.CLKout4_TYPE = lmk04816_regs_t::CLKOUT4_TYPE_LVPECL_700MVPP; //DB_1_TX @@ -501,6 +703,19 @@ private:          // PLL2_P_30 set in individual cases above          // PLL2_N_30 set in individual cases above +        if (_hw_rev >= 7) { +            _delays = X300_REV7_CLK_DELAYS; +        } else { +            _delays = X300_REV0_6_CLK_DELAYS; +        } + +        //Apply delay values +        set_clock_delay(X300_CLOCK_WHICH_FPGA,   _delays.fpga_dly_ns,  false); +        set_clock_delay(X300_CLOCK_WHICH_DB0_RX, _delays.db_rx_dly_ns, false);  //Sets both Ch0 and Ch1 +        set_clock_delay(X300_CLOCK_WHICH_DB0_TX, _delays.db_tx_dly_ns, false);  //Sets both Ch0 and Ch1 +        set_clock_delay(X300_CLOCK_WHICH_ADC0,   _delays.adc_dly_ns,   false);  //Sets both Ch0 and Ch1 +        set_clock_delay(X300_CLOCK_WHICH_DAC0,   _delays.dac_dly_ns,   false);  //Sets both Ch0 and Ch1 +          /* Write the configuration values into the LMK */          for (size_t i = 1; i <= 16; ++i) {              this->write_regs(i); @@ -512,13 +727,14 @@ private:          this->sync_clocks();      } -    const spi_iface::sptr _spiface; -    const size_t _slaveno; -    const size_t _hw_rev; -    const double _master_clock_rate; -    const double _system_ref_rate; -    lmk04816_regs_t _lmk04816_regs; -    double _vco_freq; +    const spi_iface::sptr   _spiface; +    const size_t            _slaveno; +    const size_t            _hw_rev; +    const double            _master_clock_rate; +    const double            _system_ref_rate; +    lmk04816_regs_t         _lmk04816_regs; +    double                  _vco_freq; +    x300_clk_delays         _delays;  };  x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface, diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp index 9c08aa356..160a14e6d 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.hpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp @@ -33,7 +33,7 @@ enum x300_clock_which_t      X300_CLOCK_WHICH_DB0_TX,      X300_CLOCK_WHICH_DB1_RX,      X300_CLOCK_WHICH_DB1_TX, -    X300_CLOCK_WHICH_TEST, +    X300_CLOCK_WHICH_FPGA,  };  class x300_clock_ctrl : boost::noncopyable @@ -94,6 +94,22 @@ public:       */      virtual void set_ref_out(const bool) = 0; +    /*! Set the clock delay for the given clock divider. +     * \param which which clock +     * \param rate the delay in nanoseconds +     * \param resync resync clocks to apply delays +     * \return the actual delay value set +     * \throw exception when which invalid or delay_ns out of range +     */ +    virtual double set_clock_delay(const x300_clock_which_t which, const double delay_ns, const bool resync = true) = 0; + +    /*! Get the clock delay for the given clock divider. +     * \param which which clock +     * \return the actual delay value set +     * \throw exception when which invalid +     */ +    virtual double get_clock_delay(const x300_clock_which_t which) = 0; +      /*! Reset the clocks.       *  Should be called if the reference clock changes       *  to reduce the time required to achieve a lock. diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp index d3bcb8644..bb41146b6 100644 --- a/host/lib/usrp/x300/x300_dac_ctrl.cpp +++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp @@ -129,12 +129,16 @@ public:          _check_pll();          // Configure digital interface settings -        write_ad9146_reg(0x16, 0x02); // Skew DCI signal by 615ps to find stable data eye -        write_ad9146_reg(0x03, 0x00); // 2's comp, I first, byte wide interface -        //fpga wants I,Q in the sample word: -        //first transaction goes into low bits -        //second transaction goes into high bits -        //therefore, we want Q to go first (bit 6 == 1) +        // Bypass DCI delay. We center the clock edge in the data +        // valid window in the FPGA by phase shifting the DCI going +        // to the DAC. +        write_ad9146_reg(0x16, 0x04); +        // 2's comp, I first, byte wide interface +        write_ad9146_reg(0x03, 0x00); +        // FPGA wants I,Q in the sample word: +        // - First transaction goes into low bits +        // - Second transaction goes into high bits +        //   therefore, we want Q to go first (bit 6 == 1)          write_ad9146_reg(0x03, (1 << 6)); //2s comp, i first, byte mode          // Configure interpolation filters diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 76531f921..6493e938d 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -29,10 +29,11 @@  extern "C" {  #endif -#define X300_MAX_HW_REV 6 -#define X300_FW_COMPAT_MAJOR 3 +#define X300_REVISION_COMPAT 7 +#define X300_REVISION_MIN    2 +#define X300_FW_COMPAT_MAJOR 4  #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 9 +#define X300_FPGA_COMPAT_MAJOR 13  //shared memory sections - in between the stack and the program space  #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_image_loader.cpp b/host/lib/usrp/x300/x300_image_loader.cpp new file mode 100644 index 000000000..321309868 --- /dev/null +++ b/host/lib/usrp/x300/x300_image_loader.cpp @@ -0,0 +1,402 @@ +// +// Copyright 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 <fstream> +#include <vector> + +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/xml_parser.hpp> + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/image_loader.hpp> +#include <uhd/exception.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/nirio/niusrprio_session.h> +#include <uhd/transport/nirio/status.h> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/static.hpp> + +#include "x300_impl.hpp" +#include "x300_fw_common.h" +#include "cdecode.h" + +namespace fs = boost::filesystem; + +using namespace boost::algorithm; +using namespace uhd; +using namespace uhd::transport; + +/* + * Constants + */ +#define X300_FPGA_BIN_SIZE_BYTES 15877916 +#define X300_FPGA_BIT_SIZE_BYTES 15878032 +#define X300_FPGA_PROG_UDP_PORT 49157 +#define X300_FLASH_SECTOR_SIZE 131072 +#define X300_PACKET_SIZE_BYTES 256 +#define X300_FPGA_SECTOR_START 32 +#define X300_MAX_RESPONSE_BYTES 128 +#define UDP_TIMEOUT 3 +#define FPGA_LOAD_TIMEOUT 15 + +/* + * Packet structure + */ +typedef struct { +    boost::uint32_t flags; +    boost::uint32_t sector; +    boost::uint32_t index; +    boost::uint32_t size; +    union { +        boost::uint8_t  data8[X300_PACKET_SIZE_BYTES]; +        boost::uint16_t data16[X300_PACKET_SIZE_BYTES/2]; +    }; +} x300_fpga_update_data_t; + +/* + * X-Series burn session + */ +typedef struct { +    bool                             found; +    bool                             ethernet; +    bool                             configure; // Reload FPGA after burning to flash (Ethernet only) +    bool                             verify;    // Device will verify the download along the way (Ethernet only) +    bool                             lvbitx; +    uhd::device_addr_t               dev_addr; +    std::string                      ip_addr; +    std::string                      fpga_type; +    std::string                      resource; +    std::string                      filepath; +    std::string                      rpc_port; +    boost::uint32_t                  size; +    udp_simple::sptr                 xport; +    std::vector<char>                bitstream; // .bin image extracted from .lvbitx file +    boost::uint8_t                   data_in[udp_simple::mtu]; +} x300_session_t; + +/* + * Extract the .bin image from the given LVBITX file. + */ +static void extract_from_lvbitx(x300_session_t &session){ +    boost::property_tree::ptree pt;  +    boost::property_tree::xml_parser::read_xml(session.filepath.c_str(), pt,  +                                               boost::property_tree::xml_parser::no_comments | +                                               boost::property_tree::xml_parser::trim_whitespace); +    const std::string encoded_bitstream(pt.get<std::string>("Bitfile.Bitstream")); +    std::vector<char> decoded_bitstream(encoded_bitstream.size()); + +    base64_decodestate decode_state; +    base64_init_decodestate(&decode_state); +    const size_t decoded_size = base64_decode_block(encoded_bitstream.c_str(), +                                encoded_bitstream.size(), &decoded_bitstream.front(), &decode_state); +    decoded_bitstream.resize(decoded_size); +    session.bitstream.swap(decoded_bitstream); + +    session.size = session.bitstream.size(); +} + +/* + * Validate X300 image and extract if LVBITX. + */ +static void x300_validate_image(x300_session_t &session){ +    if(not fs::exists(session.filepath)){ +        throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\".") +                                     % session.filepath)); +    } + +    std::string extension = fs::extension(session.filepath); +    session.lvbitx = (extension == ".lvbitx"); + +    if(session.lvbitx){ +        extract_from_lvbitx(session); +        if(session.size > X300_FPGA_BIN_SIZE_BYTES){ +            throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") +                                         % session.size % X300_FPGA_BIN_SIZE_BYTES)); +        } + +        /* +         * PCIe burning just takes a filepath, even for a .lvbitx file, +         * so just extract it to validate the size. +         */ +        if(!session.ethernet) session.bitstream.clear(); +    } +    else if(extension == ".bin" or extension == ".bit"){ +        boost::uint32_t max_size = (extension == ".bin") ? X300_FPGA_BIN_SIZE_BYTES +                                                         : X300_FPGA_BIT_SIZE_BYTES; + +        session.size = fs::file_size(session.filepath); +        if(session.size > max_size){ +            throw uhd::runtime_error(str(boost::format("The specified FPGA image is too large: %d vs. %d") +                                         % session.size % max_size)); +            return; +        } +    } +    else{ +        throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .bin, .bit, or .lvbitx.") +                                     % extension)); +    } +} + +static void x300_setup_session(x300_session_t &session, +                               const device_addr_t &args, +                               const std::string &filepath){ +    device_addr_t find_args; +    find_args["type"] = "x300"; +    if(args.has_key("name")) find_args["name"] = args["name"]; +    if(args.has_key("serial")) find_args["serial"] = args["serial"]; +    if(args.has_key("ip-addr")) find_args["addr"] = args["ip-addr"]; +    else if(args.has_key("resource")) find_args["resource"] = args["resource"]; + +    device_addrs_t devs = x300_find(args); +    session.found = (devs.size() > 0); +    if(!session.found) return; + +    session.dev_addr = devs[0]; +    session.ethernet = session.dev_addr.has_key("addr"); +    if(session.ethernet){ +        session.ip_addr = session.dev_addr["addr"]; +        session.configure = args.has_key("configure"); +        session.xport = udp_simple::make_connected(session.ip_addr, +                                                   BOOST_STRINGIZE(X300_FPGA_PROG_UDP_PORT)); +        session.verify = args.has_key("verify"); +    } +    else{ +        session.resource = session.dev_addr["resource"]; +        session.rpc_port = args.get("rpc-port", "5444"); +    } + +    /* +     * The user can specify an FPGA type (1G, HGS, XGS), rather than a filename. If the user +     * does not specify one, this will default to the type currently on the device. If this +     * cannot be determined, then the user is forced to specify a filename. +     */ +    session.fpga_type = args.get("fpga", session.dev_addr.get("fpga", "")); +    if(filepath == ""){ +        if(!session.dev_addr.has_key("product") or session.fpga_type == ""){ +            throw uhd::runtime_error("Found a device but could not auto-generate an image filename."); +        } +        else session.filepath = find_image_path(str(boost::format("usrp_%s_fpga_%s.bit") +                                                    % (to_lower_copy(session.dev_addr["product"])) +                                                    % session.fpga_type)); +    } +    else session.filepath = filepath; + +    // Validate image +    x300_validate_image(session); +} + +/* + * Ethernet communication functions + */ +static UHD_INLINE size_t x300_send_and_recv(udp_simple::sptr xport, +                                            boost::uint32_t pkt_code, +                                            x300_fpga_update_data_t *pkt_out, +                                            boost::uint8_t* data){ +    pkt_out->flags = uhd::htonx<boost::uint32_t>(pkt_code); +    xport->send(boost::asio::buffer(pkt_out, sizeof(*pkt_out))); +    return xport->recv(boost::asio::buffer(data, udp_simple::mtu), UDP_TIMEOUT); +} + +static UHD_INLINE bool x300_recv_ok(const x300_fpga_update_data_t *pkt_in, +                                    size_t len){ +    return (len > 0 and +            ((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR) != X300_FPGA_PROG_FLAGS_ERROR)); +} + +// Image data needs to be bitswapped +static UHD_INLINE void x300_bitswap(boost::uint8_t *num){ +    *num = ((*num & 0xF0) >> 4) | ((*num & 0x0F) << 4); +    *num = ((*num & 0xCC) >> 2) | ((*num & 0x33) << 2); +    *num = ((*num & 0xAA) >> 1) | ((*num & 0x55) << 1); +}  + +static void x300_ethernet_load(x300_session_t &session){ + +    // UDP receive buffer +    x300_fpga_update_data_t pkt_out; +    const x300_fpga_update_data_t *pkt_in = reinterpret_cast<const x300_fpga_update_data_t*>(session.data_in); + +    // Initialize write session +    boost::uint32_t flags = X300_FPGA_PROG_FLAGS_ACK | X300_FPGA_PROG_FLAGS_INIT; +    size_t len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); +    if(x300_recv_ok(pkt_in, len)){ +        std::cout << "-- Initializing FPGA loading..." << std::flush; +    } +    else if(len == 0){ +        std::cout << "failed." << std::endl; +        throw uhd::runtime_error("Timed out waiting for reply from device."); +    } +    else{ +        std::cout << "failed." << std::endl; +        throw uhd::runtime_error("Device reported an error during initialization."); +    } + +    std::cout << "successful." << std::endl; +    if(session.verify){ +        std::cout << "-- NOTE: Device is verifying the image it is receiving, increasing the loading time." << std::endl; +    } + +    size_t current_pos = 0; +    size_t sectors = (session.size / X300_FLASH_SECTOR_SIZE); +    std::ifstream image(session.filepath.c_str(), std::ios::binary); + +    // Each sector +    for(size_t i = 0; i < session.size; i += X300_FLASH_SECTOR_SIZE){ + +        // Print progress percentage at beginning of each sector +        std::cout << boost::format("\r-- Loading %s FPGA image: %d%% (%d/%d sectors)") +                     % session.fpga_type +                     % (int(double(i) / double(session.size) * 100.0)) +                     % (i / X300_FLASH_SECTOR_SIZE) +                     % sectors +                 << std::flush; + +        // Each packet +        for(size_t j = i; (j < session.size and j < (i+X300_FLASH_SECTOR_SIZE)); j += X300_PACKET_SIZE_BYTES){ +            flags = X300_FPGA_PROG_FLAGS_ACK; +            if(j == i)         flags |= X300_FPGA_PROG_FLAGS_ERASE; // Erase at beginning of sector +            if(session.verify) flags |= X300_FPGA_PROG_FLAGS_VERIFY; + +            // Set burn location +            pkt_out.sector = htonx<boost::uint32_t>(X300_FPGA_SECTOR_START + (i/X300_FLASH_SECTOR_SIZE)); +            pkt_out.index  = htonx<boost::uint32_t>((j % X300_FLASH_SECTOR_SIZE) / 2); +            pkt_out.size   = htonx<boost::uint32_t>(X300_PACKET_SIZE_BYTES / 2); + +            // Read next piece of image +            memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); +            if(session.lvbitx){ +                memcpy(pkt_out.data8, &session.bitstream[current_pos], X300_PACKET_SIZE_BYTES); +                current_pos += X300_PACKET_SIZE_BYTES; +            } +            else{ +                image.read((char*)pkt_out.data8, X300_PACKET_SIZE_BYTES); +            } + +            // Data must be bitswapped and byteswapped +            for(size_t k = 0; k < X300_PACKET_SIZE_BYTES; k++){ +                x300_bitswap(&pkt_out.data8[k]); +            } +            for(size_t k = 0; k < (X300_PACKET_SIZE_BYTES/2); k++){ +                pkt_out.data16[k] = htonx<boost::uint16_t>(pkt_out.data16[k]); +            } + +            len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); +            if(len == 0){ +                if(!session.lvbitx) image.close(); +                throw uhd::runtime_error("Timed out waiting for reply from device."); +            } +            else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ +                if(!session.lvbitx) image.close(); +                throw uhd::runtime_error("Device reported an error."); +            } +        } +    } +    if(!session.lvbitx){ +        image.close(); +    } + +    std::cout << boost::format("\r-- Loading %s FPGA image: 100%% (%d/%d sectors)") +                 % session.fpga_type +                 % sectors +                 % sectors +             << std::endl; + +    // Cleanup +    if(!session.lvbitx) image.close(); +    flags = (X300_FPGA_PROG_FLAGS_CLEANUP | X300_FPGA_PROG_FLAGS_ACK); +    pkt_out.sector = pkt_out.index = pkt_out.size = 0; +    memset(pkt_out.data8, 0, X300_PACKET_SIZE_BYTES); +    std::cout << "-- Finalizing image load..." << std::flush; +    len = x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); +    if(len == 0){ +        std::cout << "failed." << std::endl; +        throw uhd::runtime_error("Timed out waiting for reply from device."); +    } +    else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ +        std::cout << "failed." << std::endl; +        throw uhd::runtime_error("Device reported an error during cleanup."); +    } +    else std::cout << "successful." << std::endl; + +    // Save new FPGA image (if option set) +    if(session.configure){ +        flags = (X300_FPGA_PROG_CONFIGURE | X300_FPGA_PROG_FLAGS_ACK); +        x300_send_and_recv(session.xport, flags, &pkt_out, session.data_in); +        std::cout << "-- Saving image onto device..." << std::flush; +        if(len == 0){ +            std::cout << "failed." << std::endl; +            throw uhd::runtime_error("Timed out waiting for reply from device."); +        } +        else if((ntohl(pkt_in->flags) & X300_FPGA_PROG_FLAGS_ERROR)){ +            std::cout << "failed." << std::endl; +            throw uhd::runtime_error("Device reported an error while saving the image."); +        } +        else std::cout << "successful." << std::endl; +    } +} + +static void x300_pcie_load(x300_session_t &session){ + +    std::cout << boost::format("\r-- Loading %s FPGA image (this will take 5-10 minutes)...") +                 % session.fpga_type +             << std::flush; + +    nirio_status status = NiRio_Status_Success; +    niusrprio::niusrprio_session fpga_session(session.resource, session.rpc_port); +    nirio_status_chain(fpga_session.download_bitstream_to_flash(session.filepath), status); + +    if(nirio_status_fatal(status)){ +        std::cout << "failed." << std::endl; +        niusrprio::nirio_status_to_exception(status, "NI-RIO reported the following error:"); +    } +    else std::cout << "successful." << std::endl; +} + +static bool x300_image_loader(const image_loader::image_loader_args_t &image_loader_args){ +    // See if any X3x0 with the given args is found +    device_addrs_t devs = x300_find(image_loader_args.args); +    if(devs.size() == 0 or !image_loader_args.load_fpga) return false; + +    x300_session_t session; +    x300_setup_session(session, +                       image_loader_args.args, +                       image_loader_args.fpga_path +                      ); +    if(!session.found) return false; + +    std::cout << boost::format("Unit: USRP %s (%s, %s)\nFPGA Image: %s\n") +                 % session.dev_addr["product"] +                 % session.dev_addr["serial"] +                 % session.dev_addr[session.ethernet ? "addr" : "resource"] +                 % session.filepath; + +    if(session.ethernet) x300_ethernet_load(session); +    else                 x300_pcie_load(session); +    return true; +} + +UHD_STATIC_BLOCK(register_x300_image_loader){ +    std::string recovery_instructions = "Aborting. Your USRP X-Series device will likely be unusable. Visit\n" +                                        "http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_load_fpga_imgs_jtag\n" +                                        "for details on restoring your device."; + +    image_loader::register_image_loader("x300", x300_image_loader, recovery_instructions); +} diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index aff150acb..c9cc0cabc 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-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 @@ -16,7 +16,6 @@  //  #include "x300_impl.hpp" -#include "x300_regs.hpp"  #include "x300_lvbitx.hpp"  #include "x310_lvbitx.hpp"  #include <boost/algorithm/string.hpp> @@ -41,7 +40,7 @@  #define NIUSRPRIO_DEFAULT_RPC_PORT "5444" -#define X300_REV(x) (x - "A" + 1) +#define X300_REV(x) ((x) - "A" + 1)  using namespace uhd;  using namespace uhd::usrp; @@ -236,7 +235,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu      return addrs;  } -static device_addrs_t x300_find(const device_addr_t &hint_) +device_addrs_t x300_find(const device_addr_t &hint_)  {      //handle the multi-device discovery      device_addrs_t hints = separate_device_addr(hint_); @@ -400,7 +399,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)              default:                  nirio_status_to_exception(status, "Motherboard detection error. Please ensure that you \                      have a valid USRP X3x0, NI USRP-294xR or NI USRP-295xR device and that all the device \ -                    driver have been loaded."); +                    drivers have loaded successfully.");          }          //Load the lvbitx onto the device          UHD_MSG(status) << boost::format("Using LVBITX bitfile %s...\n") % lvbitx->get_bitfile_path(); @@ -410,7 +409,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)          //Tell the quirks object which FIFOs carry TX stream data          const boost::uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3}; -        mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos); +        mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos, 2);          _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE);      } @@ -508,9 +507,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)          x300_load_fw(mb.zpu_ctrl, x300_fw_image);      } -    //check compat -- good place to do after conditional loading +    //check compat numbers +    //check fpga compat before fw compat because the fw is a subset of the fpga image +    this->check_fpga_compat(mb_path, mb);      this->check_fw_compat(mb_path, mb.zpu_ctrl); -    this->check_fpga_compat(mb_path, mb.zpu_ctrl);      //store which FPGA image is loaded      mb.loaded_fpga_image = get_fpga_option(mb.zpu_ctrl); @@ -558,6 +558,13 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)          .set(mb_eeprom)          .subscribe(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1)); +    bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom"); +    if (recover_mb_eeprom) { +        UHD_MSG(warning) << "UHD is operating in EEPROM Recovery Mode which disables hardware version " +                            "checks.\nOperating in this mode may cause hardware damage and unstable " +                            "radio performance!"<< std::endl; +    } +      ////////////////////////////////////////////////////////////////////      // parse the product number      //////////////////////////////////////////////////////////////////// @@ -570,7 +577,10 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)              product_name = "X310";              break;          default: -            break; +            if (not recover_mb_eeprom) +                throw uhd::runtime_error("Unrecognized product type.\n" +                                         "Either the software does not support this device in which case please update your driver software to the latest version and retry OR\n" +                                         "The product code in the EEPROM is corrupt and may require reprogramming.");      }      _tree->create<std::string>(mb_path / "name").set(product_name);      _tree->create<std::string>(mb_path / "codename").set("Yetti"); @@ -602,36 +612,57 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      }      //////////////////////////////////////////////////////////////////// -    // create clock control objects +    // read hardware revision and compatibility number      //////////////////////////////////////////////////////////////////// -    UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl; -      mb.hw_rev = 0;      if(mb_eeprom.has_key("revision") and not mb_eeprom["revision"].empty()) {          try {              mb.hw_rev = boost::lexical_cast<size_t>(mb_eeprom["revision"]);          } catch(...) { -            UHD_MSG(warning) << "Revision in EEPROM is invalid! Please reprogram your EEPROM." << std::endl; +            if (not recover_mb_eeprom) +                throw uhd::runtime_error("Revision in EEPROM is invalid! Please reprogram your EEPROM.");          }      } else { -        UHD_MSG(warning) << "No revision detected MB EEPROM must be reprogrammed!" << std::endl; +        if (not recover_mb_eeprom) +            throw uhd::runtime_error("No revision detected. MB EEPROM must be reprogrammed!");      } -    if(mb.hw_rev == 0) { -        UHD_MSG(warning) << "Defaulting to X300 RevD Clock Settings. This will result in non-optimal lock times." << std::endl; -        mb.hw_rev = X300_REV("D"); +    size_t hw_rev_compat = 0; +    if (mb.hw_rev >= 7) { //Revision compat was added with revision 7 +        if (mb_eeprom.has_key("revision_compat") and not mb_eeprom["revision_compat"].empty()) { +            try { +                hw_rev_compat = boost::lexical_cast<size_t>(mb_eeprom["revision_compat"]); +            } catch(...) { +                if (not recover_mb_eeprom) +                    throw uhd::runtime_error("Revision compat in EEPROM is invalid! Please reprogram your EEPROM."); +            } +        } else { +            if (not recover_mb_eeprom) +                throw uhd::runtime_error("No revision compat detected. MB EEPROM must be reprogrammed!"); +        } +    } else { +        //For older HW just assume that revision_compat = revision +        hw_rev_compat = mb.hw_rev;      } -    if (mb.hw_rev > X300_MAX_HW_REV) { -        throw uhd::runtime_error(str( -                boost::format("Unsupported board revision number: %d.\n" -                              "The maximum board revision number supported in this version is %d.\n" -                              "Please update your UHD version.") -                % mb.hw_rev % X300_MAX_HW_REV -        )); +    if (hw_rev_compat > X300_REVISION_COMPAT) { +        if (not recover_mb_eeprom) +            throw uhd::runtime_error(str(boost::format( +                "Hardware is too new for this software. Please upgrade to a driver that supports hardware revision %d.") +                % mb.hw_rev)); +    } else if (mb.hw_rev < X300_REVISION_MIN) { //Compare min against the revision (and not compat) to give us more leeway for partial support for a compat +        if (not recover_mb_eeprom) +            throw uhd::runtime_error(str(boost::format( +                "Software is too new for this hardware. Please downgrade to a driver that supports hardware revision %d.") +                % mb.hw_rev));      } -    //Create clock control. NOTE: This does not configure the LMK yet. +    //////////////////////////////////////////////////////////////////// +    // create clock control objects +    //////////////////////////////////////////////////////////////////// +    UHD_MSG(status) << "Setup RF frontend clocking..." << std::endl; + +    //Initialize clock control registers. NOTE: This does not configure the LMK yet.      initialize_clock_control(mb);      mb.clock = x300_clock_ctrl::make(mb.zpu_spi,          1 /*slaveno*/, @@ -696,23 +727,33 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      ////////////////////////////////////////////////////////////////////      // setup radios      //////////////////////////////////////////////////////////////////// -    UHD_MSG(status) << "Initialize Radio control..." << std::endl; -    this->setup_radio(mb_i, "A"); -    this->setup_radio(mb_i, "B"); +    this->setup_radio(mb_i, "A", dev_addr); +    this->setup_radio(mb_i, "B", dev_addr); + +    //////////////////////////////////////////////////////////////////// +    // ADC test and cal +    //////////////////////////////////////////////////////////////////// +    if (dev_addr.has_key("self_cal_adc_delay")) { +        self_cal_adc_xfer_delay(mb, true /* Apply ADC delay */); +    } +    if (dev_addr.has_key("ext_adc_self_test")) { +        extended_adc_test(mb, dev_addr.cast<double>("ext_adc_self_test", 30)); +    } else { +        self_test_adcs(mb); +    }      ////////////////////////////////////////////////////////////////////      // front panel gpio      ////////////////////////////////////////////////////////////////////      mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); -    const std::vector<std::string> GPIO_ATTRS = boost::assign::list_of("CTRL")("DDR")("OUT")("ATR_0X")("ATR_RX")("ATR_TX")("ATR_XX"); -    BOOST_FOREACH(const std::string &attr, GPIO_ATTRS) +    BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)      { -        _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr) +        _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second)              .set(0) -            .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr, _1)); +            .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr.first, _1));      }      _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") -        .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio, "READBACK")); +        .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio));      ////////////////////////////////////////////////////////////////////      // register the time keepers - only one can be the highlander @@ -745,8 +786,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      ////////////////////////////////////////////////////////////////////      _tree->create<std::string>(mb_path / "clock_source" / "value")          .set("internal") -        .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)) -        .subscribe(boost::bind(&x300_impl::reset_radios, this, boost::ref(mb))); +        .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1));      static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo");      _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_source_options); @@ -829,8 +869,13 @@ x300_impl::~x300_impl(void)      {          BOOST_FOREACH(mboard_members_t &mb, _mb)          { -            mb.radio_perifs[0].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC -            mb.radio_perifs[1].ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //disable/reset ADC/DAC +            //Disable/reset ADC/DAC +            mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1); +            mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0); +            mb.radio_perifs[0].misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 0); +            mb.radio_perifs[0].misc_outs->flush(); +            mb.radio_perifs[1].misc_outs->set(radio_misc_outs_reg::DAC_ENABLED, 0); +            mb.radio_perifs[1].misc_outs->flush();              //kill the claimer task and unclaim the device              mb.claimer_task.reset(); @@ -850,15 +895,7 @@ x300_impl::~x300_impl(void)      }  } -static void check_adc(wb_iface::sptr iface, const boost::uint32_t val) -{ -    boost::uint32_t adc_rb = iface->peek32(RB32_RX); -    adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA -    //UHD_MSG(status) << "adc_rb " << std::hex << adc_rb << "  val " << std::hex << val << std::endl; -    UHD_ASSERT_THROW(adc_rb == val); -} - -void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name) +void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, const uhd::device_addr_t &dev_addr)  {      const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i);      UHD_ASSERT_THROW(mb_i < _mb.size()); @@ -866,6 +903,8 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)      const size_t radio_index = mb.get_radio_index(slot_name);      radio_perifs_t &perif = mb.radio_perifs[radio_index]; +    UHD_MSG(status) << boost::format("Initialize Radio%d control...") % radio_index << std::endl; +      ////////////////////////////////////////////////////////////////////      // radio control      //////////////////////////////////////////////////////////////////// @@ -873,39 +912,59 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)      boost::uint32_t ctrl_sid;      both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid);      perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, slot_name); -    perif.ctrl->poke32(TOREG(SR_MISC_OUTS), (1 << 2)); //reset adc + dac -    perif.ctrl->poke32(TOREG(SR_MISC_OUTS),  (1 << 1) | (1 << 0)); //out of reset + dac enable + +    perif.misc_outs = boost::make_shared<radio_misc_outs_reg>(); +    perif.misc_ins = boost::make_shared<radio_misc_ins_reg>(); +    perif.misc_outs->initialize(*perif.ctrl, true); +    perif.misc_ins->initialize(*perif.ctrl); + +    //Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1 +    if (radio_index == 0) { +        perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1); +        perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0); +        perif.misc_outs->flush(); +        perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 0); +        perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 1); +        perif.misc_outs->flush(); +    } +    perif.misc_outs->write(radio_misc_outs_reg::DAC_ENABLED, 1);      this->register_loopback_self_test(perif.ctrl); +    //////////////////////////////////////////////////////////////// +    // Setup peripherals +    ////////////////////////////////////////////////////////////////      perif.spi = spi_core_3000::make(perif.ctrl, TOREG(SR_SPI), RB32_SPI);      perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN);      perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate());      perif.leds = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_LEDS)); +    perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); +    perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); +    perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE); +    perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); +    perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); +    perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); +    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)); +    perif.ddc->set_link_rate(10e9/8); //whatever +    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 + +    //////////////////////////////////////////////////////////////////// +    // 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); + +    //Capture delays are calibrated every time. The status is only printed is the user +    //asks to run the xfer self cal using "self_cal_adc_delay" +    self_cal_adc_capture_delay(mb, radio_index, dev_addr.has_key("self_cal_adc_delay"));      _tree->access<time_spec_t>(mb_path / "time" / "cmd")          .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); -    _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); - -    //////////////////////////////////////////////////////////////// -    // ADC self test -    //////////////////////////////////////////////////////////////// -    perif.adc->set_test_word("ones", "ones"); check_adc(perif.ctrl, 0xfffcfffc); -    perif.adc->set_test_word("zeros", "zeros"); check_adc(perif.ctrl, 0x00000000); -    perif.adc->set_test_word("ones", "zeros"); check_adc(perif.ctrl, 0xfffc0000); -    perif.adc->set_test_word("zeros", "ones"); check_adc(perif.ctrl, 0x0000fffc); -    for (size_t k = 0; k < 14; k++) -    { -        perif.adc->set_test_word("zeros", "custom", 1 << k); -        check_adc(perif.ctrl, 1 << (k+2)); -    } -    for (size_t k = 0; k < 14; k++) -    { -        perif.adc->set_test_word("custom", "zeros", 1 << k); -        check_adc(perif.ctrl, 1 << (k+18)); -    } -    perif.adc->set_test_word("normal", "normal");      ////////////////////////////////////////////////////////////////      // create codec control objects @@ -922,80 +981,28 @@ void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name)      ////////////////////////////////////////////////////////////////////      // front end corrections      //////////////////////////////////////////////////////////////////// -    perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, TOREG(SR_RX_FRONT)); -    const fs_path rx_fe_path = mb_path / "rx_frontends" / slot_name; -    _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") -        .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, perif.rx_fe, _1)) -        .set(std::complex<double>(0.0, 0.0)); -    _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") -        .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, perif.rx_fe, _1)) -        .set(true); -    _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") -        .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, perif.rx_fe, _1)) -        .set(std::complex<double>(0.0, 0.0)); - -    perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, TOREG(SR_TX_FRONT)); -    const fs_path tx_fe_path = mb_path / "tx_frontends" / slot_name; -    _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") -        .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, perif.tx_fe, _1)) -        .set(std::complex<double>(0.0, 0.0)); -    _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") -        .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, perif.tx_fe, _1)) -        .set(std::complex<double>(0.0, 0.0)); +    perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name)); +    perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name));      //////////////////////////////////////////////////////////////////// -    // create rx dsp control objects +    // connect 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)); -    perif.ddc->set_link_rate(10e9/8); //whatever -    _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" / str(boost::format("%u") % radio_index); -    _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") -        .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")          .subscribe(boost::bind(&x300_impl::update_rx_samp_rate, this, boost::ref(mb), radio_index, _1)) -        .set(1e6); -    _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 +    // connect 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" / str(boost::format("%u") % radio_index); -    _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") -        .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")          .subscribe(boost::bind(&x300_impl::update_tx_samp_rate, this, boost::ref(mb), radio_index, _1)) -        .set(1e6); -    _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 @@ -1309,8 +1316,14 @@ void x300_impl::update_atr_leds(gpio_core_200_32wo::sptr leds, const std::string  void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate)  { -    BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) +    BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) { +        perif.ctrl->set_tick_rate(rate);          perif.time64->set_tick_rate(rate); +        perif.framer->set_tick_rate(rate); +        perif.ddc->set_tick_rate(rate); +        perif.deframer->set_tick_rate(rate); +        perif.duc->set_tick_rate(rate); +    }  }  void x300_impl::register_loopback_self_test(wb_iface::sptr iface) @@ -1365,7 +1378,8 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou      //Optimize for the case when the current source is internal and we are trying      //to set it to internal. This is the only case where we are guaranteed that      //the clock has not gone away so we can skip setting the MUX and reseting the LMK. -    if (not (mb.current_refclk_src == "internal" and source == "internal")) { +    const bool reconfigure_clks = (mb.current_refclk_src != "internal") or (source != "internal"); +    if (reconfigure_clks) {          //Update the clock MUX on the motherboard to select the requested source          mb.clock_control_regs_clock_source = 0;          mb.clock_control_regs_tcxo_enb = 0; @@ -1394,10 +1408,10 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou      //The programming code in x300_clock_ctrl is not compatible with revs <= 4 and may      //lead to locking issues. So, disable the ref-locked check for older (unsupported) boards.      if (mb.hw_rev > 4) { -        if (not wait_for_ref_locked(mb.zpu_ctrl, timeout)) { +        if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK, timeout)) {              //failed to lock on reference              if (mb.initialization_done) { -                throw uhd::runtime_error((boost::format("Reference Clock failed to lock to %s source.") % source).str()); +                throw uhd::runtime_error((boost::format("Reference Clock PLL failed to lock to %s source.") % source).str());              } else {                  //TODO: Re-enable this warning when we figure out a reliable lock time                  //UHD_MSG(warning) << "Reference clock failed to lock to " + source + " during device initialization.  " << @@ -1406,6 +1420,41 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou          }      } +    if (reconfigure_clks) { +        //Reset the radio clock PLL in the FPGA +        mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_RADIO_CLK_PLL); +        mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0); + +        //Wait for radio clock PLL to lock +        if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK, 0.01)) { +            throw uhd::runtime_error((boost::format("Reference Clock PLL in FPGA failed to lock to %s source.") % source).str()); +        } + +        //Reset the IDELAYCTRL used to calibrate the data interface delays +        mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), ZPU_SR_SW_RST_ADC_IDELAYCTRL); +        mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_SW_RST), 0); + +        //Wait for the ADC IDELAYCTRL to be ready +        if (not wait_for_clk_locked(mb.zpu_ctrl, ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK, 0.01)) { +            throw uhd::runtime_error((boost::format("ADC Calibration Clock in FPGA failed to lock to %s source.") % source).str()); +        } + +        // Reset ADCs and DACs +        for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { +            radio_perifs_t &perif = mb.radio_perifs[r]; +            if (perif.misc_outs && r==0) {  //ADC/DAC reset lines only exist in Radio0 +                perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 1); +                perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 0); +                perif.misc_outs->flush(); +                perif.misc_outs->set(radio_misc_outs_reg::ADC_RESET, 0); +                perif.misc_outs->set(radio_misc_outs_reg::DAC_RESET_N, 1); +                perif.misc_outs->flush(); +            } +            if (perif.adc) perif.adc->reset(); +            if (perif.dac) perif.dac->reset(); +        } +    } +      //Update cache value      mb.current_refclk_src = source;  } @@ -1432,24 +1481,29 @@ void x300_impl::update_time_source(mboard_members_t &mb, const std::string &sour      }  } -bool x300_impl::wait_for_ref_locked(wb_iface::sptr ctrl, double timeout) +static bool get_clk_locked(wb_iface::sptr ctrl, boost::uint32_t which) +{ +    return (ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)) & which) != 0; +} + +bool x300_impl::wait_for_clk_locked(wb_iface::sptr ctrl, boost::uint32_t which, double timeout)  {      boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::milliseconds(timeout * 1000.0); -    do -    { -        if (get_ref_locked(ctrl).to_bool()) +    do { +        if (get_clk_locked(ctrl, which))              return true;          boost::this_thread::sleep(boost::posix_time::milliseconds(1));      } while (boost::get_system_time() < timeout_time); -    //failed to lock on reference -    return false; +    //Check one last time +    return get_clk_locked(ctrl, which);  }  sensor_value_t x300_impl::get_ref_locked(wb_iface::sptr ctrl)  { -    boost::uint32_t clk_status = ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_CLK_STATUS)); -    const bool lock = ((clk_status & ZPU_RB_CLK_STATUS_LMK_LOCK) != 0); +    const bool lock = get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_LMK_LOCK) && +                      get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK) && +                      get_clk_locked(ctrl, ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK);      return sensor_value_t("Ref", lock, "locked", "unlocked");  } @@ -1469,63 +1523,6 @@ bool x300_impl::is_pps_present(wb_iface::sptr ctrl)  }  /*********************************************************************** - * reset and synchronization logic - **********************************************************************/ - -void x300_impl::reset_radios(mboard_members_t &mb) -{ -    // Reset ADCs and DACs -    BOOST_FOREACH (radio_perifs_t& perif, mb.radio_perifs) -    { -        perif.adc->reset(); -        perif.dac->reset(); -    } -} - -void x300_impl::synchronize_dacs(const std::vector<radio_perifs_t*>& radios) -{ -    if (radios.size() < 2) return;  //Nothing to synchronize - -    //**PRECONDITION** -    //This function assumes that all the VITA times in "radios" are synchronized -    //to a common reference. Currently, this function is called in get_tx_stream -    //which also has the same precondition. - -    //Reinitialize and resync all DACs -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->dac->reset_and_resync(); -    } - -    //Get a rough estimate of the cumulative command latency -    boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->ctrl->peek64(RB64_TIME_NOW); //Discard value. We are just timing the call -    } -    boost::posix_time::time_duration t_elapsed = -        boost::posix_time::microsec_clock::local_time() - t_start; - -    //Add 100% of headroom + uncertaintly to the command time -    boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; - -    //Pick radios[0] as the time reference. -    uhd::time_spec_t sync_time = -        radios[0]->time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); - -    //Send the sync command -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->ctrl->set_time(sync_time); -        radios[i]->ctrl->poke32(TOREG(SR_DACSYNC), 0x1);    //Arm FRAMEP/N sync pulse -        radios[i]->ctrl->set_time(uhd::time_spec_t(0.0));   //Clear command time -    } - -    //Wait and check status -    boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); -    for (size_t i = 0; i < radios.size(); i++) { -        radios[i]->dac->verify_sync(); -    } -} - -/***********************************************************************   * eeprom   **********************************************************************/ @@ -1544,20 +1541,24 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep   * front-panel GPIO   **********************************************************************/ -boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio, const std::string &) +boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio)  {      return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX));  } -void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const std::string &attr, const boost::uint32_t value) +void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value)  { -    if (attr == "CTRL") return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); -    if (attr == "DDR") return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); -    if (attr == "OUT") return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); -    if (attr == "ATR_0X") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); -    if (attr == "ATR_RX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); -    if (attr == "ATR_TX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); -    if (attr == "ATR_XX") return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, 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(); +    }  }  /*********************************************************************** @@ -1688,25 +1689,33 @@ void x300_impl::check_fw_compat(const fs_path &mb_path, wb_iface::sptr iface)                  % compat_major % compat_minor));  } -void x300_impl::check_fpga_compat(const fs_path &mb_path, wb_iface::sptr iface) +void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t &members)  { -    boost::uint32_t compat_num = iface->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM)); +    boost::uint32_t compat_num = members.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_COMPAT_NUM));      boost::uint32_t compat_major = (compat_num >> 16);      boost::uint32_t compat_minor = (compat_num & 0xffff);      if (compat_major != X300_FPGA_COMPAT_MAJOR)      { +        std::string image_loader_path = (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string(); +        std::string image_loader_cmd = str(boost::format("\"%s\" --args=\"type=x300,%s=%s\"") +                                              % image_loader_path +                                              % (members.xport_path == "eth" ? "addr" +                                                                             : "resource") +                                              % members.addr); +          throw uhd::runtime_error(str(boost::format(              "Expected FPGA compatibility number %d, but got %d:\n"              "The FPGA image on your device is not compatible with this host code build.\n"              "Download the appropriate FPGA images for this version of UHD.\n"              "%s\n\n"              "Then burn a new image to the on-board flash storage of your\n" -            "USRP X3xx device using the burner utility. %s\n\n" +            "USRP X3xx device using the image loader utility. Use this command:\n\n%s\n\n"              "For more information, refer to the UHD manual:\n\n"              " http://files.ettus.com/manual/page_usrp_x3x0.html#x3x0_flash"          )   % int(X300_FPGA_COMPAT_MAJOR) % compat_major -            % print_utility_error("uhd_images_downloader.py") % print_utility_error("usrp_x3xx_fpga_burner"))); +            % print_utility_error("uhd_images_downloader.py") +            % image_loader_cmd));      }      _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u")                  % compat_major % compat_minor)); @@ -1727,17 +1736,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& res          if (nirio_status_not_fatal(status)) {              //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping              switch (pid) { -                case X300_USRP_PCIE_SSID: +                case X300_USRP_PCIE_SSID_ADC_33: +                case X300_USRP_PCIE_SSID_ADC_18:                      mb_type = USRP_X300_MB; break; -                case X310_USRP_PCIE_SSID: -                case X310_2940R_PCIE_SSID: -                case X310_2942R_PCIE_SSID: -                case X310_2943R_PCIE_SSID: -                case X310_2944R_PCIE_SSID: -                case X310_2950R_PCIE_SSID: -                case X310_2952R_PCIE_SSID: -                case X310_2953R_PCIE_SSID: -                case X310_2954R_PCIE_SSID: +                case X310_USRP_PCIE_SSID_ADC_33: +                case X310_2940R_40MHz_PCIE_SSID_ADC_33: +                case X310_2940R_120MHz_PCIE_SSID_ADC_33: +                case X310_2942R_40MHz_PCIE_SSID_ADC_33: +                case X310_2942R_120MHz_PCIE_SSID_ADC_33: +                case X310_2943R_40MHz_PCIE_SSID_ADC_33: +                case X310_2943R_120MHz_PCIE_SSID_ADC_33: +                case X310_2944R_40MHz_PCIE_SSID_ADC_33: +                case X310_2950R_40MHz_PCIE_SSID_ADC_33: +                case X310_2950R_120MHz_PCIE_SSID_ADC_33: +                case X310_2952R_40MHz_PCIE_SSID_ADC_33: +                case X310_2952R_120MHz_PCIE_SSID_ADC_33: +                case X310_2953R_40MHz_PCIE_SSID_ADC_33: +                case X310_2953R_120MHz_PCIE_SSID_ADC_33: +                case X310_2954R_40MHz_PCIE_SSID_ADC_33: +                case X310_USRP_PCIE_SSID_ADC_18: +                case X310_2940R_40MHz_PCIE_SSID_ADC_18: +                case X310_2940R_120MHz_PCIE_SSID_ADC_18: +                case X310_2942R_40MHz_PCIE_SSID_ADC_18: +                case X310_2942R_120MHz_PCIE_SSID_ADC_18: +                case X310_2943R_40MHz_PCIE_SSID_ADC_18: +                case X310_2943R_120MHz_PCIE_SSID_ADC_18: +                case X310_2944R_40MHz_PCIE_SSID_ADC_18: +                case X310_2950R_40MHz_PCIE_SSID_ADC_18: +                case X310_2950R_120MHz_PCIE_SSID_ADC_18: +                case X310_2952R_40MHz_PCIE_SSID_ADC_18: +                case X310_2952R_120MHz_PCIE_SSID_ADC_18: +                case X310_2953R_40MHz_PCIE_SSID_ADC_18: +                case X310_2953R_120MHz_PCIE_SSID_ADC_18: +                case X310_2954R_40MHz_PCIE_SSID_ADC_18:                      mb_type = USRP_X310_MB; break;                  default:                      mb_type = UNKNOWN;      break; @@ -1762,17 +1793,39 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo          switch (product_num) {              //The PCIe ID -> MB mapping may be different from the EEPROM -> MB mapping -            case X300_USRP_PCIE_SSID: +            case X300_USRP_PCIE_SSID_ADC_33: +            case X300_USRP_PCIE_SSID_ADC_18:                  mb_type = USRP_X300_MB; break; -            case X310_USRP_PCIE_SSID: -            case X310_2940R_PCIE_SSID: -            case X310_2942R_PCIE_SSID: -            case X310_2943R_PCIE_SSID: -            case X310_2944R_PCIE_SSID: -            case X310_2950R_PCIE_SSID: -            case X310_2952R_PCIE_SSID: -            case X310_2953R_PCIE_SSID: -            case X310_2954R_PCIE_SSID: +            case X310_USRP_PCIE_SSID_ADC_33: +            case X310_2940R_40MHz_PCIE_SSID_ADC_33: +            case X310_2940R_120MHz_PCIE_SSID_ADC_33: +            case X310_2942R_40MHz_PCIE_SSID_ADC_33: +            case X310_2942R_120MHz_PCIE_SSID_ADC_33: +            case X310_2943R_40MHz_PCIE_SSID_ADC_33: +            case X310_2943R_120MHz_PCIE_SSID_ADC_33: +            case X310_2944R_40MHz_PCIE_SSID_ADC_33: +            case X310_2950R_40MHz_PCIE_SSID_ADC_33: +            case X310_2950R_120MHz_PCIE_SSID_ADC_33: +            case X310_2952R_40MHz_PCIE_SSID_ADC_33: +            case X310_2952R_120MHz_PCIE_SSID_ADC_33: +            case X310_2953R_40MHz_PCIE_SSID_ADC_33: +            case X310_2953R_120MHz_PCIE_SSID_ADC_33: +            case X310_2954R_40MHz_PCIE_SSID_ADC_33: +            case X310_USRP_PCIE_SSID_ADC_18: +            case X310_2940R_40MHz_PCIE_SSID_ADC_18: +            case X310_2940R_120MHz_PCIE_SSID_ADC_18: +            case X310_2942R_40MHz_PCIE_SSID_ADC_18: +            case X310_2942R_120MHz_PCIE_SSID_ADC_18: +            case X310_2943R_40MHz_PCIE_SSID_ADC_18: +            case X310_2943R_120MHz_PCIE_SSID_ADC_18: +            case X310_2944R_40MHz_PCIE_SSID_ADC_18: +            case X310_2950R_40MHz_PCIE_SSID_ADC_18: +            case X310_2950R_120MHz_PCIE_SSID_ADC_18: +            case X310_2952R_40MHz_PCIE_SSID_ADC_18: +            case X310_2952R_120MHz_PCIE_SSID_ADC_18: +            case X310_2953R_40MHz_PCIE_SSID_ADC_18: +            case X310_2953R_120MHz_PCIE_SSID_ADC_18: +            case X310_2954R_40MHz_PCIE_SSID_ADC_18:                  mb_type = USRP_X310_MB; break;              default:                  UHD_MSG(warning) << "X300 unknown product code in EEPROM: " << product_num << std::endl; @@ -1781,4 +1834,3 @@ x300_impl::x300_mboard_t x300_impl::get_mb_type_from_eeprom(const uhd::usrp::mbo      }      return mb_type;  } - diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index 890ef7bcb..20cd4d754 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -1,5 +1,5 @@  // -// Copyright 2013-2014 Ettus Research LLC +// Copyright 2013-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 @@ -49,6 +49,8 @@  #include <uhd/transport/nirio/niusrprio_session.h>  #include <uhd/transport/vrt_if_packet.hpp>  #include "recv_packet_demuxer_3000.hpp" +#include <uhd/utils/soft_register.hpp> +#include "x300_regs.hpp"  static const std::string X300_FW_FILE_NAME  = "usrp_x300_fw.bin"; @@ -140,6 +142,8 @@ uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface);  uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp);  uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy); +uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_); +  class x300_impl : public uhd::device  {  public: @@ -169,9 +173,43 @@ public:  private:      boost::shared_ptr<async_md_type> _async_md; +    class radio_misc_outs_reg : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED,          /*width*/ 1, /*shift*/ 0);  //[0] +        UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N,          /*width*/ 1, /*shift*/ 1);  //[1] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET,            /*width*/ 1, /*shift*/ 2);  //[2] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB,     /*width*/ 1, /*shift*/ 3);  //[3] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL,     /*width*/ 5, /*shift*/ 4);  //[8:4] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED,  /*width*/ 1, /*shift*/ 9);  //[9] + +        radio_misc_outs_reg(): uhd::soft_reg32_wo_t(TOREG(SR_MISC_OUTS)) { +            //Initial values +            set(DAC_ENABLED, 0); +            set(DAC_RESET_N, 0); +            set(ADC_RESET, 0); +            set(ADC_DATA_DLY_STB, 0); +            set(ADC_DATA_DLY_VAL, 16); +            set(ADC_CHECKER_ENABLED, 0); +        } +    }; +    class radio_misc_ins_reg : public uhd::soft_reg32_ro_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0);  //[0] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1);  //[1] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2);  //[2] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3);  //[3] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR,  /*width*/ 1, /*shift*/ 4);  //[4] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR,  /*width*/ 1, /*shift*/ 5);  //[5] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR,  /*width*/ 1, /*shift*/ 6);  //[6] +        UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR,  /*width*/ 1, /*shift*/ 7);  //[7] + +        radio_misc_ins_reg(): uhd::soft_reg32_ro_t(RB32_MISC_INS) { } +    }; +      //perifs in the radio core      struct radio_perifs_t      { +        //Interfaces          radio_ctrl_core_3000::sptr ctrl;          spi_core_3000::sptr spi;          x300_adc_ctrl::sptr adc; @@ -184,6 +222,9 @@ private:          gpio_core_200_32wo::sptr leds;          rx_frontend_core_200::sptr rx_fe;          tx_frontend_core_200::sptr tx_fe; +        //Registers +        radio_misc_outs_reg::sptr misc_outs; +        radio_misc_ins_reg::sptr misc_ins;      };      //overflow recovery impl @@ -211,7 +252,8 @@ private:          i2c_core_100_wb32::sptr zpu_i2c;          //perifs in each radio -        radio_perifs_t radio_perifs[2]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B +        static const size_t NUM_RADIOS = 2; +        radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B          uhd::usrp::dboard_eeprom_t db_eeproms[8];          //! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs          size_t get_radio_index(const std::string &slot_name) { @@ -259,7 +301,7 @@ private:        * \param mb_i Motherboard index        * \param slot_name Slot name (A or B).        */ -    void setup_radio(const size_t, const std::string &slot_name); +    void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr);      size_t _sid_framer;      struct sid_config_t @@ -348,21 +390,26 @@ private:      void set_time_source_out(mboard_members_t&, const bool);      void update_clock_source(mboard_members_t&, const std::string &);      void update_time_source(mboard_members_t&, const std::string &); -    void reset_radios(mboard_members_t&);      uhd::sensor_value_t get_ref_locked(uhd::wb_iface::sptr); -    bool wait_for_ref_locked(uhd::wb_iface::sptr, double timeout = 0.0); +    bool wait_for_clk_locked(uhd::wb_iface::sptr, boost::uint32_t which, double timeout);      bool is_pps_present(uhd::wb_iface::sptr);      void set_db_eeprom(uhd::i2c_iface::sptr i2c, const size_t, const uhd::usrp::dboard_eeprom_t &);      void set_mb_eeprom(uhd::i2c_iface::sptr i2c, const uhd::usrp::mboard_eeprom_t &);      void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); -    void check_fpga_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface); +    void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members);      void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant); -    boost::uint32_t get_fp_gpio(gpio_core_200::sptr, const std::string &); -    void set_fp_gpio(gpio_core_200::sptr, const std::string &, const boost::uint32_t); +    boost::uint32_t get_fp_gpio(gpio_core_200::sptr); +    void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); + +    void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false); +    double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false); +    void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100); + +    void extended_adc_test(mboard_members_t& mb, double duration_s);      //**PRECONDITION**      //This function assumes that all the VITA times in "radios" are synchronized diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index 334ae8168..e3515af0c 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -23,6 +23,7 @@  #include <uhd/transport/nirio_zero_copy.hpp>  #include "async_packet_handler.hpp"  #include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/chdr.hpp>  #include <boost/bind.hpp>  #include <uhd/utils/tasks.hpp>  #include <uhd/utils/log.hpp> @@ -124,41 +125,6 @@ void x300_impl::update_subdev_spec(const std::string &tx_rx, const size_t mb_i,  /*********************************************************************** - * VITA stuff - **********************************************************************/ -static void x300_if_hdr_unpack_be( -    const boost::uint32_t *packet_buff, -    vrt::if_packet_info_t &if_packet_info -){ -    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; -    return vrt::if_hdr_unpack_be(packet_buff, if_packet_info); -} - -static void x300_if_hdr_pack_be( -    boost::uint32_t *packet_buff, -    vrt::if_packet_info_t &if_packet_info -){ -    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; -    return vrt::if_hdr_pack_be(packet_buff, if_packet_info); -} - -static void x300_if_hdr_unpack_le( -    const boost::uint32_t *packet_buff, -    vrt::if_packet_info_t &if_packet_info -){ -    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; -    return vrt::if_hdr_unpack_le(packet_buff, if_packet_info); -} - -static void x300_if_hdr_pack_le( -    boost::uint32_t *packet_buff, -    vrt::if_packet_info_t &if_packet_info -){ -    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; -    return vrt::if_hdr_pack_le(packet_buff, if_packet_info); -} - -/***********************************************************************   * RX flow control handler   **********************************************************************/  static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args) @@ -209,9 +175,9 @@ static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xpo      //load header      if (big_endian) -        x300_if_hdr_pack_be(pkt, packet_info); +        vrt::chdr::if_hdr_pack_be(pkt, packet_info);      else -        x300_if_hdr_pack_le(pkt, packet_info); +        vrt::chdr::if_hdr_pack_le(pkt, packet_info);      //load payload      pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0); @@ -276,12 +242,12 @@ static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero      {          if (big_endian)          { -            x300_if_hdr_unpack_be(packet_buff, if_packet_info); +            vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info);              endian_conv = uhd::ntohx;          }          else          { -            x300_if_hdr_unpack_le(packet_buff, if_packet_info); +            vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info);              endian_conv = uhd::wtohx;          }      } @@ -430,10 +396,10 @@ rx_streamer::sptr x300_impl::get_rx_stream(const uhd::stream_args_t &args_)          //init some streamer stuff          std::string conv_endianness;          if (mb.if_pkt_is_big_endian) { -            my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_be); +            my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be);              conv_endianness = "be";          } else { -            my_streamer->set_vrt_unpacker(&x300_if_hdr_unpack_le); +            my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le);              conv_endianness = "le";          } @@ -594,10 +560,10 @@ tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_)          std::string conv_endianness;          if (mb.if_pkt_is_big_endian) { -            my_streamer->set_vrt_packer(&x300_if_hdr_pack_be); +            my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be);              conv_endianness = "be";          } else { -            my_streamer->set_vrt_packer(&x300_if_hdr_pack_le); +            my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le);              conv_endianness = "le";          } diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index f920b5ae2..6e92a6dbc 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -25,101 +25,134 @@  #define localparam static const int -localparam SR_DACSYNC   = 5; -localparam SR_LOOPBACK  = 6; -localparam SR_TEST      = 7; -localparam SR_SPI       = 8; -localparam SR_GPIO      = 16; -localparam SR_MISC_OUTS = 24; -localparam SR_READBACK  = 32; -localparam SR_TX_CTRL   = 64; -localparam SR_RX_CTRL   = 96; -localparam SR_TIME      = 128; -localparam SR_RX_DSP    = 144; -localparam SR_TX_DSP    = 184; -localparam SR_LEDS      = 195; -localparam SR_FP_GPIO   = 200; -localparam SR_RX_FRONT  = 208; -localparam SR_TX_FRONT  = 216; - -localparam RB32_GPIO            = 0; -localparam RB32_SPI             = 4; -localparam RB64_TIME_NOW        = 8; -localparam RB64_TIME_PPS        = 16; -localparam RB32_TEST            = 24; -localparam RB32_RX              = 28; -localparam RB32_FP_GPIO         = 32; - -localparam BL_ADDRESS     = 0; -localparam BL_DATA        = 1; +localparam SR_DACSYNC    = 5; +localparam SR_LOOPBACK   = 6; +localparam SR_TEST       = 7; +localparam SR_SPI        = 8; +localparam SR_GPIO       = 16; +localparam SR_MISC_OUTS  = 24; +localparam SR_READBACK   = 32; +localparam SR_TX_CTRL    = 64; +localparam SR_RX_CTRL    = 96; +localparam SR_TIME       = 128; +localparam SR_RX_DSP     = 144; +localparam SR_TX_DSP     = 184; +localparam SR_LEDS       = 195; +localparam SR_FP_GPIO    = 200; +localparam SR_RX_FRONT   = 208; +localparam SR_TX_FRONT   = 216; + +localparam RB32_GPIO     = 0; +localparam RB32_SPI      = 4; +localparam RB64_TIME_NOW = 8; +localparam RB64_TIME_PPS = 16; +localparam RB32_TEST     = 24; +localparam RB32_RX       = 28; +localparam RB32_FP_GPIO  = 32; +localparam RB32_MISC_INS = 36; + +localparam BL_ADDRESS    = 0; +localparam BL_DATA       = 1;  //wishbone settings map - relevant to host code -#define SET0_BASE 0xa000 -#define SETXB_BASE 0xb000 -#define BOOT_LDR_BASE 0xFA00 -#define I2C0_BASE 0xfe00 -#define I2C1_BASE 0xff00 +#define SET0_BASE     0xa000 +#define SETXB_BASE    0xb000 +#define BOOT_LDR_BASE 0xfa00 +#define I2C0_BASE     0xfe00 +#define I2C1_BASE     0xff00  #define SR_ADDR(base, offset) ((base) + (offset)*4)  localparam ZPU_SR_LEDS       = 00; -localparam ZPU_SR_PHY_RST    = 01; +localparam ZPU_SR_SW_RST     = 01;  localparam ZPU_SR_CLOCK_CTRL = 02;  localparam ZPU_SR_XB_LOCAL   = 03;  localparam ZPU_SR_SPI        = 32;  localparam ZPU_SR_ETHINT0    = 40;  localparam ZPU_SR_ETHINT1    = 56; +//reset bits +#define ZPU_SR_SW_RST_ETH_PHY           (1<<0) +#define ZPU_SR_SW_RST_RADIO_RST         (1<<1) +#define ZPU_SR_SW_RST_RADIO_CLK_PLL     (1<<2) +#define ZPU_SR_SW_RST_ADC_IDELAYCTRL    (1<<3) +  //clock controls -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL  0x00 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL  0x02 -#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO     0x03 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL  0x00 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL  0x02 -#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO     0x03 - -localparam ZPU_RB_SPI = 2; +#define ZPU_SR_CLOCK_CTRL_CLK_SRC_EXTERNAL 0x00 +#define ZPU_SR_CLOCK_CTRL_CLK_SRC_INTERNAL 0x02 +#define ZPU_SR_CLOCK_CTRL_CLK_SRC_GPSDO    0x03 +#define ZPU_SR_CLOCK_CTRL_PPS_SRC_EXTERNAL 0x00 +#define ZPU_SR_CLOCK_CTRL_PPS_SRC_INTERNAL 0x02 +#define ZPU_SR_CLOCK_CTRL_PPS_SRC_GPSDO    0x03 + +localparam ZPU_RB_SPI        = 2;  localparam ZPU_RB_CLK_STATUS = 3;  localparam ZPU_RB_COMPAT_NUM = 6;  localparam ZPU_RB_ETH_TYPE0  = 4;  localparam ZPU_RB_ETH_TYPE1  = 5;  //clock status -#define ZPU_RB_CLK_STATUS_LMK_STATUS    (0x3 << 0) -#define ZPU_RB_CLK_STATUS_LMK_LOCK      (0x1 << 2) -#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER  (0x1 << 3) -#define ZPU_RB_CLK_STATUS_PPS_DETECT    (0x1 << 4) +#define ZPU_RB_CLK_STATUS_LMK_STATUS        (0x3 << 0) +#define ZPU_RB_CLK_STATUS_LMK_LOCK          (0x1 << 2) +#define ZPU_RB_CLK_STATUS_LMK_HOLDOVER      (0x1 << 3) +#define ZPU_RB_CLK_STATUS_PPS_DETECT        (0x1 << 4) +#define ZPU_RB_CLK_STATUS_RADIO_CLK_LOCK    (0x1 << 5) +#define ZPU_RB_CLK_STATUS_IDELAYCTRL_LOCK   (0x1 << 6)  //spi slaves on radio -#define DB_DAC_SEN (1 << 7) -#define DB_ADC_SEN (1 << 6) +#define DB_DAC_SEN      (1 << 7) +#define DB_ADC_SEN      (1 << 6)  #define DB_RX_LSADC_SEN (1 << 5)  #define DB_RX_LSDAC_SEN (1 << 4)  #define DB_TX_LSADC_SEN (1 << 3)  #define DB_TX_LSDAC_SEN (1 << 2) -#define DB_RX_SEN (1 << 1) -#define DB_TX_SEN (1 << 0) +#define DB_RX_SEN       (1 << 1) +#define DB_TX_SEN       (1 << 0)  //-------------------------------------------------------------------  // PCIe Registers  //------------------------------------------------------------------- -static const uint32_t X300_PCIE_VID         = 0x1093; -static const uint32_t X300_PCIE_PID         = 0xC4C4; -static const uint32_t X300_USRP_PCIE_SSID   = 0x7736; -static const uint32_t X310_USRP_PCIE_SSID   = 0x76CA; -static const uint32_t X310_2940R_PCIE_SSID  = 0x772B; -static const uint32_t X310_2942R_PCIE_SSID  = 0x772C; -static const uint32_t X310_2943R_PCIE_SSID  = 0x772D; -static const uint32_t X310_2944R_PCIE_SSID  = 0x772E; -static const uint32_t X310_2950R_PCIE_SSID  = 0x772F; -static const uint32_t X310_2952R_PCIE_SSID  = 0x7730; -static const uint32_t X310_2953R_PCIE_SSID  = 0x7731; -static const uint32_t X310_2954R_PCIE_SSID  = 0x7732; +static const uint32_t X300_PCIE_VID               = 0x1093; +static const uint32_t X300_PCIE_PID               = 0xC4C4; +//Rev 0-6 motherboard/PCIe IDs (ADC driven at 3.3V) +static const uint32_t X300_USRP_PCIE_SSID_ADC_33         = 0x7736; +static const uint32_t X310_USRP_PCIE_SSID_ADC_33         = 0x76CA; +static const uint32_t X310_2940R_40MHz_PCIE_SSID_ADC_33  = 0x772B; +static const uint32_t X310_2940R_120MHz_PCIE_SSID_ADC_33 = 0x77FB; +static const uint32_t X310_2942R_40MHz_PCIE_SSID_ADC_33  = 0x772C; +static const uint32_t X310_2942R_120MHz_PCIE_SSID_ADC_33 = 0x77FC; +static const uint32_t X310_2943R_40MHz_PCIE_SSID_ADC_33  = 0x772D; +static const uint32_t X310_2943R_120MHz_PCIE_SSID_ADC_33 = 0x77FD; +static const uint32_t X310_2944R_40MHz_PCIE_SSID_ADC_33  = 0x772E; +static const uint32_t X310_2950R_40MHz_PCIE_SSID_ADC_33  = 0x772F; +static const uint32_t X310_2950R_120MHz_PCIE_SSID_ADC_33 = 0x77FE; +static const uint32_t X310_2952R_40MHz_PCIE_SSID_ADC_33  = 0x7730; +static const uint32_t X310_2952R_120MHz_PCIE_SSID_ADC_33 = 0x77FF; +static const uint32_t X310_2953R_40MHz_PCIE_SSID_ADC_33  = 0x7731; +static const uint32_t X310_2953R_120MHz_PCIE_SSID_ADC_33 = 0x7800; +static const uint32_t X310_2954R_40MHz_PCIE_SSID_ADC_33  = 0x7732; +//Rev 7+ motherboard/PCIe IDs (ADCs driven at 1.8V) +static const uint32_t X300_USRP_PCIE_SSID_ADC_18         = 0x7861; +static const uint32_t X310_USRP_PCIE_SSID_ADC_18         = 0x7862; +static const uint32_t X310_2940R_40MHz_PCIE_SSID_ADC_18  = 0x7853; +static const uint32_t X310_2940R_120MHz_PCIE_SSID_ADC_18 = 0x785B; +static const uint32_t X310_2942R_40MHz_PCIE_SSID_ADC_18  = 0x7854; +static const uint32_t X310_2942R_120MHz_PCIE_SSID_ADC_18 = 0x785C; +static const uint32_t X310_2943R_40MHz_PCIE_SSID_ADC_18  = 0x7855; +static const uint32_t X310_2943R_120MHz_PCIE_SSID_ADC_18 = 0x785D; +static const uint32_t X310_2944R_40MHz_PCIE_SSID_ADC_18  = 0x7856; +static const uint32_t X310_2950R_40MHz_PCIE_SSID_ADC_18  = 0x7857; +static const uint32_t X310_2950R_120MHz_PCIE_SSID_ADC_18 = 0x785E; +static const uint32_t X310_2952R_40MHz_PCIE_SSID_ADC_18  = 0x7858; +static const uint32_t X310_2952R_120MHz_PCIE_SSID_ADC_18 = 0x785F; +static const uint32_t X310_2953R_40MHz_PCIE_SSID_ADC_18  = 0x7859; +static const uint32_t X310_2953R_120MHz_PCIE_SSID_ADC_18 = 0x7860; +static const uint32_t X310_2954R_40MHz_PCIE_SSID_ADC_18  = 0x785A;  static const uint32_t FPGA_X3xx_SIG_VALUE   = 0x58333030;  static const uint32_t PCIE_FPGA_ADDR_BASE   = 0xC0000; -#define PCIE_FPGA_REG(X)                    (PCIE_FPGA_ADDR_BASE + X) +#define PCIE_FPGA_REG(X)                    (PCIE_FPGA_ADDR_BASE + (X))  static const uint32_t FPGA_PCIE_SIG_REG     = PCIE_FPGA_REG(0x0000);  static const uint32_t FPGA_CNTR_LO_REG      = PCIE_FPGA_REG(0x0004); @@ -140,8 +173,8 @@ static const uint32_t DMA_FRAME_SIZE_REG    = 0x4;  static const uint32_t DMA_SAMPLE_COUNT_REG  = 0x8;  static const uint32_t DMA_PKT_COUNT_REG     = 0xC; -#define PCIE_TX_DMA_REG(REG, CHAN)          (PCIE_TX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) -#define PCIE_RX_DMA_REG(REG, CHAN)          (PCIE_RX_DMA_REG_BASE + (CHAN*DMA_REG_GRP_SIZE) + REG) +#define PCIE_TX_DMA_REG(REG, CHAN)          (PCIE_TX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG)) +#define PCIE_RX_DMA_REG(REG, CHAN)          (PCIE_RX_DMA_REG_BASE + ((CHAN)*DMA_REG_GRP_SIZE) + (REG))  static const uint32_t DMA_CTRL_DISABLED     = 0x00000000;  static const uint32_t DMA_CTRL_ENABLED      = 0x00000002; @@ -154,15 +187,15 @@ static const uint32_t DMA_STATUS_ERROR      = 0x00000001;  static const uint32_t DMA_STATUS_BUSY       = 0x00000002;  static const uint32_t PCIE_ROUTER_REG_BASE  = PCIE_FPGA_REG(0x0500); -#define PCIE_ROUTER_REG(X)                  (PCIE_ROUTER_REG_BASE + X) +#define PCIE_ROUTER_REG(X)                  (PCIE_ROUTER_REG_BASE + (X))  static const uint32_t PCIE_ZPU_DATA_BASE    = 0x30000;  static const uint32_t PCIE_ZPU_READ_BASE    = 0x20000;  //Trig and Status share the same base  static const uint32_t PCIE_ZPU_STATUS_BASE  = 0x20000; -#define PCIE_ZPU_DATA_REG(X)                (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + X) -#define PCIE_ZPU_READ_REG(X)                (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + X) -#define PCIE_ZPU_STATUS_REG(X)              (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + X) +#define PCIE_ZPU_DATA_REG(X)                (PCIE_FPGA_REG(PCIE_ZPU_DATA_BASE) + (X)) +#define PCIE_ZPU_READ_REG(X)                (PCIE_FPGA_REG(PCIE_ZPU_READ_BASE) + (X)) +#define PCIE_ZPU_STATUS_REG(X)              (PCIE_FPGA_REG(PCIE_ZPU_STATUS_BASE) + (X))  static const uint32_t PCIE_ZPU_READ_START       = 0x0;  static const uint32_t PCIE_ZPU_READ_CLOBBER     = 0x80000000;  | 
