diff options
Diffstat (limited to 'host')
113 files changed, 2055 insertions, 1359 deletions
diff --git a/host/.gitignore b/host/.gitignore index 796b96d1c..9b0584c23 100644 --- a/host/.gitignore +++ b/host/.gitignore @@ -1 +1,3 @@ /build +tags +*~ diff --git a/host/AUTHORS.txt b/host/AUTHORS.txt deleted file mode 100644 index 44b7516cd..000000000 --- a/host/AUTHORS.txt +++ /dev/null @@ -1,40 +0,0 @@ -Matt Ettus - matt@ettus.com - USRP1 FPGA code - USRP2/N200 FPGA code - USRP-E100 FPGA code - -Josh Blum - josh@ettus.com - driver framework - USRP2/N200 firmware - USRP2/N200 host code - USRP-E100 host code - Basic/LF host code - XCVR2450 host code - RFX Series host code - -Jason Abele - jason@ettus.com - RFX Series host code - WBX host code - DBSRX host code - DBSRX2 host code - -Eric Blossom - eb@comsec.com - USRP1 firmware - USRP2 firmware - -Tom Tsou - ttsou@vt.edu - UHD-USB framework - LIBUSB host code - USRP1 host code - USRP1 firmware - -Nick Foster - nick@ettus.com - LIBUSB host code - USRP1 host code - TVRX host code - USRP-N200 firmware - USRP-N200 host code - -Philip Balister - philip@opensdr.com - USRP-E100 kernel module - USRP-E100 utilities diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index d5301bce3..103266edd 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -201,9 +201,8 @@ ADD_CUSTOM_TARGET(uninstall # Install Package Docs ######################################################################## UHD_INSTALL(FILES - ${CMAKE_CURRENT_SOURCE_DIR}/README.txt - ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt - ${CMAKE_CURRENT_SOURCE_DIR}/AUTHORS.txt + ${CMAKE_CURRENT_SOURCE_DIR}/../README.md + ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${PKG_DOC_DIR} COMPONENT readme ) @@ -212,8 +211,8 @@ UHD_INSTALL(FILES # Images download directory for utils/uhd_images_downloader.py ######################################################################## -SET(UHD_IMAGES_MD5SUM "3da7fc23661b7e612815dcae1ac95efa") -SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/master_images/archive/uhd-images_003.005.003-159-g0159006c.zip") +SET(UHD_IMAGES_MD5SUM "61db1170aaaadd787f5150b3eceab6f2") +SET(UHD_IMAGES_DOWNLOAD_SRC "http://files.ettus.com/binaries/maint_images/archive/uhd-images_003.006.002-rc1.zip") ######################################################################## # Register top level components @@ -285,7 +284,7 @@ ENDIF(NOT LIBUHD_PKG AND NOT UHDHOST_PKG) # Handle pre-built images ######################################################################## IF(DEFINED UHD_IMAGES_DIR AND EXISTS "${UHD_IMAGES_DIR}") - FILE(GLOB _image_files "${UHD_IMAGES_DIR}/*.*") + FILE(GLOB_RECURSE _image_files "${UHD_IMAGES_DIR}/*") MESSAGE(STATUS "Using images:") FOREACH(_img ${_image_files}) MESSAGE(STATUS " ${_img}") diff --git a/host/LICENSE.txt b/host/LICENSE index 9aa03b39b..9aa03b39b 100644 --- a/host/LICENSE.txt +++ b/host/LICENSE diff --git a/host/README.txt b/host/README.txt deleted file mode 100644 index 0e8e62ac1..000000000 --- a/host/README.txt +++ /dev/null @@ -1,43 +0,0 @@ -############################################### -# Ettus Research - USRP Hardware Driver -############################################### -The hardware driver for Ettus Research products. - -############################################### -# Supported USRP Motherboards -############################################### -USRP1 -USRP2 -N200 -N210 -E100 -E110 -B100 -B200 - -############################################### -# Supported USRP Daughterboards -############################################### -Basic RX -Basic TX -LF RX -LF TX -RFX Series -XCVR 2450 -WBX + Simple GDB -DBSRX -DBSRX2 -TVRX -TVRX2 -SBX -CBX - -############################################### -# Documentation -############################################### -Online documentation available at: -http://code.ettus.com/redmine/ettus/projects/uhd/wiki - -The build system can generate the html for the manual and Doxygen. -Docutils and Doxygen are required to build the html docs. -See the docs directory for the manual source (reStructuredText). diff --git a/host/apps/omap_debug/README b/host/apps/omap_debug/README deleted file mode 100644 index bbe0c2cc4..000000000 --- a/host/apps/omap_debug/README +++ /dev/null @@ -1 +0,0 @@ -OMAP development tools go here diff --git a/host/cmake/Modules/FindUSB1.cmake b/host/cmake/Modules/FindUSB1.cmake index a494e1350..b2e4d118c 100644 --- a/host/cmake/Modules/FindUSB1.cmake +++ b/host/cmake/Modules/FindUSB1.cmake @@ -8,7 +8,8 @@ PKG_CHECK_MODULES(PC_LIBUSB QUIET libusb-1.0) FIND_PATH(LIBUSB_INCLUDE_DIRS NAMES libusb.h - HINTS $ENV{LIBUSB_DIR}/include ${PC_LIBUSB_INCLUDEDIR} + HINTS $ENV{LIBUSB_DIR}/include $ENV{LIBUSB_DIR}/include/libusb-1.0 + ${PC_LIBUSB_INCLUDEDIR} ${PC_LIBUSB_INCLUDEDIR}/libusb-1.0 PATHS /usr/local/include/libusb-1.0 /usr/local/include /usr/include/libusb-1.0 /usr/include /opt/local/include/libusb-1.0 diff --git a/host/cmake/Modules/UHDPackage.cmake b/host/cmake/Modules/UHDPackage.cmake index 6af474f7a..4968571b5 100644 --- a/host/cmake/Modules/UHDPackage.cmake +++ b/host/cmake/Modules/UHDPackage.cmake @@ -99,9 +99,8 @@ SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Ettus Research - USRP Hardware Driver") SET(CPACK_PACKAGE_VENDOR "Ettus Research LLC") SET(CPACK_PACKAGE_CONTACT "Ettus Research <support@ettus.com>") SET(CPACK_PACKAGE_VERSION "${UHD_VERSION}") -SET(CPACK_RESOURCE_FILE_WELCOME ${CMAKE_SOURCE_DIR}/README.txt) -SET(CPACK_RESOURCE_FILE_README ${CMAKE_SOURCE_DIR}/AUTHORS.txt) -SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE.txt) +SET(CPACK_RESOURCE_FILE_WELCOME ${CMAKE_SOURCE_DIR}/../README.md) +SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE) ######################################################################## # Setup CPack Source diff --git a/host/cmake/Modules/UHDUnitTest.cmake b/host/cmake/Modules/UHDUnitTest.cmake index 47cddc521..76fec14b8 100644 --- a/host/cmake/Modules/UHDUnitTest.cmake +++ b/host/cmake/Modules/UHDUnitTest.cmake @@ -64,7 +64,11 @@ function(UHD_ADD_TEST test_name) list(APPEND environs "PATH=${binpath}" "${LD_PATH_VAR}=${libpath}") #generate a bat file that sets the environment and runs the test - find_program(SHELL sh) + if (CMAKE_CROSSCOMPILING) + set(SHELL "/bin/sh") + else(CMAKE_CROSSCOMPILING) + find_program(SHELL sh) + endif(CMAKE_CROSSCOMPILING) set(sh_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.sh) file(WRITE ${sh_file} "#!${SHELL}\n") #each line sets an environment variable diff --git a/host/cmake/Modules/UHDVersion.cmake b/host/cmake/Modules/UHDVersion.cmake index 8e14b26bf..b0d04fe70 100644 --- a/host/cmake/Modules/UHDVersion.cmake +++ b/host/cmake/Modules/UHDVersion.cmake @@ -26,8 +26,8 @@ FIND_PACKAGE(Git QUIET) # - increment patch on for bug fixes and docs ######################################################################## SET(UHD_VERSION_MAJOR 003) -SET(UHD_VERSION_MINOR 005) -SET(UHD_VERSION_PATCH 004) +SET(UHD_VERSION_MINOR 006) +SET(UHD_VERSION_PATCH 002) ######################################################################## # Set up trimmed version numbers for DLL resource files and packages @@ -87,7 +87,7 @@ IF(UHD_RELEASE_MODE) #Ignore UHD_GIT_COUNT in UHD_VERSION if the string 'release' is in UHD_RELEASE_MODE EXECUTE_PROCESS( WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - COMMAND ${PYTHON_EXECUTABLE} -c "print 'release' in '${UHD_RELEASE_MODE}'" + COMMAND ${PYTHON_EXECUTABLE} -c "print ('release' in '${UHD_RELEASE_MODE}') or ('rc' in '${UHD_RELEASE_MODE}')" OUTPUT_VARIABLE TRIM_UHD_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) ENDIF() diff --git a/host/cmake/Toolchains/armv7athf_native.cmake b/host/cmake/Toolchains/armv7athf_native.cmake new file mode 100644 index 000000000..d9d1e2598 --- /dev/null +++ b/host/cmake/Toolchains/armv7athf_native.cmake @@ -0,0 +1,8 @@ +######################################################################## +# Toolchain file for building native on a ARM Cortex A8 w/ NEON +# Usage: cmake -DCMAKE_TOOLCHAIN_FILE=<this file> <source directory> +######################################################################## +set(CMAKE_CXX_COMPILER g++) +set(CMAKE_C_COMPILER gcc) +set(CMAKE_CXX_FLAGS "-march=armv7-a -mthumb -mfpu=neon -mfloat-abi=hard" CACHE STRING "" FORCE) +set(CMAKE_C_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "" FORCE) #same flags for C sources diff --git a/host/cmake/Toolchains/oe-sdk_cross.cmake b/host/cmake/Toolchains/oe-sdk_cross.cmake new file mode 100644 index 000000000..ea77815c9 --- /dev/null +++ b/host/cmake/Toolchains/oe-sdk_cross.cmake @@ -0,0 +1,13 @@ +set( CMAKE_SYSTEM_NAME Linux ) +#set( CMAKE_C_COMPILER $ENV{CC} ) +#set( CMAKE_CXX_COMPILER $ENV{CXX} ) +set( CMAKE_CXX_FLAGS $ENV{CXXFLAGS} CACHE STRING "" FORCE ) +set( CMAKE_C_FLAGS $ENV{CFLAGS} CACHE STRING "" FORCE ) #same flags for C sources +set( CMAKE_LDFLAGS_FLAGS ${CMAKE_CXX_FLAGS} CACHE STRING "" FORCE ) #same flags for C sources +set( CMAKE_LIBRARY_PATH ${OECORE_TARGET_SYSROOT}/usr/lib ) +set( CMAKE_FIND_ROOT_PATH $ENV{OECORE_NATIVE_SYSROOT} $ENV{OECORE_TARGET_SYSROOT} ) +set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) +set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) +set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) +set ( ORC_INCLUDE_DIRS $ENV{OECORE_TARGET_SYSROOT}/usr/include/orc-0.4 ) +set ( ORC_LIBRARY_DIRS $ENV{OECORE_TARGET_SYSROOT}/usr/lib ) diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt index 17744db7d..fa6e98918 100644 --- a/host/docs/CMakeLists.txt +++ b/host/docs/CMakeLists.txt @@ -26,6 +26,7 @@ SET(manual_sources coding.rst dboards.rst gpsdo.rst + gpsdo_b2x0.rst general.rst images.rst stream.rst diff --git a/host/docs/build.rst b/host/docs/build.rst index 00f79aaaa..5512e71ae 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -64,7 +64,7 @@ LibUSB * **Minimum Version:** 1.0 * **Usage:** build time + runtime (optional) * **Download URL:** http://sourceforge.net/projects/libusb/files/libusb-1.0/ -* **Download URL (Windows binaries):** http://www.libusb.org/wiki/windows_backend#LatestBinarySnapshots +* **Download URL (Windows):** https://github.com/libusbx/libusbx ^^^^^^^^^^^^^^^^ Python @@ -169,7 +169,7 @@ Generate the project with CMake * Click "Generate", and a project file will be created in the build directory. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LibUSB CMake notes +LibUSB notes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ On Windows, CMake does not have the advantage of **pkg-config**, so we must manually tell CMake how to locate the LibUSB header and lib. @@ -182,6 +182,8 @@ so we must manually tell CMake how to locate the LibUSB header and lib. * Check the box to enable USB support, click "Configure" and "Generate". +**Note:** On Windows, LibUSBx is required to use most USB3 controllers. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Build the project in MSVC ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/host/docs/gpsdo.rst b/host/docs/gpsdo.rst index 1e9019a0f..1cf1dae29 100644 --- a/host/docs/gpsdo.rst +++ b/host/docs/gpsdo.rst @@ -1,5 +1,5 @@ ======================================================================== -UHD - Internal GPSDO Application Notes +UHD - Internal GPSDO Application Notes (USRP-N2x0/E1X0 Models) ======================================================================== .. contents:: Table of Contents diff --git a/host/docs/gpsdo_b2x0.rst b/host/docs/gpsdo_b2x0.rst new file mode 100644 index 000000000..bca10e99a --- /dev/null +++ b/host/docs/gpsdo_b2x0.rst @@ -0,0 +1,80 @@ +======================================================================== +UHD - Internal GPSDO Application Notes (USRP-B2X0 Models) +======================================================================== + +.. contents:: Table of Contents + +This application note describes the use of integrated GPS-disciplined +oscillators with Ettus Research USRP devices. It pertains specifically +to the Jackson Labs LC_XO device unless noted otherwise. + +------------------------------------------------------------------------ +Specifications +------------------------------------------------------------------------ +* **Receiver type**: 50 channel with WAAS, EGNOS, MSAS +* **10MHz ADEV**: 5e-11 over >24h +* **1PPS RMS jitter**: <50ns 1-sigma +* **Holdover**: <20us over 3h + +**Phase noise**: + ++------------+-------------+------------+ +| | TCXO | OXCO | ++============+=============+============+ +| **1Hz** | -65dBc/Hz | -75dBc/Hz | ++------------+-------------+------------+ +| **10Hz** | -102dBc/Hz | -110dBc/Hz | ++------------+-------------+------------+ +| **100Hz** | -132dBc/Hz | -132dBc/Hz | ++------------+-------------+------------+ +| **1kHz** | -148dBc/Hz | -142dBc/Hz | ++------------+-------------+------------+ +| **10kHz** | -152dBc/Hz | -145dBc/Hz | ++------------+-------------+------------+ +| **100kHz** | <-155dBc/Hz | -150dBc/Hz | ++------------+-------------+------------+ + +**Antenna Types:** + +The GPSDO is capable of supplying a 3V for active GPS antennas or supporting passive antennas. + +------------------------------------------------------------------------ +Installation Instructions +------------------------------------------------------------------------ +To install the GPSDO, you must insert it into the slot on the board +near the 10 MHz Reference SMA. Keep in mind that the two sides of the +GPSDO have a different number of pins. When inserting the GPSDO, make +sure to press down firmly and evenly. When turning on the USRP B2X0 device, +a green LED should illuminate on the GPSDO. This signifies that the unit +has successfully been placed. + +**NOTE: The pins on the GPSDO are very fragile. Be sure to press down +evenly, or the pins may bend or break. Once the GPSDO is in place, +we very highly discourage further removal, as this also risks damaging +the pins.** + +------------------------------------------------------------------------ +Using the GPSDO in Your Application +------------------------------------------------------------------------ +By default, if a GPSDO is detected at startup, the USRP will be configured +to use it as a frequency and time reference. The internal VITA timestamp +will be initialized to the GPS time, and the internal oscillator will be +phase-locked to the 10MHz GPSDO reference. If the GPSDO is not locked to +satellites, the VITA time will not be initialized. + +GPS data is obtained through the **mboard_sensors** interface. To retrieve +the current GPS time, use the **gps_time** sensor: + +:: + + usrp->get_mboard_sensor("gps_time"); + +The returned value will be the current epoch time, in seconds since +January 1, 1970. This value is readily converted into human-readable +format using the **time.h** library in C, **boost::posix_time** in C++, etc. + +Other information can be fetched as well. You can query the lock status +with the **gps_locked** sensor, as well as obtain raw NMEA sentences using +the **gps_gprmc**, and **gps_gpgga** sensors. Location +information can be parsed out of the **gps_gpgga** sensor by using **gpsd** or +another NMEA parser. diff --git a/host/docs/index.rst b/host/docs/index.rst index 27523c139..65069059f 100644 --- a/host/docs/index.rst +++ b/host/docs/index.rst @@ -32,7 +32,8 @@ Application Notes * `Daughterboard Application Notes <./dboards.html>`_ * `Transport Application Notes <./transport.html>`_ * `Synchronization Application Notes <./sync.html>`_ -* `Internal GPSDO Application Notes <./gpsdo.html>`_ +* `Internal GPSDO Application Notes (USRP-N2X0/E1X0 Models) <./gpsdo.html>`_ +* `Internal GPSDO Application Notes (USRP-B2X0 Models) <./gpsdo_b2x0.html>`_ * `Calibration Application Notes <./calibration.html>`_ ^^^^^^^^^^^^^^^^^^^^^ diff --git a/host/docs/uhd_find_devices.1 b/host/docs/uhd_find_devices.1 index 7dc0dd470..08f9a789c 100644 --- a/host/docs/uhd_find_devices.1 +++ b/host/docs/uhd_find_devices.1 @@ -9,9 +9,6 @@ The UHD package is the universal hardware driver for Ettus Research products. The goal is to provide a host driver and API for current and future Ettus Research products. Users will be able to use the UHD driver standalone or with 3rd party applications. -.LP -Hardware supporting UHD drivers includes the Universal Software Radio -Peripheral, or USRP, available in several models. .SH SYNOPSIS .B uhd_find_devices [OPTIONS] .SH OPTIONS diff --git a/host/docs/uhd_images_downloader.1 b/host/docs/uhd_images_downloader.1 index ece826cb5..28310dd37 100644 --- a/host/docs/uhd_images_downloader.1 +++ b/host/docs/uhd_images_downloader.1 @@ -7,9 +7,6 @@ products. The goal of the UHD is to provide a host driver and API for current and future Ettus Research products. Users will be able to use the UHD driver standalone or with 3rd party applications. .LP -Hardware supporting UHD drivers includes the Universal Software Radio -Peripheral, or USRP, available in several models. -.LP This program installs the firmware and FPGA binaries from Ettus Research matching this UHD driver release onto the host system. If the uhd-images package is installed, there is no need to run this installer. diff --git a/host/docs/uhd_usrp_probe.1 b/host/docs/uhd_usrp_probe.1 index 7e82bf99a..a6c7150ee 100644 --- a/host/docs/uhd_usrp_probe.1 +++ b/host/docs/uhd_usrp_probe.1 @@ -1,6 +1,6 @@ .TH "uhd_usrp_probe" 1 "3.5.1" UHD "User Commands" .SH NAME -uhd_usrp_probe \- Universal Hardware Driver Peripheral Report Utility +uhd_usrp_probe \- USRP Hardware Driver Peripheral Report Utility .SH DESCRIPTION Report detailed information on UHD-supported Software Radio Peripherals attached by USB, network, or embedded configuration. diff --git a/host/examples/network_relay.cpp b/host/examples/network_relay.cpp index bb09296b3..0a67bbf09 100644 --- a/host/examples/network_relay.cpp +++ b/host/examples/network_relay.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2013 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 @@ -18,6 +18,7 @@ #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> #include <boost/program_options.hpp> +#include <boost/thread/condition_variable.hpp> #include <boost/thread/thread.hpp> #include <boost/format.hpp> #include <boost/asio.hpp> @@ -33,8 +34,6 @@ typedef boost::shared_ptr<asio::ip::udp::socket> socket_type; static const size_t insane_mtu = 9000; -boost::mutex spawn_mutex; - #if defined(UHD_PLATFORM_MACOS) //limit buffer resize on macos or it will error const size_t rx_dsp_buff_size = size_t(1e6); @@ -100,14 +99,11 @@ public: } std::cout << "spawning relay threads... " << _port << std::endl; + boost::unique_lock<boost::mutex> lock(spawn_mutex); // lock in preparation to wait for threads to spawn _thread_group.create_thread(boost::bind(&udp_relay_type::server_thread, this)); - spawn_mutex.lock(); - spawn_mutex.lock(); - spawn_mutex.unlock(); + wait_for_thread.wait(lock); // wait for thread to spin up _thread_group.create_thread(boost::bind(&udp_relay_type::client_thread, this)); - spawn_mutex.lock(); - spawn_mutex.lock(); - spawn_mutex.unlock(); + wait_for_thread.wait(lock); // wait for thread to spin up std::cout << " done!" << std::endl << std::endl; } @@ -128,7 +124,7 @@ private: void server_thread(void){ uhd::set_thread_priority_safe(); std::cout << " entering server_thread..." << std::endl; - spawn_mutex.unlock(); + wait_for_thread.notify_one(); // notify constructor that this thread has started std::vector<char> buff(insane_mtu); while (not boost::this_thread::interruption_requested()){ if (wait_for_recv_ready(_server_socket->native())){ @@ -154,7 +150,7 @@ private: void client_thread(void){ uhd::set_thread_priority_safe(); std::cout << " entering client_thread..." << std::endl; - spawn_mutex.unlock(); + wait_for_thread.notify_one(); // notify constructor that this thread has started std::vector<char> buff(insane_mtu); while (not boost::this_thread::interruption_requested()){ if (wait_for_recv_ready(_client_socket->native())){ @@ -172,6 +168,8 @@ private: asio::ip::udp::endpoint _endpoint; boost::mutex _endpoint_mutex; socket_type _server_socket, _client_socket; + boost::mutex spawn_mutex; + boost::condition_variable wait_for_thread; }; diff --git a/host/examples/rx_samples_to_file.cpp b/host/examples/rx_samples_to_file.cpp index b0e498125..a28d1d628 100644 --- a/host/examples/rx_samples_to_file.cpp +++ b/host/examples/rx_samples_to_file.cpp @@ -236,8 +236,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)") ("wirefmt", po::value<std::string>(&wirefmt)->default_value("sc16"), "wire format (sc8 or sc16)") ("setup", po::value<double>(&setup_time)->default_value(1.0), "seconds of setup time") - ("progress", "show average bandwidth on exit") - ("stats", "periodically display short-term bandwidth") + ("progress", "periodically display short-term bandwidth") + ("stats", "show average bandwidth on exit") ("sizemap", "track packet size and display breakdown on exit") ("null", "run without writing to file") ("continue", "don't abort on a bad packet") diff --git a/host/examples/test_dboard_coercion.cpp b/host/examples/test_dboard_coercion.cpp index cfc745147..86c59d9d7 100644 --- a/host/examples/test_dboard_coercion.cpp +++ b/host/examples/test_dboard_coercion.cpp @@ -53,7 +53,7 @@ std::string return_USRP_config_string(uhd::usrp::multi_usrp::sptr usrp, bool tes if(test_tx){ if(tx_info.get("tx_serial") != "") tx_serial = tx_info.get("tx_serial"); - else tx_serial = "no serial"; + else tx_serial = "no serial"; tx_subdev_name = tx_info.get("tx_subdev_name"); tx_subdev_spec = tx_info.get("tx_subdev_spec"); @@ -121,7 +121,7 @@ std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo gains.push_back(current_gain); current_gain++; } - if(gain_end != *gains.end()) gains.push_back(gain_end); + gains.push_back(gain_end); } @@ -182,19 +182,19 @@ std::string tx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo } if(test_gain){ - + //Testing for successful gain tune for(std::vector<double>::iterator g = gains.begin(); g != gains.end(); ++g){ usrp->set_tx_gain(*g); boost::this_thread::sleep(boost::posix_time::microseconds(1000)); - + double actual_gain = usrp->get_tx_gain(); if(*g == 0.0){ if(actual_gain == 0.0){ if(verbose) std::cout << boost::format("TX gain successfully set to %5.2f at TX frequency %s.") % *g % return_MHz_string(*f) << std::endl; - } + } else{ if(verbose) std::cout << boost::format("TX gain set to %5.2f instead of %5.2f at TX frequency %s.") % actual_gain % *g % return_MHz_string(*f) << std::endl; std::vector<double> bad_gain_freq; @@ -313,7 +313,7 @@ std::string rx_test(uhd::usrp::multi_usrp::sptr usrp, bool test_gain, bool verbo gains.push_back(current_gain); current_gain++; } - if(gain_end != *gains.end()) gains.push_back(gain_end); + gains.push_back(gain_end); } @@ -518,7 +518,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << "REF must equal internal, external, or mimo." << std::endl; return ~0; } - + if(vm.count("tx") + vm.count("rx") == 0){ std::cout << desc << std::endl; std::cout << "Specify --tx to test for TX frequency coercion\n" @@ -557,12 +557,12 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked",0); std::cout << boost::format("Checking MIMO lock: %s ...") % mimo_locked.to_pp_string() << std::endl; UHD_ASSERT_THROW(mimo_locked.to_bool()); - } + } if ((ref == "external") and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end())) { uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked",0); std::cout << boost::format("Checking REF lock: %s ...") % ref_locked.to_pp_string() << std::endl; UHD_ASSERT_THROW(ref_locked.to_bool()); - } + } usrp_config = return_USRP_config_string(usrp, test_tx, test_rx); if(test_tx) tx_results = tx_test(usrp, test_tx_gain, verbose); if(test_rx) rx_results = rx_test(usrp, test_rx_gain, verbose); diff --git a/host/examples/test_messages.cpp b/host/examples/test_messages.cpp index e39a8bd30..4240e830b 100644 --- a/host/examples/test_messages.cpp +++ b/host/examples/test_messages.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2013 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/config.hpp> #include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/utils/static.hpp> @@ -84,7 +85,7 @@ bool test_late_command_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streame * Issue a stream command with num samps and more. * We expect to get an inline broken chain message. */ -bool test_broken_chain_message(uhd::usrp::multi_usrp::sptr usrp, uhd::rx_streamer::sptr rx_stream, uhd::tx_streamer::sptr){ +bool test_broken_chain_message(UHD_UNUSED(uhd::usrp::multi_usrp::sptr usrp), uhd::rx_streamer::sptr rx_stream, uhd::tx_streamer::sptr){ std::cout << "Test broken chain message... " << std::flush; uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE); diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index 6fd2932cf..619bd0787 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -55,18 +55,21 @@ typedef ptrdiff_t ssize_t; #define UHD_INLINE __forceinline #define UHD_DEPRECATED __declspec(deprecated) #define UHD_ALIGNED(x) __declspec(align(x)) + #define UHD_UNUSED(x) x #elif defined(__GNUG__) && __GNUG__ >= 4 #define UHD_EXPORT __attribute__((visibility("default"))) #define UHD_IMPORT __attribute__((visibility("default"))) #define UHD_INLINE inline __attribute__((always_inline)) #define UHD_DEPRECATED __attribute__((deprecated)) #define UHD_ALIGNED(x) __attribute__((aligned(x))) + #define UHD_UNUSED(x) x __attribute__((unused)) #else #define UHD_EXPORT #define UHD_IMPORT #define UHD_INLINE inline #define UHD_DEPRECATED #define UHD_ALIGNED(x) + #define UHD_UNUSED(x) x #endif // Define API declaration macro diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index 2951d7fbb..fdea9e2be 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -73,6 +73,12 @@ public: virtual boost::uint16_t get_product_id() const = 0; /*! + * Test whether the firmware is loaded on the device. + * \return true if firmware is loaded + */ + virtual bool firmware_loaded() = 0; + + /*! * Return a vector of USB devices on this host * \return a vector of USB device handles that match vid and pid */ diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 1dc0e8e26..40d7b8c59 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -29,7 +29,9 @@ namespace uhd{ namespace transport{ //! Simple managed buffer with release interface class UHD_API managed_buffer{ public: - managed_buffer(void):_ref_count(0){} + managed_buffer(void):_ref_count(0),_buffer(NULL),_length(0){} + + virtual ~managed_buffer(void) {} /*! * Signal to the transport that we are done with the buffer. @@ -72,6 +74,7 @@ namespace uhd{ namespace transport{ } boost::detail::atomic_count _ref_count; + typedef boost::intrusive_ptr<managed_buffer> sptr; protected: void *_buffer; diff --git a/host/include/uhd/types/CMakeLists.txt b/host/include/uhd/types/CMakeLists.txt index 28e646117..334f1b41b 100644 --- a/host/include/uhd/types/CMakeLists.txt +++ b/host/include/uhd/types/CMakeLists.txt @@ -33,6 +33,7 @@ UHD_INSTALL(FILES time_spec.hpp tune_request.hpp tune_result.hpp + wb_iface.hpp DESTINATION ${INCLUDE_DIR}/uhd/types COMPONENT headers ) diff --git a/host/lib/usrp/cores/wb_iface.hpp b/host/include/uhd/types/wb_iface.hpp index 197788180..c508062e4 100644 --- a/host/lib/usrp/cores/wb_iface.hpp +++ b/host/include/uhd/types/wb_iface.hpp @@ -15,19 +15,24 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#ifndef INCLUDED_LIBUHD_USRP_WB_IFACE_HPP -#define INCLUDED_LIBUHD_USRP_WB_IFACE_HPP +#ifndef INCLUDED_UHD_TYPES_WB_IFACE_HPP +#define INCLUDED_UHD_TYPES_WB_IFACE_HPP #include <uhd/config.hpp> #include <boost/cstdint.hpp> #include <boost/shared_ptr.hpp> -class /*UHD_API*/ wb_iface +namespace uhd +{ + +class UHD_API wb_iface { public: typedef boost::shared_ptr<wb_iface> sptr; typedef boost::uint32_t wb_addr_type; + virtual ~wb_iface(void); + /*! * Write a register (64 bits) * \param addr the address @@ -72,4 +77,6 @@ public: }; -#endif /* INCLUDED_LIBUHD_USRP_WB_IFACE_HPP */ +} //namespace uhd + +#endif /* INCLUDED_UHD_TYPES_WB_IFACE_HPP */ diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index cdef2e946..e86826435 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -27,6 +27,7 @@ UHD_INSTALL(FILES images.hpp log.hpp msg.hpp + msg_task.hpp paths.hpp pimpl.hpp safe_call.hpp diff --git a/host/include/uhd/utils/atomic.hpp b/host/include/uhd/utils/atomic.hpp index 8ddee73ca..55769d2fd 100644 --- a/host/include/uhd/utils/atomic.hpp +++ b/host/include/uhd/utils/atomic.hpp @@ -78,6 +78,10 @@ namespace uhd{ class UHD_API reusable_barrier{ public: + reusable_barrier():_size (0) {} + + reusable_barrier(const size_t size):_size(size) {} + //! Resize the barrier for N threads void resize(const size_t size){ _size = size; diff --git a/host/include/uhd/utils/msg_task.hpp b/host/include/uhd/utils/msg_task.hpp new file mode 100644 index 000000000..ebb29af08 --- /dev/null +++ b/host/include/uhd/utils/msg_task.hpp @@ -0,0 +1,74 @@ +// +// Copyright 2011-2013 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_UHD_UTILS_MSG_TASK_HPP +#define INCLUDED_UHD_UTILS_MSG_TASK_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> +#include <boost/utility.hpp> +#include <boost/optional/optional.hpp> +#include <vector> + +namespace uhd{ + class UHD_API msg_task : boost::noncopyable{ + public: + typedef boost::shared_ptr<msg_task> sptr; + typedef std::vector<uint8_t> msg_payload_t; + typedef std::pair<uint32_t, msg_payload_t > msg_type_t; + typedef boost::function<boost::optional<msg_type_t>(void)> task_fcn_type; + + /* + * During shutdown message queues for radio control cores might not be available anymore. + * Such stranded messages get pushed into a dump queue. + * With this function radio_ctrl_core can check if one of the messages meant for it got stranded. + */ + virtual msg_payload_t get_msg_from_dump_queue(boost::uint32_t sid) = 0; + + inline static std::vector<uint8_t> buff_to_vector(uint8_t* p, size_t n) { + if(p and n > 0){ + std::vector<uint8_t> v(n); + memcpy(&v.front(), p, n); + return v; + } + return std::vector<uint8_t>(); + } + + /*! + * Create a new task object with function callback. + * The task function callback will be run in a loop. + * until the thread is interrupted by the deconstructor. + * + * A function may return payload which is then pushed to + * a synchronized message queue. + * + * A task should return in a reasonable amount of time + * or may block forever under the following conditions: + * - The blocking call is interruptible. + * - The task polls the interrupt condition. + * + * \param task_fcn the task callback function + * \return a new task object + */ + static sptr make(const task_fcn_type &task_fcn); + }; +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_MSG_TASK_HPP */ + diff --git a/host/include/uhd/utils/tasks.hpp b/host/include/uhd/utils/tasks.hpp index dcb003e39..a1f682a83 100644 --- a/host/include/uhd/utils/tasks.hpp +++ b/host/include/uhd/utils/tasks.hpp @@ -46,7 +46,6 @@ namespace uhd{ static sptr make(const task_fcn_type &task_fcn); }; - } //namespace uhd #endif /* INCLUDED_UHD_UTILS_TASKS_HPP */ diff --git a/host/lib/convert/convert_fc32_item32.cpp b/host/lib/convert/convert_fc32_item32.cpp index 29bfefd46..641fc2608 100644 --- a/host/lib/convert/convert_fc32_item32.cpp +++ b/host/lib/convert/convert_fc32_item32.cpp @@ -28,7 +28,7 @@ typedef boost::uint32_t (*to32_type)(boost::uint32_t); template <typename type, to32_type tohost> struct convert_fc32_item32_1_to_star_1 : public converter { - convert_fc32_item32_1_to_star_1(void) + convert_fc32_item32_1_to_star_1(void):_scalar(0.0) { //NOP } @@ -48,9 +48,9 @@ struct convert_fc32_item32_1_to_star_1 : public converter { const item32_t i32 = tohost(input[i++]); const item32_t q32 = tohost(input[i++]); - const float i_f32 = reinterpret_cast<const float &>(i32); - const float q_f32 = reinterpret_cast<const float &>(q32); - output[o] = std::complex<type>(type(i_f32*_scalar), type(q_f32*_scalar)); + const float *i_f32p = reinterpret_cast<const float *>(&i32); + const float *q_f32p = reinterpret_cast<const float *>(&q32); + output[o] = std::complex<type>(type((*i_f32p)*_scalar), type((*q_f32p)*_scalar)); } } @@ -60,7 +60,7 @@ struct convert_fc32_item32_1_to_star_1 : public converter template <typename type, to32_type towire> struct convert_star_1_to_fc32_item32_1 : public converter { - convert_star_1_to_fc32_item32_1(void) + convert_star_1_to_fc32_item32_1(void):_scalar(0.0) { //NOP } @@ -80,9 +80,10 @@ struct convert_star_1_to_fc32_item32_1 : public converter { const float i_f32 = type(input[i].real()*_scalar); const float q_f32 = type(input[i].imag()*_scalar); - const item32_t i32 = towire(reinterpret_cast<const item32_t &>(i_f32)); - const item32_t q32 = towire(reinterpret_cast<const item32_t &>(q_f32)); - output[o++] = i32; output[o++] = q32; + const item32_t *i32p = reinterpret_cast<const item32_t *>(&i_f32); + const item32_t *q32p = reinterpret_cast<const item32_t *>(&q_f32); + output[o++] = towire(*i32p); + output[o++] = towire(*q32p); } } diff --git a/host/lib/convert/convert_pack_sc12.cpp b/host/lib/convert/convert_pack_sc12.cpp index 680814994..92cd5d152 100644 --- a/host/lib/convert/convert_pack_sc12.cpp +++ b/host/lib/convert/convert_pack_sc12.cpp @@ -67,7 +67,7 @@ void convert_star_4_to_sc12_item32_3 template <typename type, towire32_type towire> struct convert_star_1_to_sc12_item32_1 : public converter { - convert_star_1_to_sc12_item32_1(void) + convert_star_1_to_sc12_item32_1(void):_scalar(0.0) { //NOP } diff --git a/host/lib/convert/convert_unpack_sc12.cpp b/host/lib/convert/convert_unpack_sc12.cpp index f578b6c95..a2aec2ae5 100644 --- a/host/lib/convert/convert_unpack_sc12.cpp +++ b/host/lib/convert/convert_unpack_sc12.cpp @@ -32,6 +32,17 @@ struct item32_sc12_3x item32_t line2; }; +/* + * convert_sc12_item32_3_to_star_4 takes in 3 lines with 32 bit each + * and converts them 4 samples of type 'std::complex<type>'. + * The structure of the 3 lines is as follows: + * _ _ _ _ _ _ _ _ + * |_ _ _1_ _ _|_ _| + * |_2_ _ _|_ _ _3_| + * |_ _|_ _ _4_ _ _| + * + * The numbers mark the position of one complex sample. + */ template <typename type, tohost32_type tohost> void convert_sc12_item32_3_to_star_4 ( @@ -73,7 +84,7 @@ void convert_sc12_item32_3_to_star_4 template <typename type, tohost32_type tohost> struct convert_sc12_item32_1_to_star_1 : public converter { - convert_sc12_item32_1_to_star_1(void) + convert_sc12_item32_1_to_star_1(void):_scalar(0.0) { //NOP } @@ -84,17 +95,48 @@ struct convert_sc12_item32_1_to_star_1 : public converter _scalar = scalar/unpack_growth; } + /* + * This converter takes in 24 bits complex samples, 12 bits I and 12 bits Q, and converts them to type 'std::complex<type>'. + * 'type' is usually 'float'. + * For the converter to work correctly the used managed_buffer which holds all samples of one packet has to be 32 bits aligned. + * We assume 32 bits to be one line. This said the converter must be aware where it is supposed to start within 3 lines. + * + */ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps) { - const item32_sc12_3x *input = reinterpret_cast<const item32_sc12_3x *>(size_t(inputs[0]) & ~0x3); + /* + * Looking at the line structure above we can identify 4 cases. + * Each corresponds to the start of a different sample within a 3 line block. + * head_samps derives the number of samples left within one block. + * Then the number of bytes the converter has to rewind are calculated. + */ + const size_t head_samps = size_t(inputs[0]) & 0x3; + size_t rewind = 0; + switch(head_samps) + { + case 0: break; + case 1: rewind = 9; break; + case 2: rewind = 6; break; + case 3: rewind = 3; break; + } + + /* + * The pointer *input now points to the head of a 3 line block. + */ + const item32_sc12_3x *input = reinterpret_cast<const item32_sc12_3x *>(size_t(inputs[0]) - rewind); std::complex<type> *output = reinterpret_cast<std::complex<type> *>(outputs[0]); //helper variables std::complex<type> dummy0, dummy1, dummy2; size_t i = 0, o = 0; - //handle the head case - const size_t head_samps = size_t(inputs[0]) & 0x3; + /* + * handle the head case + * head_samps holds the number of samples left in a block. + * The 3 line converter is called for the whole block and already processed samples are dumped. + * We don't run into the risk of a SIGSEGV because input will always point to valid memory within a managed_buffer. + * Furthermore the bytes in a buffer remain unchanged after they have been copied into it. + */ switch (head_samps) { case 0: break; //no head @@ -111,7 +153,18 @@ struct convert_sc12_item32_1_to_star_1 : public converter i++; o += 4; } - //handle the tail case + /* + * handle the tail case + * The converter can be called with any number of samples to be converted. + * This can end up in only a part of a block to be converted in one call. + * We never have to worry about SIGSEGVs here as long as we end in the middle of a managed_buffer. + * If we are at the end of managed_buffer there are 2 precautions to prevent SIGSEGVs. + * Firstly only a read operation is performed. + * Secondly managed_buffers allocate a fixed size memory which is always larger than the actually used size. + * e.g. The current sample maximum is 2000 samples in a packet over USB. + * With sc12 samples a packet consists of 6000kb but managed_buffers allocate 16kb each. + * Thus we don't run into problems here either. + */ const size_t tail_samps = nsamps - o; switch (tail_samps) { diff --git a/host/lib/convert/sse2_fc32_to_sc16.cpp b/host/lib/convert/sse2_fc32_to_sc16.cpp index 90bf0ed04..69786d7ce 100644 --- a/host/lib/convert/sse2_fc32_to_sc16.cpp +++ b/host/lib/convert/sse2_fc32_to_sc16.cpp @@ -27,6 +27,7 @@ DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ const __m128 scalar = _mm_set_ps1(float(scale_factor)); + // this macro converts values faster by using SSE intrinsics to convert 4 values at a time #define convert_fc32_1_to_item32_1_nswap_guts(_al_) \ for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ @@ -48,17 +49,25 @@ DECLARE_CONVERTER(fc32, 1, sc16_item32_le, 1, PRIORITY_SIMD){ size_t i = 0; - //dispatch according to alignment + // need to dispatch according to alignment for fastest conversion switch (size_t(input) & 0xf){ - case 0x8: - xx_to_item32_sc16<uhd::htowx>(input, output, 1, scale_factor); i++; case 0x0: + // the data is 16-byte aligned, so do the fast processing of the bulk of the samples + convert_fc32_1_to_item32_1_nswap_guts(_) + break; + case 0x8: + // the first sample is 8-byte aligned - process it to align the remainder of the samples to 16-bytes + xx_to_item32_sc16<uhd::htowx>(input, output, 1, scale_factor); + i++; + // do faster processing of the bulk of the samples now that we are 16-byte aligned convert_fc32_1_to_item32_1_nswap_guts(_) break; - default: convert_fc32_1_to_item32_1_nswap_guts(u_) + default: + // we are not 8 or 16-byte aligned, so do fast processing with the unaligned load + convert_fc32_1_to_item32_1_nswap_guts(u_) } - //convert remainder + // convert any remaining samples xx_to_item32_sc16<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } @@ -68,6 +77,7 @@ DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){ const __m128 scalar = _mm_set_ps1(float(scale_factor)); + // this macro converts values faster by using SSE intrinsics to convert 4 values at a time #define convert_fc32_1_to_item32_1_bswap_guts(_al_) \ for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ @@ -88,16 +98,24 @@ DECLARE_CONVERTER(fc32, 1, sc16_item32_be, 1, PRIORITY_SIMD){ size_t i = 0; - //dispatch according to alignment + // need to dispatch according to alignment for fastest conversion switch (size_t(input) & 0xf){ - case 0x8: - xx_to_item32_sc16<uhd::htonx>(input, output, 1, scale_factor); i++; case 0x0: + // the data is 16-byte aligned, so do the fast processing of the bulk of the samples + convert_fc32_1_to_item32_1_bswap_guts(_) + break; + case 0x8: + // the first value is 8-byte aligned - process it and prepare the bulk of the data for fast conversion + xx_to_item32_sc16<uhd::htonx>(input, output, 1, scale_factor); + i++; + // do faster processing of the remaining samples now that we are 16-byte aligned convert_fc32_1_to_item32_1_bswap_guts(_) break; - default: convert_fc32_1_to_item32_1_bswap_guts(u_) + default: + // we are not 8 or 16-byte aligned, so do fast processing with the unaligned load + convert_fc32_1_to_item32_1_bswap_guts(u_) } - //convert remainder + // convert any remaining samples xx_to_item32_sc16<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); } diff --git a/host/lib/convert/sse2_sc16_to_fc32.cpp b/host/lib/convert/sse2_sc16_to_fc32.cpp index c03e41585..0ac7f1798 100644 --- a/host/lib/convert/sse2_sc16_to_fc32.cpp +++ b/host/lib/convert/sse2_sc16_to_fc32.cpp @@ -28,6 +28,7 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 16)); const __m128i zeroi = _mm_setzero_si128(); + // this macro converts values faster by using SSE intrinsics to convert 4 values at a time #define convert_item32_1_to_fc32_1_nswap_guts(_al_) \ for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ @@ -50,17 +51,25 @@ DECLARE_CONVERTER(sc16_item32_le, 1, fc32, 1, PRIORITY_SIMD){ size_t i = 0; - //dispatch according to alignment + // need to dispatch according to alignment for fastest conversion switch (size_t(output) & 0xf){ - case 0x8: - item32_sc16_to_xx<uhd::htowx>(input, output, 1, scale_factor); i++; case 0x0: + // the data is 16-byte aligned, so do the fast processing of the bulk of the samples + convert_item32_1_to_fc32_1_nswap_guts(_) + break; + case 0x8: + // the first sample is 8-byte aligned - process it to align the remainder of the samples to 16-bytes + item32_sc16_to_xx<uhd::htowx>(input, output, 1, scale_factor); + i++; + // do faster processing of the bulk of the samples now that we are 16-byte aligned convert_item32_1_to_fc32_1_nswap_guts(_) break; - default: convert_item32_1_to_fc32_1_nswap_guts(u_) + default: + // we are not 8 or 16-byte aligned, so do fast processing with the unaligned load and store + convert_item32_1_to_fc32_1_nswap_guts(u_) } - //convert remainder + // convert any remaining samples item32_sc16_to_xx<uhd::htowx>(input+i, output+i, nsamps-i, scale_factor); } @@ -71,6 +80,7 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ const __m128 scalar = _mm_set_ps1(float(scale_factor)/(1 << 16)); const __m128i zeroi = _mm_setzero_si128(); + // this macro converts values faster by using SSE intrinsics to convert 4 values at a time #define convert_item32_1_to_fc32_1_bswap_guts(_al_) \ for (; i+3 < nsamps; i+=4){ \ /* load from input */ \ @@ -92,16 +102,24 @@ DECLARE_CONVERTER(sc16_item32_be, 1, fc32, 1, PRIORITY_SIMD){ size_t i = 0; - //dispatch according to alignment + // need to dispatch according to alignment for fastest conversion switch (size_t(output) & 0xf){ - case 0x8: - item32_sc16_to_xx<uhd::htonx>(input, output, 1, scale_factor); i++; case 0x0: + // the data is 16-byte aligned, so do the fast processing of the bulk of the samples + convert_item32_1_to_fc32_1_bswap_guts(_) + break; + case 0x8: + // the first sample is 8-byte aligned - process it to align the remainder of the samples to 16-bytes + item32_sc16_to_xx<uhd::htonx>(input, output, 1, scale_factor); + i++; + // do faster processing of the bulk of the samples now that we are 16-byte aligned convert_item32_1_to_fc32_1_bswap_guts(_) break; - default: convert_item32_1_to_fc32_1_bswap_guts(u_) + default: + // we are not 8 or 16-byte aligned, so do fast processing with the unaligned load and store + convert_item32_1_to_fc32_1_bswap_guts(u_) } - //convert remainder + // convert any remaining samples item32_sc16_to_xx<uhd::htonx>(input+i, output+i, nsamps-i, scale_factor); } diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 0ef53db0a..8bd0f4354 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -19,10 +19,12 @@ #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/log.hpp> +#include <uhd/utils/tasks.hpp> #include <uhd/types/dict.hpp> #include <boost/weak_ptr.hpp> #include <boost/thread/mutex.hpp> #include <boost/foreach.hpp> +#include <boost/bind.hpp> #include <cstdlib> #include <iostream> @@ -37,9 +39,11 @@ public: libusb_session_impl(void){ UHD_ASSERT_THROW(libusb_init(&_context) == 0); libusb_set_debug(_context, debug_level); + task_handler = task::make(boost::bind(&libusb_session_impl::libusb_event_handler_task, this, _context)); } ~libusb_session_impl(void){ + task_handler.reset(); libusb_exit(_context); } @@ -49,6 +53,21 @@ public: private: libusb_context *_context; + task::sptr task_handler; + + /* + * Task to handle libusb events. There should only be one thread per libusb_context handling events. + * Using more than one thread can result in excessive CPU usage in kernel space (presumably from locking/waiting). + * The libusb documentation says it is safe, which it is, but it neglects to state the cost in CPU usage. + * Just don't do it! + */ + UHD_INLINE void libusb_event_handler_task(libusb_context *context) + { + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000; + libusb_handle_events_timeout(context, &tv); + } }; libusb::session::sptr libusb::session::get_global_session(void){ @@ -274,6 +293,10 @@ public: return libusb::device_descriptor::make(this->get_device())->get().idProduct; } + bool firmware_loaded() { + return (get_manufacturer() == "Ettus Research LLC"); + } + private: libusb::device::sptr _dev; //always keep a reference to device }; diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 197e257da..2d18e1623 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -73,6 +73,15 @@ struct lut_result_t int completed; libusb_transfer_status status; int actual_length; + boost::mutex mut; + boost::condition_variable usb_transfer_complete; +}; + +// Created to be used as an argument to boost::condition_variable::timed_wait() function +struct lut_result_completed { + const lut_result_t& _result; + lut_result_completed(const lut_result_t& result):_result(result) {} + bool operator()() const {return (_result.completed ? true : false);} }; /*! @@ -84,48 +93,11 @@ struct lut_result_t static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut) { lut_result_t *r = (lut_result_t *)lut->user_data; - r->completed = 1; + boost::lock_guard<boost::mutex> lock(r->mut); r->status = lut->status; r->actual_length = lut->actual_length; -} - -/*! - * Wait for a managed buffer to become complete. - * - * This routine processes async events until the transaction completes. - * We must call the libusb handle events in a loop because the handler - * may complete managed buffers other than the one we are waiting on. - * - * We cannot determine if handle events timed out or processed an event. - * Therefore, the timeout condition is handled by using boost system time. - * - * \param ctx the libusb context structure - * \param timeout the wait timeout in seconds - * \param completed a reference to the completed flag - * \return true for completion, false for timeout - */ -UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, int &completed) -{ - //already completed by a previous call? - if (completed) return true; - - //perform a non-blocking event handle - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; - libusb_handle_events_timeout_completed(ctx, &tv, &completed); - if (completed) return true; - - //finish the rest with a timeout loop - const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); - while (not completed and (boost::get_system_time() < timeout_time)){ - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 10000; /*10ms*/ - libusb_handle_events_timeout_completed(ctx, &tv, &completed); - } - - return completed; + r->completed = 1; + r->usb_transfer_complete.notify_one(); // wake up thread waiting in wait_for_completion() member function below } /*********************************************************************** @@ -154,7 +126,7 @@ public: template <typename buffer_type> UHD_INLINE typename buffer_type::sptr get_new(const double timeout) { - if (wait_for_completion(_ctx, timeout, result.completed)) + if (wait_for_completion(timeout)) { if (result.status != LIBUSB_TRANSFER_COMPLETED) throw uhd::runtime_error(str(boost::format( "usb %s transfer status: %d") % _name % int(result.status))); @@ -164,9 +136,31 @@ public: return typename buffer_type::sptr(); } + // This is public because it is accessed from the libusb_zero_copy_single constructor lut_result_t result; + /*! + * Wait for a managed buffer to become complete. + * + * \param timeout the wait timeout in seconds. A negative value will wait forever. + * \return true for completion, false for timeout + */ + UHD_INLINE bool wait_for_completion(const double timeout) + { + boost::unique_lock<boost::mutex> lock(result.mut); + if (!result.completed) { + if (timeout < 0.0) { + result.usb_transfer_complete.wait(lock); + } else { + const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); + result.usb_transfer_complete.timed_wait(lock, timeout_time, lut_result_completed(result)); + } + } + return result.completed; + } + private: + boost::function<void(libusb_zero_copy_mb *)> _release_cb; const bool _is_recv; const std::string _name; @@ -252,8 +246,6 @@ public: ~libusb_zero_copy_single(void) { - libusb_context *ctx = libusb::session::get_global_session()->get_context(); - //cancel all transfers BOOST_FOREACH(libusb_transfer *lut, _all_luts) { @@ -261,8 +253,10 @@ public: } //process all transfers until timeout occurs - int completed = 0; - wait_for_completion(ctx, 0.01, completed); + BOOST_FOREACH(libusb_zero_copy_mb *mb, _enqueued) + { + mb->wait_for_completion(0.01); + } //free all transfers BOOST_FOREACH(libusb_transfer *lut, _all_luts) @@ -276,19 +270,18 @@ public: { typename buffer_type::sptr buff; libusb_zero_copy_mb *front = NULL; + boost::mutex::scoped_lock lock(_mutex); + if (_enqueued.empty()) { - boost::mutex::scoped_lock l(_mutex); - if (_enqueued.empty()) - { - _cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6))); - } - if (_enqueued.empty()) return buff; - front = _enqueued.front(); + _cond.timed_wait(lock, boost::posix_time::microseconds(long(timeout*1e6))); } + if (_enqueued.empty()) return buff; + front = _enqueued.front(); + lock.unlock(); buff = front->get_new<buffer_type>(timeout); + lock.lock(); - boost::mutex::scoped_lock l(_mutex); if (buff) _enqueued.pop_front(); this->submit_what_we_can(); return buff; diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 688228e49..5080182d6 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -246,7 +246,8 @@ private: struct xport_chan_props_type{ xport_chan_props_type(void): packet_count(0), - handle_overflow(&handle_overflow_nop) + handle_overflow(&handle_overflow_nop), + fc_update_window(0) {} get_buff_type get_buff; issue_stream_cmd_type issue_stream_cmd; diff --git a/host/lib/transport/super_send_packet_handler.hpp b/host/lib/transport/super_send_packet_handler.hpp index 41f030ea6..ae483d1f3 100644 --- a/host/lib/transport/super_send_packet_handler.hpp +++ b/host/lib/transport/super_send_packet_handler.hpp @@ -239,7 +239,7 @@ private: size_t _header_offset_words32; double _tick_rate, _samp_rate; struct xport_chan_props_type{ - xport_chan_props_type(void):has_sid(false){} + xport_chan_props_type(void):has_sid(false),sid(0){} get_buff_type get_buff; bool has_sid; boost::uint32_t sid; diff --git a/host/lib/transport/udp_zero_copy.cpp b/host/lib/transport/udp_zero_copy.cpp index 166177177..7b6a476f5 100644 --- a/host/lib/transport/udp_zero_copy.cpp +++ b/host/lib/transport/udp_zero_copy.cpp @@ -68,7 +68,7 @@ static void check_registry_for_fast_send_threshold(const size_t mtu){ class udp_zero_copy_asio_mrb : public managed_recv_buffer{ public: udp_zero_copy_asio_mrb(void *mem, int sock_fd, const size_t frame_size): - _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size) { /*NOP*/ } + _mem(mem), _sock_fd(sock_fd), _frame_size(frame_size), _len(0) { /*NOP*/ } void release(void){ _claimer.release(); @@ -87,6 +87,7 @@ public: if (wait_for_recv_ready(_sock_fd, timeout)){ _len = ::recv(_sock_fd, (char *)_mem, _frame_size, 0); + UHD_ASSERT_THROW(_len > 0); // TODO: Handle case of recv error index++; //advances the caller's buffer return make(this, _mem, size_t(_len)); } diff --git a/host/lib/types/CMakeLists.txt b/host/lib/types/CMakeLists.txt index 2ca0faef7..b69c8e487 100644 --- a/host/lib/types/CMakeLists.txt +++ b/host/lib/types/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-2013 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 @@ -88,4 +88,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/time_spec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tune.cpp ${CMAKE_CURRENT_SOURCE_DIR}/types.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/wb_iface.cpp ) diff --git a/host/lib/types/tune.cpp b/host/lib/types/tune.cpp index 154f0990f..7697bd966 100644 --- a/host/lib/types/tune.cpp +++ b/host/lib/types/tune.cpp @@ -24,7 +24,9 @@ using namespace uhd; tune_request_t::tune_request_t(double target_freq): target_freq(target_freq), rf_freq_policy(POLICY_AUTO), - dsp_freq_policy(POLICY_AUTO) + rf_freq(0.0), + dsp_freq_policy(POLICY_AUTO), + dsp_freq(0.0) { /* NOP */ } @@ -33,7 +35,8 @@ tune_request_t::tune_request_t(double target_freq, double lo_off): target_freq(target_freq), rf_freq_policy(POLICY_MANUAL), rf_freq(target_freq + lo_off), - dsp_freq_policy(POLICY_AUTO) + dsp_freq_policy(POLICY_AUTO), + dsp_freq(0.0) { /* NOP */ } diff --git a/host/lib/usrp/cores/wb_iface.cpp b/host/lib/types/wb_iface.cpp index 9aa6d18d4..6edfdfe2f 100644 --- a/host/lib/usrp/cores/wb_iface.cpp +++ b/host/lib/types/wb_iface.cpp @@ -15,11 +15,16 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <uhd/exception.hpp> using namespace uhd; +wb_iface::~wb_iface(void) +{ + //NOP +} + void wb_iface::poke64(const wb_iface::wb_addr_type, const boost::uint64_t) { throw uhd::not_implemented_error("poke64 not implemented"); diff --git a/host/lib/usrp/README b/host/lib/usrp/README deleted file mode 100644 index 344209179..000000000 --- a/host/lib/usrp/README +++ /dev/null @@ -1,15 +0,0 @@ -######################################################################## -# lib USRP directories: -######################################################################## - -dboard: - Daughterboard implementation code for all USRP daughterboards - -usrp1: - Implementation code for the USB-based USRP Classic motherboard. - -usrp2: - Implementation code for USRP2, USRP-N200, and USRP-N210. - -usrp_e100: - Implementation code for USRP-E100. diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 19df3d6af..305ba42a7 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -310,12 +310,14 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(b100_codec_ctrl::rx_pga_gain_range); _tree->create<double>(rx_codec_path / "gains/pga/value") - .coerce(boost::bind(&b100_impl::update_rx_codec_gain, this, _1)); + .coerce(boost::bind(&b100_impl::update_rx_codec_gain, this, _1)) + .set(0.0); _tree->create<std::string>(tx_codec_path / "name").set("ad9522"); _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(b100_codec_ctrl::tx_pga_gain_range); _tree->create<double>(tx_codec_path / "gains/pga/value") .subscribe(boost::bind(&b100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) - .publish(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)); + .publish(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)) + .set(0.0); //////////////////////////////////////////////////////////////////// // and do the misc mboard sensors diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index f81aa3568..ab83c80a3 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -67,7 +67,7 @@ static const std::string B100_EEPROM_MAP_KEY = "B100"; //! Make a b100 dboard interface uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( - wb_iface::sptr wb_iface, + uhd::wb_iface::sptr wb_iface, uhd::i2c_iface::sptr i2c_iface, uhd::spi_iface::sptr spi_iface, b100_clock_ctrl::sptr clock, diff --git a/host/lib/usrp/b100/usb_zero_copy_wrapper.cpp b/host/lib/usrp/b100/usb_zero_copy_wrapper.cpp index 2096e4ef4..451cdae50 100644 --- a/host/lib/usrp/b100/usb_zero_copy_wrapper.cpp +++ b/host/lib/usrp/b100/usb_zero_copy_wrapper.cpp @@ -167,6 +167,7 @@ public: usb_zero_copy_wrapper(zero_copy_if::sptr usb_zc, const size_t frame_boundary): _internal_zc(usb_zc), _frame_boundary(frame_boundary), + _last_recv_offset(0), _next_recv_buff_index(0) { for (size_t i = 0; i < this->get_num_recv_frames(); i++){ diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 1d05e159c..5d799bf01 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -17,6 +17,7 @@ #include "b200_iface.hpp" +#include <uhd/config.hpp> #include <uhd/utils/msg.hpp> #include <uhd/exception.hpp> #include <boost/functional/hash.hpp> @@ -62,6 +63,7 @@ const static boost::uint8_t B200_VREQ_FX3_RESET = 0x99; const static boost::uint8_t B200_VREQ_EEPROM_WRITE = 0xBA; const static boost::uint8_t B200_VREQ_EEPROM_READ = 0xBB; +const static boost::uint8_t FX3_STATE_UNDEFINED = 0x00; const static boost::uint8_t FX3_STATE_FPGA_READY = 0x01; const static boost::uint8_t FX3_STATE_CONFIGURING_FPGA = 0x02; const static boost::uint8_t FX3_STATE_BUSY = 0x03; @@ -69,6 +71,11 @@ const static boost::uint8_t FX3_STATE_RUNNING = 0x04; const static boost::uint8_t FX3_STATE_UNCONFIGURED = 0x05; const static boost::uint8_t FX3_STATE_ERROR = 0x06; +const static int VREQ_MAX_SIZE_USB2 = 64; +const static int VREQ_MAX_SIZE_USB3 = 512; +const static int VREQ_DEFAULT_SIZE = VREQ_MAX_SIZE_USB2; +const static int VREQ_MAX_SIZE = VREQ_MAX_SIZE_USB3; + typedef boost::uint32_t hash_type; @@ -83,6 +90,9 @@ typedef boost::uint32_t hash_type; */ static hash_type generate_hash(const char *filename) { + if (filename == NULL) + return hash_type(0); + std::ifstream file(filename); if (not file){ throw uhd::io_error(std::string("cannot open input file ") + filename); @@ -115,15 +125,15 @@ static hash_type generate_hash(const char *filename) * \param record a line from an Intel HEX file * \return true if record is valid, false otherwise */ -bool checksum(std::string *record) { +bool checksum(const std::string& record) { - size_t len = record->length(); + size_t len = record.length(); unsigned int i; unsigned char sum = 0; unsigned int val; for (i = 1; i < len; i += 2) { - std::istringstream(record->substr(i, 2)) >> std::hex >> val; + std::istringstream(record.substr(i, 2)) >> std::hex >> val; sum += val; } @@ -144,22 +154,25 @@ bool checksum(std::string *record) { * \param data output data * \return true if record is sucessfully read, false on error */ -bool parse_record(std::string *record, boost::uint16_t &len, \ +bool parse_record(const std::string& record, boost::uint16_t &len, \ boost::uint16_t &addr, boost::uint16_t &type, unsigned char* data) { unsigned int i; std::string _data; unsigned int val; - if (record->substr(0, 1) != ":") + if (record.substr(0, 1) != ":") return false; - std::istringstream(record->substr(1, 2)) >> std::hex >> len; - std::istringstream(record->substr(3, 4)) >> std::hex >> addr; - std::istringstream(record->substr(7, 2)) >> std::hex >> type; + std::istringstream(record.substr(1, 2)) >> std::hex >> len; + std::istringstream(record.substr(3, 4)) >> std::hex >> addr; + std::istringstream(record.substr(7, 2)) >> std::hex >> type; + + if (len > (2 * (record.length() - 9))) // sanity check to prevent buffer overrun + return false; for (i = 0; i < len; i++) { - std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; + std::istringstream(record.substr(9 + 2 * i, 2)) >> std::hex >> val; data[i] = (unsigned char) val; } @@ -174,19 +187,16 @@ class b200_iface_impl : public b200_iface{ public: b200_iface_impl(usb_control::sptr usb_ctrl): - _usb_ctrl(usb_ctrl) - { + _usb_ctrl(usb_ctrl) { //NOP } - int fx3_control_write(boost::uint8_t request, boost::uint16_t value, boost::uint16_t index, unsigned char *buff, boost::uint16_t length, - boost::int32_t timeout = 0) - { + boost::int32_t timeout = 0) { return _usb_ctrl->submit(VRT_VENDOR_OUT, // bmReqeustType request, // bRequest value, // wValue @@ -196,14 +206,12 @@ public: timeout); // timeout } - int fx3_control_read(boost::uint8_t request, boost::uint16_t value, boost::uint16_t index, unsigned char *buff, boost::uint16_t length, - boost::int32_t timeout = 0) - { + boost::int32_t timeout = 0) { return _usb_ctrl->submit(VRT_VENDOR_IN, // bmReqeustType request, // bRequest value, // wValue @@ -213,37 +221,45 @@ public: timeout); // timeout } - - void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes) + void write_i2c(UHD_UNUSED(boost::uint16_t addr), UHD_UNUSED(const byte_vector_t &bytes)) { throw uhd::not_implemented_error("b200 write i2c"); } - byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes) + byte_vector_t read_i2c(UHD_UNUSED(boost::uint16_t addr), UHD_UNUSED(size_t num_bytes)) { throw uhd::not_implemented_error("b200 read i2c"); } void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, - const byte_vector_t &bytes) - { - fx3_control_write(B200_VREQ_EEPROM_WRITE, + const byte_vector_t &bytes) { + int ret = fx3_control_write(B200_VREQ_EEPROM_WRITE, 0, offset | (boost::uint16_t(addr) << 8), (unsigned char *) &bytes[0], bytes.size()); + + if (ret < 0) + throw uhd::io_error((boost::format("Failed to write EEPROM (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if ((size_t)ret != bytes.size()) + throw uhd::io_error((boost::format("Short write on write EEPROM (expecting: %d, returned: %d)") % bytes.size() % ret).str()); } byte_vector_t read_eeprom( boost::uint16_t addr, boost::uint16_t offset, - size_t num_bytes - ){ + size_t num_bytes) { byte_vector_t recv_bytes(num_bytes); - fx3_control_read(B200_VREQ_EEPROM_READ, + int bytes_read = fx3_control_read(B200_VREQ_EEPROM_READ, 0, offset | (boost::uint16_t(addr) << 8), (unsigned char*) &recv_bytes[0], num_bytes); + + if (bytes_read < 0) + throw uhd::io_error((boost::format("Failed to read EEPROM (%d: %s)") % bytes_read % libusb_error_name(bytes_read)).str()); + else if ((size_t)bytes_read != num_bytes) + throw uhd::io_error((boost::format("Short read on read EEPROM (expecting: %d, returned: %d)") % num_bytes % bytes_read).str()); + return recv_bytes; } @@ -251,8 +267,7 @@ public: unsigned char *tx_data, size_t num_tx_bits, unsigned char *rx_data, - size_t num_rx_bits - ){ + size_t num_rx_bits) { int ret = 0; boost::uint16_t tx_length = num_tx_bits / 8; @@ -264,9 +279,10 @@ public: 0x00, tx_data, tx_length); } - if(ret < 0) { - throw uhd::io_error("transact_spi: fx3_control_write failed!"); - } + if (ret < 0) + throw uhd::io_error((boost::format("Failed to write SPI (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != tx_length) + throw uhd::io_error((boost::format("Short write on write SPI (expecting: %d, returned: %d)") % tx_length % ret).str()); if(num_rx_bits) { @@ -275,25 +291,38 @@ public: ret = fx3_control_read(B200_VREQ_LOOP, 0x00, \ 0x00, rx_data, total_length); - if(ret < 0) { - throw uhd::io_error("transact_spi: readback failed!"); - } + if (ret < 0) + throw uhd::io_error((boost::format("Failed to readback (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != total_length) + throw uhd::io_error((boost::format("Short read on readback (expecting: %d, returned: %d)") % total_length % ret).str()); } } void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) { - fx3_control_write(B200_VREQ_AD9361_CTRL_WRITE, 0x00, 0x00, (unsigned char *)in_buff, 64); - int ret = 0; - for (size_t i = 0; i < 30; i++) + const int bytes_to_write = 64; + const int bytes_to_read = 64; + const size_t read_retries = 30; + + int ret = fx3_control_write(B200_VREQ_AD9361_CTRL_WRITE, 0x00, 0x00, (unsigned char *)in_buff, bytes_to_write); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to write AD9361 (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_write) + throw uhd::io_error((boost::format("Short write on write AD9361 (expecting: %d, returned: %d)") % bytes_to_write % ret).str()); + + for (size_t i = 0; i < read_retries; i++) { - ret = fx3_control_read(B200_VREQ_AD9361_CTRL_READ, 0x00, 0x00, out_buff, 64, 1000); - if (ret == 64) return; + ret = fx3_control_read(B200_VREQ_AD9361_CTRL_READ, 0x00, 0x00, out_buff, bytes_to_read, 1000); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to read AD9361 (%d: %s)") % ret % libusb_error_name(ret)).str()); + + if (ret == bytes_to_read) + return; } - throw uhd::io_error(str(boost::format("ad9361_transact failed with usb error: %d") % ret)); - } + throw uhd::io_error(str(boost::format("Failed to read complete AD9361 (expecting: %d, last read: %d)") % bytes_to_read % ret)); + } - void load_firmware(const std::string filestring, bool force = false) + void load_firmware(const std::string filestring, UHD_UNUSED(bool force) = false) { const char *filename = filestring.c_str(); @@ -323,8 +352,11 @@ public: std::string record; file >> record; + if (!(record.length() > 0)) + continue; + /* Check for valid Intel HEX record. */ - if (!checksum(&record) || !parse_record(&record, len, \ + if (!checksum(record) || !parse_record(record, len, \ lower_address_bits, type, data)) { throw uhd::io_error("fx3_load_firmware: bad intel hex record checksum"); } @@ -397,46 +429,75 @@ public: throw uhd::io_error("fx3_load_firmware: No EOF record found."); } - void reset_fx3(void) { unsigned char data[4]; memset(data, 0x00, sizeof(data)); + const int bytes_to_send = sizeof(data); - fx3_control_write(B200_VREQ_FX3_RESET, 0x00, 0x00, data, 4); + int ret = fx3_control_write(B200_VREQ_FX3_RESET, 0x00, 0x00, data, bytes_to_send); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to reset FX3 (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_send) + throw uhd::io_error((boost::format("Short write on reset FX3 (expecting: %d, returned: %d)") % bytes_to_send % ret).str()); } void reset_gpif(void) { unsigned char data[4]; memset(data, 0x00, sizeof(data)); + const int bytes_to_send = sizeof(data); - fx3_control_write(B200_VREQ_GPIF_RESET, 0x00, 0x00, data, 4); + int ret = fx3_control_write(B200_VREQ_GPIF_RESET, 0x00, 0x00, data, bytes_to_send); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to reset GPIF (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_send) + throw uhd::io_error((boost::format("Short write on reset GPIF (expecting: %d, returned: %d)") % bytes_to_send % ret).str()); } - void set_fpga_reset_pin(const bool reset) - { + void set_fpga_reset_pin(const bool reset) { unsigned char data[4]; memset(data, (reset)? 0xFF : 0x00, sizeof(data)); UHD_THROW_INVALID_CODE_PATH(); - fx3_control_write(B200_VREQ_FPGA_RESET, 0x00, 0x00, data, 4); + // Below is dead code as long as UHD_THROW_INVALID_CODE_PATH(); is declared above. + // It is preserved here in a comment in case it is needed later: + /* + const int bytes_to_send = sizeof(data); + + int ret = fx3_control_write(B200_VREQ_FPGA_RESET, 0x00, 0x00, data, bytes_to_send); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to reset FPGA (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_send) + throw uhd::io_error((boost::format("Short write on reset FPGA (expecting: %d, returned: %d)") % bytes_to_send % ret).str()); + */ } boost::uint8_t get_usb_speed(void) { unsigned char rx_data[1]; + memset(rx_data, 0x00, sizeof(rx_data)); + const int bytes_to_recv = sizeof(rx_data); - fx3_control_read(B200_VREQ_GET_USB, 0x00, 0x00, rx_data, 1); + int ret = fx3_control_read(B200_VREQ_GET_USB, 0x00, 0x00, rx_data, bytes_to_recv); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to get USB speed (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_recv) + throw uhd::io_error((boost::format("Short read on get USB speed (expecting: %d, returned: %d)") % bytes_to_recv % ret).str()); return boost::lexical_cast<boost::uint8_t>(rx_data[0]); } - boost::uint8_t get_fx3_status(void) { unsigned char rx_data[1]; + memset(rx_data, 0x00, sizeof(rx_data)); + const int bytes_to_recv = sizeof(rx_data); - fx3_control_read(B200_VREQ_GET_STATUS, 0x00, 0x00, &rx_data[0], 1); + int ret = fx3_control_read(B200_VREQ_GET_STATUS, 0x00, 0x00, rx_data, bytes_to_recv); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to get FX3 status (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_recv) + throw uhd::io_error((boost::format("Short read on get FX3 status (expecting: %d, returned: %d)") % bytes_to_recv % ret).str()); return boost::lexical_cast<boost::uint8_t>(rx_data[0]); } @@ -444,40 +505,72 @@ public: boost::uint16_t get_compat_num(void) { unsigned char rx_data[2]; + memset(rx_data, 0x00, sizeof(rx_data)); + const int bytes_to_recv = sizeof(rx_data); - fx3_control_read(B200_VREQ_GET_COMPAT , 0x00, 0x00, rx_data, 2); - - boost::uint16_t compat = 0x0000; - compat |= (((uint16_t) rx_data[0]) << 8); - compat |= (rx_data[1] & 0x00FF); + int ret = fx3_control_read(B200_VREQ_GET_COMPAT , 0x00, 0x00, rx_data, bytes_to_recv); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to get compat num (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_recv) + throw uhd::io_error((boost::format("Short read on get compat num (expecting: %d, returned: %d)") % bytes_to_recv % ret).str()); - return compat; + return (((uint16_t)rx_data[0]) << 8) | rx_data[1]; } void usrp_get_firmware_hash(hash_type &hash) { - fx3_control_read(B200_VREQ_GET_FW_HASH, 0x00, 0x00, - (unsigned char*) &hash, 4, 500); + const int bytes_to_recv = 4; + if (sizeof(hash_type) != bytes_to_recv) + throw uhd::type_error((boost::format("hash_type is %d bytes but transfer length is %d bytes") % sizeof(hash_type) % bytes_to_recv).str()); + + int ret = fx3_control_read(B200_VREQ_GET_FW_HASH, 0x00, 0x00, (unsigned char*) &hash, bytes_to_recv, 500); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to get firmware hash (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_recv) + throw uhd::io_error((boost::format("Short read on get firmware hash (expecting: %d, returned: %d)") % bytes_to_recv % ret).str()); } void usrp_set_firmware_hash(hash_type hash) { - fx3_control_write(B200_VREQ_SET_FW_HASH, 0x00, 0x00, - (unsigned char*) &hash, 4); + const int bytes_to_send = 4; + if (sizeof(hash_type) != bytes_to_send) + throw uhd::type_error((boost::format("hash_type is %d bytes but transfer length is %d bytes") % sizeof(hash_type) % bytes_to_send).str()); + + int ret = fx3_control_write(B200_VREQ_SET_FW_HASH, 0x00, 0x00, (unsigned char*) &hash, bytes_to_send); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to set firmware hash (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_send) + throw uhd::io_error((boost::format("Short write on set firmware hash (expecting: %d, returned: %d)") % bytes_to_send % ret).str()); } void usrp_get_fpga_hash(hash_type &hash) { - fx3_control_read(B200_VREQ_GET_FPGA_HASH, 0x00, 0x00, - (unsigned char*) &hash, 4, 500); + const int bytes_to_recv = 4; + if (sizeof(hash_type) != bytes_to_recv) + throw uhd::type_error((boost::format("hash_type is %d bytes but transfer length is %d bytes") % sizeof(hash_type) % bytes_to_recv).str()); + + int ret = fx3_control_read(B200_VREQ_GET_FPGA_HASH, 0x00, 0x00, (unsigned char*) &hash, bytes_to_recv, 500); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to get FPGA hash (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_recv) + throw uhd::io_error((boost::format("Short read on get FPGA hash (expecting: %d, returned: %d)") % bytes_to_recv % ret).str()); } void usrp_set_fpga_hash(hash_type hash) { - fx3_control_write(B200_VREQ_SET_FPGA_HASH, 0x00, 0x00, - (unsigned char*) &hash, 4); + const int bytes_to_send = 4; + if (sizeof(hash_type) != bytes_to_send) + throw uhd::type_error((boost::format("hash_type is %d bytes but transfer length is %d bytes") % sizeof(hash_type) % bytes_to_send).str()); + + int ret = fx3_control_write(B200_VREQ_SET_FPGA_HASH, 0x00, 0x00, (unsigned char*) &hash, bytes_to_send); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to set FPGA hash (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_send) + 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::uint8_t fx3_state = 0; boost::uint32_t wait_count; + int ret = 0; + int bytes_to_xfer = 0; const char *filename = filestring.c_str(); @@ -485,9 +578,27 @@ public: hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash); if (hash == loaded_hash) return 0; - unsigned char out_buff[64]; - memset(out_buff, 0x00, sizeof(out_buff)); - fx3_control_write(B200_VREQ_FPGA_CONFIG, 0, 0, out_buff, 1, 1000); + // Establish default largest possible control request transfer size based on operating USB speed + int transfer_size = VREQ_DEFAULT_SIZE; + int current_usb_speed = get_usb_speed(); + if (current_usb_speed == 3) + transfer_size = VREQ_MAX_SIZE_USB3; + else if (current_usb_speed != 2) + throw uhd::io_error("load_fpga: get_usb_speed returned invalid USB speed (not 2 or 3)."); + + UHD_ASSERT_THROW(transfer_size <= VREQ_MAX_SIZE); + + unsigned char out_buff[VREQ_MAX_SIZE]; + + // Request loopback read, which will indicate the firmware's current control request buffer size + // Make sure that if operating as USB2, requested length is within spec + int ntoread = std::min(transfer_size, (int)sizeof(out_buff)); + int nread = fx3_control_read(B200_VREQ_LOOP, 0, 0, out_buff, ntoread, 1000); + if (nread < 0) + throw uhd::io_error((boost::format("load_fpga: unable to complete firmware loopback request (%d: %s)") % nread % libusb_error_name(nread)).str()); + else if (nread != ntoread) + throw uhd::io_error((boost::format("load_fpga: short read on firmware loopback request (expecting: %d, returned: %d)") % ntoread % nread).str()); + transfer_size = std::min(transfer_size, nread); // Select the smaller value size_t file_size = 0; { @@ -498,15 +609,26 @@ public: std::ifstream file; file.open(filename, std::ios::in | std::ios::binary); - if(!file.good()) { + if (!file.good()) { throw uhd::io_error("load_fpga: cannot open FPGA input file."); } + // Zero the hash, in case we abort programming another image and revert to the previously programmed image + usrp_set_fpga_hash(0); + + memset(out_buff, 0x00, sizeof(out_buff)); + bytes_to_xfer = 1; + ret = fx3_control_write(B200_VREQ_FPGA_CONFIG, 0, 0, out_buff, bytes_to_xfer, 1000); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to start FPGA config (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_xfer) + throw uhd::io_error((boost::format("Short write on start FPGA config (expecting: %d, returned: %d)") % bytes_to_xfer % ret).str()); + wait_count = 0; do { fx3_state = get_fx3_status(); - if((wait_count >= 500) || (fx3_state == FX3_STATE_ERROR)) { + if((wait_count >= 500) || (fx3_state == FX3_STATE_ERROR) || (fx3_state == FX3_STATE_UNDEFINED)) { return fx3_state; } @@ -518,13 +640,18 @@ public: if (load_img_msg) UHD_MSG(status) << "Loading FPGA image: " \ << filestring << "..." << std::flush; - fx3_control_write(B200_VREQ_FPGA_START, 0, 0, out_buff, 1, 1000); + bytes_to_xfer = 1; + ret = fx3_control_write(B200_VREQ_FPGA_START, 0, 0, out_buff, bytes_to_xfer, 1000); + if (ret < 0) + throw uhd::io_error((boost::format("Failed to start FPGA bitstream (%d: %s)") % ret % libusb_error_name(ret)).str()); + else if (ret != bytes_to_xfer) + throw uhd::io_error((boost::format("Short write on start FPGA bitstream (expecting: %d, returned: %d)") % bytes_to_xfer % ret).str()); wait_count = 0; do { fx3_state = get_fx3_status(); - if((wait_count >= 1000) || (fx3_state == FX3_STATE_ERROR)) { + if((wait_count >= 1000) || (fx3_state == FX3_STATE_ERROR) || (fx3_state == FX3_STATE_UNDEFINED)) { return fx3_state; } @@ -534,15 +661,20 @@ public: } while(fx3_state != FX3_STATE_CONFIGURING_FPGA); size_t bytes_sent = 0; - while(!file.eof()) { - file.read((char *) out_buff, sizeof(out_buff)); + while (!file.eof()) { + file.read((char *) out_buff, transfer_size); const std::streamsize n = file.gcount(); - if(n == 0) continue; + if(n == 0) + continue; boost::uint16_t transfer_count = boost::uint16_t(n); /* Send the data to the device. */ - fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000); + int nwritten = fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000); + if (nwritten < 0) + throw uhd::io_error((boost::format("load_fpga: cannot write bitstream to FX3 (%d: %s)") % nwritten % libusb_error_name(nwritten)).str()); + else if (nwritten != transfer_count) + throw uhd::io_error((boost::format("load_fpga: short write while transferring bitstream to FX3 (expecting: %d, returned: %d)") % transfer_count % nwritten).str()); if (load_img_msg) { @@ -563,7 +695,7 @@ public: do { fx3_state = get_fx3_status(); - if((wait_count >= 500) || (fx3_state == FX3_STATE_ERROR)) { + if((wait_count >= 500) || (fx3_state == FX3_STATE_ERROR) || (fx3_state == FX3_STATE_UNDEFINED)) { return fx3_state; } @@ -574,7 +706,8 @@ public: usrp_set_fpga_hash(hash); - if (load_img_msg) UHD_MSG(status) << "\b\b\b\b done" << std::endl; + if (load_img_msg) + UHD_MSG(status) << "\b\b\b\b done" << std::endl; return 0; } @@ -583,6 +716,29 @@ private: usb_control::sptr _usb_ctrl; }; + +std::string b200_iface::fx3_state_string(boost::uint8_t state) +{ + switch (state) + { + case FX3_STATE_FPGA_READY: + return std::string("Ready"); + case FX3_STATE_CONFIGURING_FPGA: + return std::string("Configuring FPGA"); + case FX3_STATE_BUSY: + return std::string("Busy"); + case FX3_STATE_RUNNING: + return std::string("Running"); + case FX3_STATE_UNCONFIGURED: + return std::string("Unconfigured"); + case FX3_STATE_ERROR: + return std::string("Error"); + default: + break; + } + return std::string("Unknown"); +} + /*********************************************************************** * Make an instance of the implementation **********************************************************************/ diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 1247d1f86..20b4a7a89 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -25,7 +25,17 @@ #include <boost/utility.hpp> #include "ad9361_ctrl.hpp" -class b200_iface: boost::noncopyable, public virtual uhd::i2c_iface, +const static boost::uint16_t B200_VENDOR_ID = 0x2500; +const static boost::uint16_t B200_PRODUCT_ID = 0x0020; +const static boost::uint16_t FX3_VID = 0x04b4; +const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; +const static boost::uint16_t FX3_REENUM_PID = 0x00f0; + +static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; +static const std::string B200_FPGA_FILE_NAME = "usrp_b200_fpga.bin"; +static const std::string B210_FPGA_FILE_NAME = "usrp_b210_fpga.bin"; + +class UHD_API b200_iface: boost::noncopyable, public virtual uhd::i2c_iface, public ad9361_ctrl_iface_type { public: typedef boost::shared_ptr<b200_iface> sptr; @@ -64,6 +74,12 @@ public: //! send SPI through the FX3 virtual void transact_spi( unsigned char *tx_data, size_t num_tx_bits, \ unsigned char *rx_data, size_t num_rx_bits) = 0; + + virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0; + + virtual uhd::byte_vector_t read_eeprom(boost::uint16_t addr, boost::uint16_t offset, size_t num_bytes) = 0; + + static std::string fx3_state_string(boost::uint8_t state); }; diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 0da388b93..132b1198d 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -37,10 +37,6 @@ using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; -const boost::uint16_t B200_VENDOR_ID = 0x2500; -const boost::uint16_t B200_PRODUCT_ID = 0x0020; -const boost::uint16_t INIT_PRODUCT_ID = 0x00f0; - static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000); //! mapping of frontend to radio perif index @@ -99,7 +95,7 @@ static device_addrs_t b200_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed //check if fw was already loaded - if (handle->get_manufacturer() != "Ettus Research LLC") + if (!(handle->firmware_loaded())) { b200_iface::make(control)->load_firmware(b200_fw_image); } @@ -160,8 +156,15 @@ b200_impl::b200_impl(const device_addr_t &device_addr) const fs_path mb_path = "/mboards/0"; //try to match the given device address with something on the USB bus + uint16_t vid = B200_VENDOR_ID; + uint16_t pid = B200_PRODUCT_ID; + if (device_addr.has_key("vid")) + sscanf(device_addr.get("vid").c_str(), "%x", &vid); + if (device_addr.has_key("pid")) + sscanf(device_addr.get("pid").c_str(), "%x", &pid); + std::vector<usb_device_handle::sptr> device_list = - usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID); + usb_device_handle::get_device_list(vid, pid); //locate the matching handle in the device list usb_device_handle::sptr handle; @@ -252,7 +255,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) //////////////////////////////////////////////////////////////////// _async_task_data.reset(new AsyncTaskData()); _async_task_data->async_md.reset(new async_md_type(1000/*messages deep*/)); - _async_task = uhd::task::make(boost::bind(&b200_impl::handle_async_task, this, _ctrl_transport, _async_task_data)); + _async_task = uhd::msg_task::make(boost::bind(&b200_impl::handle_async_task, this, _ctrl_transport, _async_task_data)); //////////////////////////////////////////////////////////////////// // Local control endpoint @@ -474,7 +477,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) b200_impl::~b200_impl(void) { - UHD_SAFE_CALL + UHD_SAFE_CALL ( _async_task.reset(); ) @@ -612,7 +615,7 @@ void b200_impl::setup_radio(const size_t dspno) /*********************************************************************** * loopback tests **********************************************************************/ - + void b200_impl::register_loopback_self_test(wb_iface::sptr iface) { bool test_fail = false; diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 818906e60..bee42679b 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -44,13 +44,9 @@ #include <uhd/transport/bounded_buffer.hpp> #include <boost/weak_ptr.hpp> #include "recv_packet_demuxer_3000.hpp" - -static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; -static const std::string B200_FPGA_FILE_NAME = "usrp_b200_fpga.bin"; -static const std::string B210_FPGA_FILE_NAME = "usrp_b210_fpga.bin"; -static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x03; +static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x04; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0x00; -static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x02; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x03; static const double B200_LINK_RATE_BPS = (5e9)/8; //practical link rate (5 Gbps) static const double B200_BUS_CLOCK_RATE = 100e6; static const double B200_DEFAULT_TICK_RATE = 32e6; @@ -120,7 +116,7 @@ struct b200_impl : public uhd::device boost::weak_ptr<uhd::tx_streamer> _tx_streamer; //async ctrl + msgs - uhd::task::sptr _async_task; + uhd::msg_task::sptr _async_task; typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type; struct AsyncTaskData { @@ -130,10 +126,10 @@ struct b200_impl : public uhd::device b200_uart::sptr gpsdo_uart; }; boost::shared_ptr<AsyncTaskData> _async_task_data; - void handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr<AsyncTaskData>); + 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(wb_iface::sptr iface); - void codec_loopback_self_test(wb_iface::sptr iface); + void register_loopback_self_test(uhd::wb_iface::sptr iface); + void codec_loopback_self_test(uhd::wb_iface::sptr iface); void set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &); void check_fw_compat(void); void check_fpga_compat(void); diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index d643ef855..4768aa37b 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -139,27 +139,44 @@ bool b200_impl::recv_async_msg( return _async_task_data->async_md->pop_with_timed_wait(async_metadata, timeout); } -void b200_impl::handle_async_task( +/* + * This method is constantly called in a msg_task loop. + * Incoming messages are dispatched in to the hosts radio_ctrl_cores. + * The radio_ctrl_core queues are accessed via a weak_ptr to them, stored in AsyncTaskData. + * During shutdown the radio_ctrl_core dtor's are called. + * An empty peek32(0) is sent out to flush pending async messages. + * The response to those messages can't be delivered to the ctrl_core queues anymore + * because the shared pointer corresponding to the weak_ptrs is no longer valid. + * Those stranded messages are put into a dump_queue implemented in msg_task. + * A radio_ctrl_core can search for missing messages there. + */ +boost::optional<uhd::msg_task::msg_type_t> b200_impl::handle_async_task( uhd::transport::zero_copy_if::sptr xport, boost::shared_ptr<AsyncTaskData> data ) { managed_recv_buffer::sptr buff = xport->get_recv_buff(); - if (not buff or buff->size() < 8) return; + if (not buff or buff->size() < 8) + return NULL; + const boost::uint32_t sid = uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]); - switch (sid) - { + switch (sid) { //if the packet is a control response case B200_RESP0_MSG_SID: case B200_RESP1_MSG_SID: case B200_LOCAL_RESP_SID: { - radio_ctrl_core_3000::sptr ctrl; + radio_ctrl_core_3000::sptr ctrl; if (sid == B200_RESP0_MSG_SID) ctrl = data->radio_ctrl[0].lock(); if (sid == B200_RESP1_MSG_SID) ctrl = data->radio_ctrl[1].lock(); if (sid == B200_LOCAL_RESP_SID) ctrl = data->local_ctrl.lock(); - if (ctrl) ctrl->push_response(buff->cast<const boost::uint32_t *>()); + if (ctrl){ + ctrl->push_response(buff->cast<const boost::uint32_t *>()); + } + else{ + return std::make_pair(sid, uhd::msg_task::buff_to_vector(buff->cast<boost::uint8_t *>(), buff->size() ) ); + } break; } @@ -204,6 +221,7 @@ void b200_impl::handle_async_task( default: UHD_MSG(error) << "Got a ctrl packet with unknown SID " << sid << std::endl; } + return NULL; } /*********************************************************************** @@ -231,14 +249,14 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) //calculate packet size static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) - + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + //+ sizeof(vrt::if_packet_info_t().tlr) //no longer using trailer - sizeof(vrt::if_packet_info_t().cid) //no class id ever used - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used ; const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; const size_t bpi = convert::get_bytes_per_item(args.otw_format); size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); - spp = std::min<size_t>(2000, spp); //magic maximum for framing at full rate + spp = std::min<size_t>(4092, spp); //FPGA FIFO maximum for framing at full rate //make the new streamer given the samples per packet if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp index c3ef65a2c..bd7777ffa 100644 --- a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp @@ -24,7 +24,7 @@ #include <uhd/transport/zero_copy.hpp> #include <boost/shared_ptr.hpp> #include <boost/utility.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <string> @@ -40,7 +40,8 @@ struct fifo_ctrl_excelsior_config /*! * Provide access to peek, poke, spi, and async messages. */ -class fifo_ctrl_excelsior : public wb_iface, public uhd::spi_iface{ +class fifo_ctrl_excelsior : public uhd::wb_iface, public uhd::spi_iface +{ public: typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr; diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp index 6111efea9..6552f1b2d 100644 --- a/host/lib/usrp/common/fx2_ctrl.cpp +++ b/host/lib/usrp/common/fx2_ctrl.cpp @@ -119,6 +119,9 @@ bool parse_record(std::string *record, unsigned int &len, std::istringstream(record->substr(3, 4)) >> std::hex >> addr; std::istringstream(record->substr(7, 2)) >> std::hex >> type; + if (len > (2 * (record->length() - 9))) // sanity check to prevent buffer overrun + return false; + for (i = 0; i < len; i++) { std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; data[i] = (unsigned char) val; @@ -181,6 +184,9 @@ public: std::string record; file >> record; + if (!(record.length() > 0)) + continue; + //check for valid record if (not checksum(&record) or not parse_record(&record, len, addr, type, data)) { throw uhd::io_error("usrp_load_firmware: bad record checksum"); diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index f526319bc..f28ae040f 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -22,7 +22,6 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) LIBUHD_APPEND_SOURCES( - ${CMAKE_CURRENT_SOURCE_DIR}/wb_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gpio_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100.cpp ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_200.cpp diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index a3edf5454..15fe5f2dd 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -23,7 +23,7 @@ #include <boost/cstdint.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class gpio_core_200 : boost::noncopyable{ public: @@ -33,7 +33,7 @@ public: typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t; //! makes a new GPIO core from iface and slave base - static sptr make(wb_iface::sptr iface, const size_t base, const size_t rb_addr); + static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr); //! 1 = ATR virtual void set_pin_ctrl(const unit_t unit, const boost::uint16_t value) = 0; @@ -56,7 +56,7 @@ public: typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t; - static sptr make(wb_iface::sptr iface, const size_t); + static sptr make(uhd::wb_iface::sptr iface, const size_t); virtual void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value) = 0; diff --git a/host/lib/usrp/cores/i2c_core_100.hpp b/host/lib/usrp/cores/i2c_core_100.hpp index f7a5ae4f7..4e7a2874b 100644 --- a/host/lib/usrp/cores/i2c_core_100.hpp +++ b/host/lib/usrp/cores/i2c_core_100.hpp @@ -22,14 +22,14 @@ #include <uhd/types/serial.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class i2c_core_100 : boost::noncopyable, public uhd::i2c_iface{ public: typedef boost::shared_ptr<i2c_core_100> sptr; //! makes a new i2c core from iface and slave base - static sptr make(wb_iface::sptr iface, const size_t base); + static sptr make(uhd::wb_iface::sptr iface, const size_t base); }; #endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_HPP */ diff --git a/host/lib/usrp/cores/i2c_core_100_wb32.hpp b/host/lib/usrp/cores/i2c_core_100_wb32.hpp index f2ac98292..b5912ba9a 100644 --- a/host/lib/usrp/cores/i2c_core_100_wb32.hpp +++ b/host/lib/usrp/cores/i2c_core_100_wb32.hpp @@ -22,14 +22,14 @@ #include <uhd/types/serial.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class i2c_core_100_wb32 : boost::noncopyable, public uhd::i2c_iface{ public: typedef boost::shared_ptr<i2c_core_100_wb32> sptr; //! makes a new i2c core from iface and slave base - static sptr make(wb_iface::sptr iface, const size_t base); + static sptr make(uhd::wb_iface::sptr iface, const size_t base); virtual void set_clock_rate(const double rate) = 0; }; diff --git a/host/lib/usrp/cores/i2c_core_200.hpp b/host/lib/usrp/cores/i2c_core_200.hpp index 508855985..1b20455d3 100644 --- a/host/lib/usrp/cores/i2c_core_200.hpp +++ b/host/lib/usrp/cores/i2c_core_200.hpp @@ -22,14 +22,14 @@ #include <uhd/types/serial.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class i2c_core_200 : boost::noncopyable, public uhd::i2c_iface{ public: typedef boost::shared_ptr<i2c_core_200> sptr; //! makes a new i2c core from iface and slave base - static sptr make(wb_iface::sptr iface, const size_t base, const size_t readback); + static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback); }; #endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp index 5298fd213..27fac3471 100644 --- a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp @@ -35,35 +35,27 @@ using namespace uhd::transport; static const double ACK_TIMEOUT = 2.0; //supposed to be worst case practical timeout static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command -static const size_t SR_READBACK = 32; +static const size_t SR_READBACK = 32; -class radio_ctrl_core_3000_impl : public radio_ctrl_core_3000 +class radio_ctrl_core_3000_impl: public radio_ctrl_core_3000 { public: - radio_ctrl_core_3000_impl( - const bool big_endian, - uhd::transport::zero_copy_if::sptr ctrl_xport, - uhd::transport::zero_copy_if::sptr resp_xport, - const boost::uint32_t sid, - const std::string &name - ): - _link_type(vrt::if_packet_info_t::LINK_TYPE_CHDR), - _packet_type(vrt::if_packet_info_t::PACKET_TYPE_CONTEXT), - _bige(big_endian), - _ctrl_xport(ctrl_xport), - _resp_xport(resp_xport), - _sid(sid), - _name(name), - _seq_out(0), - _timeout(ACK_TIMEOUT), - _resp_queue(128/*max response msgs*/), - _resp_queue_size(_resp_xport? _resp_xport->get_num_recv_frames() : 3) + radio_ctrl_core_3000_impl(const bool big_endian, + uhd::transport::zero_copy_if::sptr ctrl_xport, + uhd::transport::zero_copy_if::sptr resp_xport, + const boost::uint32_t sid, const std::string &name) : + _link_type(vrt::if_packet_info_t::LINK_TYPE_CHDR), _packet_type( + vrt::if_packet_info_t::PACKET_TYPE_CONTEXT), _bige( + big_endian), _ctrl_xport(ctrl_xport), _resp_xport( + resp_xport), _sid(sid), _name(name), _seq_out(0), _timeout( + ACK_TIMEOUT), _resp_queue(128/*max response msgs*/), _resp_queue_size( + _resp_xport ? _resp_xport->get_num_recv_frames() : 3) { - UHD_LOG << "radio_ctrl_core_3000_impl() " << _name << std::endl; + UHD_LOG<< "radio_ctrl_core_3000_impl() " << _name << std::endl; if (resp_xport) { - while (resp_xport->get_recv_buff(0.0)){} //flush + while (resp_xport->get_recv_buff(0.0)) {} //flush } this->set_time(uhd::time_spec_t(0.0)); this->set_tick_rate(1.0); //something possible but bogus @@ -74,8 +66,8 @@ public: UHD_LOG << "~radio_ctrl_core_3000_impl() " << _name << std::endl; _timeout = ACK_TIMEOUT; //reset timeout to something small UHD_SAFE_CALL( - this->peek32(0); //dummy peek with the purpose of ack'ing all packets - _async_task.reset(); //now its ok to release the task + this->peek32(0);//dummy peek with the purpose of ack'ing all packets + _async_task.reset();//now its ok to release the task ) } @@ -95,7 +87,6 @@ public: { boost::mutex::scoped_lock lock(_mutex); UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; - this->send_pkt(SR_READBACK, addr/8); this->wait_for_ack(false); @@ -136,6 +127,11 @@ public: } private: + // This is the buffer type for messages in radio control core. + struct resp_buff_type + { + boost::uint32_t data[8]; + }; /******************************************************************* * Primary control and interaction private methods @@ -143,7 +139,7 @@ private: UHD_INLINE void send_pkt(const boost::uint32_t addr, const boost::uint32_t data = 0) { managed_send_buffer::sptr buff = _ctrl_xport->get_send_buff(0.0); - if (not buff){ + if (not buff) { throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); } boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); @@ -173,12 +169,11 @@ private: pkt[packet_info.num_header_words32+0] = (_bige)? uhd::htonx(addr) : uhd::htowx(addr); pkt[packet_info.num_header_words32+1] = (_bige)? uhd::htonx(data) : uhd::htowx(data); //UHD_MSG(status) << boost::format("0x%08x, 0x%08x\n") % addr % data; - //send the buffer over the interface _outstanding_seqs.push(_seq_out); buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); - _seq_out++; //inc seq for next call + _seq_out++;//inc seq for next call } UHD_INLINE boost::uint64_t wait_for_ack(const bool readback) @@ -186,7 +181,6 @@ private: while (readback or (_outstanding_seqs.size() >= _resp_queue_size)) { UHD_LOGV(always) << _name << " wait_for_ack: " << "readback = " << readback << " outstanding_seqs.size() " << _outstanding_seqs.size() << std::endl; - //get seq to ack from outstanding packets list UHD_ASSERT_THROW(not _outstanding_seqs.empty()); const size_t seq_to_ack = _outstanding_seqs.front(); @@ -195,6 +189,7 @@ private: //parse the packet vrt::if_packet_info_t packet_info; resp_buff_type resp_buff; + memset(&resp_buff, 0x00, sizeof(resp_buff)); boost::uint32_t const *pkt = NULL; managed_recv_buffer::sptr buff; @@ -218,7 +213,27 @@ private: //get buffer from response endpoint - or die in timeout else { - UHD_ASSERT_THROW(_resp_queue.pop_with_timed_wait(resp_buff, _timeout)); + /* + * Couldn't get message with haste. + * Now check both possible queues for messages. + * Messages should come in on _resp_queue, + * but could end up in dump_queue. + * If we don't get a message --> Die in timeout. + */ + double accum_timeout = 0.0; + const double short_timeout = 0.005; // == 5ms + while(not ((_resp_queue.pop_with_haste(resp_buff)) + || (check_dump_queue(resp_buff)) + || (_resp_queue.pop_with_timed_wait(resp_buff, short_timeout)) + )){ + /* + * If a message couldn't be received within a given timeout + * --> throw AssertionError! + */ + accum_timeout += short_timeout; + UHD_ASSERT_THROW(accum_timeout < _timeout); + } + pkt = resp_buff.data; packet_info.num_packet_words32 = sizeof(resp_buff)/sizeof(boost::uint32_t); } @@ -233,7 +248,12 @@ private: catch(const std::exception &ex) { UHD_MSG(error) << "Radio ctrl bad VITA packet: " << ex.what() << std::endl; - UHD_VAR(buff->size()); + if (buff){ + UHD_VAR(buff->size()); + } + else{ + UHD_MSG(status) << "buff is NULL" << std::endl; + } UHD_MSG(status) << std::hex << pkt[0] << std::dec << std::endl; UHD_MSG(status) << std::hex << pkt[1] << std::dec << std::endl; UHD_MSG(status) << std::hex << pkt[2] << std::dec << std::endl; @@ -262,9 +282,34 @@ private: return ((hi << 32) | lo); } } + return 0; } + /* + * If ctrl_core waits for a message that didn't arrive it can search for it in the dump queue. + * This actually happens during shutdown. + * handle_async_task can't access radio_ctrl_cores queue anymore thus it returns the corresponding message. + * msg_task class implements a dump_queue to store such messages. + * With check_dump_queue we can check if a message we are waiting for got stranded there. + * If a message got stuck we get it here and push it onto our own message_queue. + */ + bool check_dump_queue(resp_buff_type& b) { + const size_t min_buff_size = 8; // Same value as in b200_io_impl->handle_async_task + boost::uint32_t recv_sid = (((_sid)<<16)|((_sid)>>16)); + uhd::msg_task::msg_payload_t msg; + do{ + msg = _async_task->get_msg_from_dump_queue(recv_sid); + } + while(msg.size() < min_buff_size && msg.size() != 0); + + if(msg.size() >= min_buff_size) { + memcpy(b.data, &msg.front(), std::min(msg.size(), sizeof(b.data))); + return true; + } + return false; + } + void push_response(const boost::uint32_t *buff) { resp_buff_type resp_buff; @@ -272,7 +317,7 @@ private: _resp_queue.push_with_haste(resp_buff); } - void hold_task(boost::shared_ptr<void> task) + void hold_task(uhd::msg_task::sptr task) { _async_task = task; } @@ -282,7 +327,7 @@ private: const bool _bige; const uhd::transport::zero_copy_if::sptr _ctrl_xport; const uhd::transport::zero_copy_if::sptr _resp_xport; - boost::shared_ptr<void> _async_task; + uhd::msg_task::sptr _async_task; const boost::uint32_t _sid; const std::string _name; boost::mutex _mutex; @@ -292,22 +337,15 @@ private: double _tick_rate; double _timeout; std::queue<size_t> _outstanding_seqs; - struct resp_buff_type - { - boost::uint32_t data[8]; - }; bounded_buffer<resp_buff_type> _resp_queue; const size_t _resp_queue_size; }; - -radio_ctrl_core_3000::sptr radio_ctrl_core_3000::make( - const bool big_endian, - zero_copy_if::sptr ctrl_xport, - zero_copy_if::sptr resp_xport, - const boost::uint32_t sid, - const std::string &name -) +radio_ctrl_core_3000::sptr radio_ctrl_core_3000::make(const bool big_endian, + zero_copy_if::sptr ctrl_xport, zero_copy_if::sptr resp_xport, + const boost::uint32_t sid, const std::string &name) { - return sptr(new radio_ctrl_core_3000_impl(big_endian, ctrl_xport, resp_xport, sid, name)); + return sptr( + new radio_ctrl_core_3000_impl(big_endian, ctrl_xport, resp_xport, + sid, name)); } diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.hpp b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp index 8c0548d89..51a307c10 100644 --- a/host/lib/usrp/cores/radio_ctrl_core_3000.hpp +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp @@ -18,17 +18,18 @@ #ifndef INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP #define INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP +#include <uhd/utils/msg_task.hpp> #include <uhd/types/time_spec.hpp> #include <uhd/transport/zero_copy.hpp> +#include <uhd/types/wb_iface.hpp> #include <boost/shared_ptr.hpp> #include <boost/utility.hpp> -#include "wb_iface.hpp" #include <string> /*! * Provide access to peek, poke for the radio ctrl module */ -class radio_ctrl_core_3000 : public wb_iface +class radio_ctrl_core_3000 : public uhd::wb_iface { public: typedef boost::shared_ptr<radio_ctrl_core_3000> sptr; @@ -43,7 +44,7 @@ public: ); //! Hold a ref to a task thats feeding push response - virtual void hold_task(boost::shared_ptr<void> task) = 0; + virtual void hold_task(uhd::msg_task::sptr task) = 0; //! Push a response externall (resp_xport is NULL) virtual void push_response(const boost::uint32_t *buff) = 0; diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index ef6b85de9..2fdc220b5 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -59,8 +59,11 @@ public: const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid, const bool lingering_packet ): - _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base), _sid(sid) + _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base), _sid(sid) { + // previously uninitialized - assuming zero for all + _tick_rate = _link_rate = _host_extra_scaling = _fxpt_scalar_correction = 0.0; + //init to something so update method has reasonable defaults _scaling_adjustment = 1.0; _dsp_extra_scaling = 1.0; diff --git a/host/lib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/usrp/cores/rx_dsp_core_200.hpp index b01f751e9..3937df9e8 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp @@ -24,7 +24,7 @@ #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <uhd/types/stream_cmd.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <string> class rx_dsp_core_200 : boost::noncopyable{ @@ -32,7 +32,7 @@ public: typedef boost::shared_ptr<rx_dsp_core_200> sptr; static sptr make( - wb_iface::sptr iface, + uhd::wb_iface::sptr iface, const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid, const bool lingering_packet = false ); diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 7b3324f74..525916032 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -50,10 +50,13 @@ public: ): _iface(iface), _dsp_base(dsp_base) { + // previously uninitialized - assuming zero for all + _link_rate = _host_extra_scaling = _fxpt_scalar_correction = 0.0; + //init to something so update method has reasonable defaults _scaling_adjustment = 1.0; _dsp_extra_scaling = 1.0; - this->set_tick_rate(1.0); + _tick_rate = 1.0; } ~rx_dsp_core_3000_impl(void) diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp index 23b12b9b7..02e5587a2 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp @@ -24,7 +24,7 @@ #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <uhd/types/stream_cmd.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <string> class rx_dsp_core_3000 : boost::noncopyable{ @@ -32,7 +32,7 @@ public: typedef boost::shared_ptr<rx_dsp_core_3000> sptr; static sptr make( - wb_iface::sptr iface, + uhd::wb_iface::sptr iface, const size_t dsp_base ); diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp index 1813758da..864b5cc53 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -18,6 +18,8 @@ #include "rx_frontend_core_200.hpp" #include <boost/math/special_functions/round.hpp> +using namespace uhd; + #define REG_RX_FE_SWAP_IQ _base + 0 //lower bit #define REG_RX_FE_MAG_CORRECTION _base + 4 //18 bits #define REG_RX_FE_PHASE_CORRECTION _base + 8 //18 bits @@ -36,7 +38,7 @@ static boost::uint32_t fs_to_bits(const double num, const size_t bits){ 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): - _iface(iface), _base(base) + _i_dc_off(0), _q_dc_off(0), _iface(iface), _base(base) { //NOP } diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp index 5755424c8..8327aef8b 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp @@ -21,7 +21,7 @@ #include <uhd/config.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <complex> #include <string> @@ -29,7 +29,7 @@ class rx_frontend_core_200 : boost::noncopyable{ public: typedef boost::shared_ptr<rx_frontend_core_200> sptr; - static sptr make(wb_iface::sptr iface, const size_t base); + static sptr make(uhd::wb_iface::sptr iface, const size_t base); virtual void set_mux(const bool swap) = 0; diff --git a/host/lib/usrp/cores/rx_vita_core_3000.hpp b/host/lib/usrp/cores/rx_vita_core_3000.hpp index b011a7388..577510728 100644 --- a/host/lib/usrp/cores/rx_vita_core_3000.hpp +++ b/host/lib/usrp/cores/rx_vita_core_3000.hpp @@ -24,7 +24,7 @@ #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <uhd/types/stream_cmd.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <string> class rx_vita_core_3000 : boost::noncopyable @@ -33,7 +33,7 @@ public: typedef boost::shared_ptr<rx_vita_core_3000> sptr; static sptr make( - wb_iface::sptr iface, + uhd::wb_iface::sptr iface, const size_t base ); diff --git a/host/lib/usrp/cores/spi_core_100.hpp b/host/lib/usrp/cores/spi_core_100.hpp index 87d328aaa..ce53c0b86 100644 --- a/host/lib/usrp/cores/spi_core_100.hpp +++ b/host/lib/usrp/cores/spi_core_100.hpp @@ -22,14 +22,14 @@ #include <uhd/types/serial.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class spi_core_100 : boost::noncopyable, public uhd::spi_iface{ public: typedef boost::shared_ptr<spi_core_100> sptr; //! makes a new spi core from iface and slave base - static sptr make(wb_iface::sptr iface, const size_t base); + static sptr make(uhd::wb_iface::sptr iface, const size_t base); }; #endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_100_HPP */ diff --git a/host/lib/usrp/cores/spi_core_3000.hpp b/host/lib/usrp/cores/spi_core_3000.hpp index 995ad59db..923efed3d 100644 --- a/host/lib/usrp/cores/spi_core_3000.hpp +++ b/host/lib/usrp/cores/spi_core_3000.hpp @@ -22,7 +22,7 @@ #include <uhd/types/serial.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class spi_core_3000 : boost::noncopyable, public uhd::spi_iface { @@ -30,7 +30,7 @@ public: typedef boost::shared_ptr<spi_core_3000> sptr; //! makes a new spi core from iface and slave base - static sptr make(wb_iface::sptr iface, const size_t base, const size_t readback); + static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback); //! Set the spi clock divider to something usable virtual void set_divider(const double div) = 0; diff --git a/host/lib/usrp/cores/time64_core_200.cpp b/host/lib/usrp/cores/time64_core_200.cpp index 11b310362..ad5e6477c 100644 --- a/host/lib/usrp/cores/time64_core_200.cpp +++ b/host/lib/usrp/cores/time64_core_200.cpp @@ -48,6 +48,7 @@ public: ): _iface(iface), _base(base), _readback_bases(readback_bases), + _tick_rate(0.0), _mimo_delay_cycles(mimo_delay_cycles) { _sources.push_back("none"); diff --git a/host/lib/usrp/cores/time64_core_200.hpp b/host/lib/usrp/cores/time64_core_200.hpp index 315f2ba67..e211ce040 100644 --- a/host/lib/usrp/cores/time64_core_200.hpp +++ b/host/lib/usrp/cores/time64_core_200.hpp @@ -22,7 +22,7 @@ #include <uhd/types/time_spec.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <string> #include <vector> @@ -37,7 +37,7 @@ public: //! makes a new time64 core from iface and slave base static sptr make( - wb_iface::sptr iface, const size_t base, + uhd::wb_iface::sptr iface, const size_t base, const readback_bases_type &readback_bases, const size_t mimo_delay_cycles = 0 // 0 means no-mimo ); diff --git a/host/lib/usrp/cores/time_core_3000.hpp b/host/lib/usrp/cores/time_core_3000.hpp index ffe2f4133..fad408810 100644 --- a/host/lib/usrp/cores/time_core_3000.hpp +++ b/host/lib/usrp/cores/time_core_3000.hpp @@ -22,7 +22,7 @@ #include <uhd/types/time_spec.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class time_core_3000 : boost::noncopyable { @@ -37,7 +37,7 @@ public: //! makes a new time core from iface and slave base static sptr make( - wb_iface::sptr iface, const size_t base, + uhd::wb_iface::sptr iface, const size_t base, const readback_bases_type &readback_bases ); diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp index 808f13028..f8aa87aa3 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -60,6 +60,9 @@ public: ): _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base), _sid(sid) { + // previously uninitialized - assuming zero for all + _tick_rate = _link_rate = _host_extra_scaling = _fxpt_scalar_correction = 0.0; + //init to something so update method has reasonable defaults _scaling_adjustment = 1.0; _dsp_extra_scaling = 1.0; diff --git a/host/lib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/usrp/cores/tx_dsp_core_200.hpp index 0e1cfb6bc..ce3d1dbdd 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.hpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp @@ -23,14 +23,14 @@ #include <uhd/types/ranges.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class tx_dsp_core_200 : boost::noncopyable{ public: typedef boost::shared_ptr<tx_dsp_core_200> sptr; static sptr make( - wb_iface::sptr iface, + uhd::wb_iface::sptr iface, const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid ); diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp index feb749cd9..93c8702bc 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -45,6 +45,9 @@ public: ): _iface(iface), _dsp_base(dsp_base) { + // previously uninitialized - assuming zero for all + _link_rate = _host_extra_scaling = _fxpt_scalar_correction = 0.0; + //init to something so update method has reasonable defaults _scaling_adjustment = 1.0; _dsp_extra_scaling = 1.0; diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/usrp/cores/tx_dsp_core_3000.hpp index eb5ffaf0f..6f725b836 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.hpp @@ -23,14 +23,14 @@ #include <uhd/types/ranges.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class tx_dsp_core_3000 : boost::noncopyable{ public: typedef boost::shared_ptr<tx_dsp_core_3000> sptr; static sptr make( - wb_iface::sptr iface, + uhd::wb_iface::sptr iface, const size_t dsp_base ); diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp index e35874173..d701027e5 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -21,6 +21,8 @@ #include <boost/assign/list_of.hpp> #include <boost/math/special_functions/round.hpp> +using namespace uhd; + #define REG_TX_FE_DC_OFFSET_I _base + 0 //24 bits #define REG_TX_FE_DC_OFFSET_Q _base + 4 //24 bits #define REG_TX_FE_MAG_CORRECTION _base + 8 //18 bits diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp index 8ee0f3e6d..7d09b39d2 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.hpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp @@ -21,7 +21,7 @@ #include <uhd/config.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <complex> #include <string> @@ -29,7 +29,7 @@ class tx_frontend_core_200 : boost::noncopyable{ public: typedef boost::shared_ptr<tx_frontend_core_200> sptr; - static sptr make(wb_iface::sptr iface, const size_t base); + static sptr make(uhd::wb_iface::sptr iface, const size_t base); virtual void set_mux(const std::string &mode) = 0; diff --git a/host/lib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/usrp/cores/tx_vita_core_3000.hpp index 2070936ce..d4677a3e3 100644 --- a/host/lib/usrp/cores/tx_vita_core_3000.hpp +++ b/host/lib/usrp/cores/tx_vita_core_3000.hpp @@ -24,7 +24,7 @@ #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <uhd/types/stream_cmd.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <string> class tx_vita_core_3000 : boost::noncopyable @@ -33,7 +33,7 @@ public: typedef boost::shared_ptr<tx_vita_core_3000> sptr; static sptr make( - wb_iface::sptr iface, + uhd::wb_iface::sptr iface, const size_t base ); diff --git a/host/lib/usrp/cores/user_settings_core_200.cpp b/host/lib/usrp/cores/user_settings_core_200.cpp index d262631b1..391725edc 100644 --- a/host/lib/usrp/cores/user_settings_core_200.cpp +++ b/host/lib/usrp/cores/user_settings_core_200.cpp @@ -17,6 +17,8 @@ #include "user_settings_core_200.hpp" +using namespace uhd; + #define REG_USER_ADDR _base + 0 #define REG_USER_DATA _base + 4 diff --git a/host/lib/usrp/cores/user_settings_core_200.hpp b/host/lib/usrp/cores/user_settings_core_200.hpp index 1f5d13de7..f5fca2ce6 100644 --- a/host/lib/usrp/cores/user_settings_core_200.hpp +++ b/host/lib/usrp/cores/user_settings_core_200.hpp @@ -21,14 +21,14 @@ #include <uhd/config.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> class user_settings_core_200 : boost::noncopyable{ public: typedef boost::shared_ptr<user_settings_core_200> sptr; typedef std::pair<boost::uint8_t, boost::uint32_t> user_reg_t; - static sptr make(wb_iface::sptr iface, const size_t base); + static sptr make(uhd::wb_iface::sptr iface, const size_t base); virtual void set_reg(const user_reg_t ®) = 0; }; diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index b1cee4aa7..9d04d8e16 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -179,7 +179,7 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){ "DBSRX: incorrect dbid\n" "Expected dbid 0x0002 and R193\n" "found dbid == %d\n" - "Please see the daughterboard app notes" + "Please see the daughterboard app notes" ) % this->get_rx_id().to_pp_string(); //warn user about incorrect DBID on non-USRP1, requires R194 populated @@ -188,7 +188,7 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){ "DBSRX: incorrect dbid\n" "Expected dbid 0x000D and R194\n" "found dbid == %d\n" - "Please see the daughterboard app notes" + "Please see the daughterboard app notes" ) % this->get_rx_id().to_pp_string(); //send initial register settings @@ -305,13 +305,13 @@ double dbsrx::set_lo_freq(double target_freq){ goto done_loop; } - } + } done_loop: - //Assert because we failed to find a suitable combination of ref_clock, R and N + //Assert because we failed to find a suitable combination of ref_clock, R and N UHD_ASSERT_THROW(ref_clock <= 27.0e6 and ref_clock >= 0.0); - UHD_ASSERT_THROW(ref_clock/m >= 1e6 and ref_clock/m <= 2.5e6); + UHD_ASSERT_THROW(m and ref_clock/m >= 1e6 and ref_clock/m <= 2.5e6); UHD_ASSERT_THROW((pfd_freq >= dbsrx_pfd_freq_range.start()) and (pfd_freq <= dbsrx_pfd_freq_range.stop())); UHD_ASSERT_THROW((N >= 256) and (N <= 32768)); @@ -332,7 +332,7 @@ double dbsrx::set_lo_freq(double target_freq){ _max2118_write_regs.r_divider = (max2118_write_regs_t::r_divider_t) r; _max2118_write_regs.set_n_divider(N); _max2118_write_regs.ade_vco_ade_read = max2118_write_regs_t::ADE_VCO_ADE_READ_ENABLED; - + //compute prescaler variables int scaler = actual_freq > 1125e6 ? 2 : 4; _max2118_write_regs.div2 = scaler == 4 ? max2118_write_regs_t::DIV2_DIV4 : max2118_write_regs_t::DIV2_DIV2; @@ -377,7 +377,7 @@ double dbsrx::set_lo_freq(double target_freq){ if (_max2118_read_regs.adc == 0){ if (_max2118_write_regs.osc_band == 0){ UHD_MSG(warning) << boost::format( - "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n" + "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n" ) % int(_max2118_write_regs.osc_band); UHD_ASSERT_THROW(_max2118_read_regs.adc != 0); //just to cause a throw } @@ -389,7 +389,7 @@ double dbsrx::set_lo_freq(double target_freq){ if (_max2118_read_regs.adc == 7){ if (_max2118_write_regs.osc_band == 7){ UHD_MSG(warning) << boost::format( - "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n" + "DBSRX: Tuning exceeded vco range, _max2118_write_regs.osc_band == %d\n" ) % int(_max2118_write_regs.osc_band); UHD_ASSERT_THROW(_max2118_read_regs.adc != 7); //just to cause a throw } @@ -408,7 +408,7 @@ double dbsrx::set_lo_freq(double target_freq){ //allow for setup time before checking condition again boost::this_thread::sleep(boost::posix_time::milliseconds(10)); } - + UHD_LOGV(often) << boost::format( "DBSRX: final vco %d, vtune adc %d" ) % int(_max2118_write_regs.osc_band) % int(_max2118_read_regs.adc) << std::endl; @@ -417,7 +417,7 @@ double dbsrx::set_lo_freq(double target_freq){ if (_max2118_read_regs.adc <= 2) _max2118_write_regs.cp_current = max2118_write_regs_t::CP_CURRENT_I_CP_100UA; else if (_max2118_read_regs.adc >= 5) _max2118_write_regs.cp_current = max2118_write_regs_t::CP_CURRENT_I_CP_400UA; else _max2118_write_regs.cp_current = max2118_write_regs_t::CP_CURRENT_I_CP_200UA; - + //update charge pump bias current setting send_reg(0x2, 0x2); @@ -524,7 +524,7 @@ double dbsrx::set_bandwidth(double bandwidth){ bandwidth = dbsrx_bandwidth_range.clip(bandwidth); double ref_clock = this->get_iface()->get_clock_rate(dboard_iface::UNIT_RX); - + //NOTE: _max2118_write_regs.m_divider set in set_lo_freq //compute f_dac setting diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp index 013f3178a..8a8f61a69 100644 --- a/host/lib/usrp/dboard/db_dbsrx2.cpp +++ b/host/lib/usrp/dboard/db_dbsrx2.cpp @@ -358,12 +358,12 @@ double dbsrx2::set_gain(double gain, const std::string &name){ * Bandwidth Handling **********************************************************************/ double dbsrx2::set_bandwidth(double bandwidth){ - //convert complex bandpass to lowpass bandwidth - bandwidth = bandwidth/2.0; - //clip the input bandwidth = dbsrx2_bandwidth_range.clip(bandwidth); + //convert complex bandpass to lowpass bandwidth + bandwidth = bandwidth/2.0; + _max2112_write_regs.lp = int((bandwidth/1e6 - 4)/0.29 + 12); _bandwidth = double(4 + (_max2112_write_regs.lp - 12) * 0.29)*1e6; diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index 9db29e65a..5b713c6d7 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -21,6 +21,137 @@ using namespace uhd; using namespace uhd::usrp; using namespace boost::assign; +/*********************************************************************** + * ADF 4350/4351 Tuning Utility + **********************************************************************/ +sbx_xcvr::sbx_versionx::adf435x_tuning_settings sbx_xcvr::sbx_versionx::_tune_adf435x_synth( + double target_freq, + double ref_freq, + const adf435x_tuning_constraints& constraints, + double& actual_freq) +{ + //Default invalid value for actual_freq + actual_freq = 0; + + double pfd_freq = 0; + boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0; + boost::uint16_t RFdiv = static_cast<boost::uint16_t>(constraints.rf_divider_range.start()); + bool D = false, T = false; + + //Reference doubler for 50% duty cycle + //If ref_freq < 12.5MHz enable the reference doubler + D = (ref_freq <= constraints.ref_doubler_threshold); + + static const double MIN_VCO_FREQ = 2.2e9; + static const double MAX_VCO_FREQ = 4.4e9; + + //increase RF divider until acceptable VCO frequency + double vco_freq = target_freq; + while (vco_freq < MIN_VCO_FREQ && RFdiv < static_cast<boost::uint16_t>(constraints.rf_divider_range.stop())) { + vco_freq *= 2; + RFdiv *= 2; + } + + /* + * The goal here is to loop though possible R dividers, + * band select clock dividers, N (int) dividers, and FRAC + * (frac) dividers. + * + * Calculate the N and F dividers for each set of values. + * The loop exits when it meets all of the constraints. + * The resulting loop values are loaded into the registers. + * + * from pg.21 + * + * f_pfd = f_ref*(1+D)/(R*(1+T)) + * f_vco = (N + (FRAC/MOD))*f_pfd + * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD + * f_rf = f_vco/RFdiv) + * f_actual = f_rf/2 + */ + for(R = 1; R <= 1023; R+=1){ + //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) + pfd_freq = ref_freq*(D?2:1)/(R*(T?2:1)); + + //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) + if (pfd_freq > constraints.pfd_freq_max) continue; + + //ignore fractional part of tuning + //N is computed from target_freq and not vco_freq because the feedback + //mode is set to FEEDBACK_SELECT_DIVIDED + N = boost::uint16_t(std::floor(target_freq/pfd_freq)); + + //keep N > minimum int divider requirement + if (N < static_cast<boost::uint16_t>(constraints.int_range.start())) continue; + + for(BS=1; BS <= 255; BS+=1){ + //keep the band select frequency at or below band_sel_freq_max + //constraint on band select clock + if (pfd_freq/BS > constraints.band_sel_freq_max) continue; + goto done_loop; + } + } done_loop: + + //Fractional-N calculation + MOD = 4095; //max fractional accuracy + //N is computed from target_freq and not vco_freq because the feedback + //mode is set to FEEDBACK_SELECT_DIVIDED + FRAC = static_cast<boost::uint16_t>((target_freq/pfd_freq - N)*MOD); + if (constraints.force_frac0) { + if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target + N++; + } + FRAC = 0; + } + + //Reference divide-by-2 for 50% duty cycle + // if R even, move one divide by 2 to to regs.reference_divide_by_2 + if(R % 2 == 0) { + T = true; + R /= 2; + } + + //Typical phase resync time documented in data sheet pg.24 + static const double PHASE_RESYNC_TIME = 400e-6; + + //actual frequency calculation + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(D?2:1)/(R*(T?2:1))); + + //load the settings + adf435x_tuning_settings settings; + settings.frac_12_bit = FRAC; + settings.int_16_bit = N; + settings.mod_12_bit = MOD; + settings.clock_divider_12_bit = std::max<boost::uint16_t>(1, std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD)); + settings.r_counter_10_bit = R; + settings.r_divide_by_2_en = T; + settings.r_doubler_en = D; + settings.band_select_clock_div = BS; + settings.rf_divider = RFdiv; + settings.feedback_after_divider = true; + + UHD_LOGV(often) + << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f" + ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl + << boost::format("ADF 435X Intermediates (MHz): VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" + ) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl + << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl; + + UHD_ASSERT_THROW((settings.frac_12_bit & ((boost::uint16_t)~0xFFF)) == 0); + UHD_ASSERT_THROW((settings.mod_12_bit & ((boost::uint16_t)~0xFFF)) == 0); + UHD_ASSERT_THROW((settings.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0); + UHD_ASSERT_THROW((settings.r_counter_10_bit & ((boost::uint16_t)~0x3FF)) == 0); + + UHD_ASSERT_THROW(vco_freq >= MIN_VCO_FREQ and vco_freq <= MAX_VCO_FREQ); + UHD_ASSERT_THROW(settings.rf_divider >= static_cast<boost::uint16_t>(constraints.rf_divider_range.start())); + UHD_ASSERT_THROW(settings.rf_divider <= static_cast<boost::uint16_t>(constraints.rf_divider_range.stop())); + UHD_ASSERT_THROW(settings.int_16_bit >= static_cast<boost::uint16_t>(constraints.int_range.start())); + UHD_ASSERT_THROW(settings.int_16_bit <= static_cast<boost::uint16_t>(constraints.int_range.stop())); + + return settings; +} + /*********************************************************************** * Register the SBX dboard (min freq, max freq, rx div2, tx div2) @@ -362,4 +493,3 @@ void sbx_xcvr::flash_leds(void) { this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); } - diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 4f3a2eeaa..e9bb2434c 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -181,6 +181,34 @@ protected: ~sbx_versionx(void) {} virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0; + protected: + struct adf435x_tuning_constraints { + bool force_frac0; + double ref_doubler_threshold; + double pfd_freq_max; + double band_sel_freq_max; + uhd::range_t rf_divider_range; + uhd::range_t int_range; + }; + + struct adf435x_tuning_settings { + boost::uint16_t frac_12_bit; + boost::uint16_t int_16_bit; + boost::uint16_t mod_12_bit; + boost::uint16_t r_counter_10_bit; + bool r_doubler_en; + bool r_divide_by_2_en; + boost::uint16_t clock_divider_12_bit; + boost::uint8_t band_select_clock_div; + boost::uint16_t rf_divider; + bool feedback_after_divider; + }; + + adf435x_tuning_settings _tune_adf435x_synth( + double target_freq, + double ref_freq, + const adf435x_tuning_constraints& constraints, + double& actual_freq); }; /*! diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index 2765d530c..b0c9cd18f 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -63,85 +63,21 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) ; - double actual_freq, pfd_freq; - double ref_freq = self_base->get_iface()->get_clock_rate(unit); - int R=0, BS=0, N=0, FRAC=0, MOD=0; - int RFdiv = 1; - adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; - - //Reference doubler for 50% duty cycle - // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 - if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - - //increase RF divider until acceptable VCO frequency - double vco_freq = target_freq; - while (vco_freq < 2.2e9) { - vco_freq *= 2; - RFdiv *= 2; - } - //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - /* - * The goal here is to loop though possible R dividers, - * band select clock dividers, N (int) dividers, and FRAC - * (frac) dividers. - * - * Calculate the N and F dividers for each set of values. - * The loop exits when it meets all of the constraints. - * The resulting loop values are loaded into the registers. - * - * from pg.21 - * - * f_pfd = f_ref*(1+D)/(R*(1+T)) - * f_vco = (N + (FRAC/MOD))*f_pfd - * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD - * f_rf = f_vco/RFdiv) - * f_actual = f_rf/2 - */ - for(R = 1; R <= 1023; R+=1){ - //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) - pfd_freq = ref_freq*(1+D)/(R*(1+T)); - - //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) - if (pfd_freq > 25e6) continue; - - //ignore fractional part of tuning - N = int(std::floor(target_freq/pfd_freq)); - - //keep N > minimum int divider requirement - if (N < prescaler_to_min_int_div[prescaler]) continue; - - for(BS=1; BS <= 255; BS+=1){ - //keep the band select frequency at or below 100KHz - //constraint on band select clock - if (pfd_freq/BS > 100e3) continue; - goto done_loop; - } - } done_loop: - - //Fractional-N calculation - MOD = 4095; //max fractional accuracy - FRAC = int((target_freq/pfd_freq - N)*MOD); - - //Reference divide-by-2 for 50% duty cycle - // if R even, move one divide by 2 to to regs.reference_divide_by_2 - if(R % 2 == 0){ - T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; - R /= 2; - } - - //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); + adf435x_tuning_constraints tuning_constraints; + tuning_constraints.force_frac0 = false; + tuning_constraints.band_sel_freq_max = 100e3; + tuning_constraints.ref_doubler_threshold = 12.5e6; + tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field + tuning_constraints.pfd_freq_max = 25e6; + tuning_constraints.rf_divider_range = uhd::range_t(1, 16); - UHD_LOGV(often) - << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl - << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" - ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + double actual_freq; + adf435x_tuning_settings tuning_settings = _tune_adf435x_synth( + target_freq, self_base->get_iface()->get_clock_rate(unit), + tuning_constraints, actual_freq); //load the register values adf4350_regs_t regs; @@ -151,19 +87,25 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar else regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - regs.frac_12_bit = FRAC; - regs.int_16_bit = N; - regs.mod_12_bit = MOD; - regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); - regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; - regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; - regs.prescaler = prescaler; - regs.r_counter_10_bit = R; - regs.reference_divide_by_2 = T; - regs.reference_doubler = D; - regs.band_select_clock_div = BS; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + regs.frac_12_bit = tuning_settings.frac_12_bit; + regs.int_16_bit = tuning_settings.int_16_bit; + regs.mod_12_bit = tuning_settings.mod_12_bit; + regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; + regs.feedback_select = tuning_settings.feedback_after_divider ? + adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : + adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.prescaler = prescaler; + regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; + regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? + adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : + adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + regs.reference_doubler = tuning_settings.r_doubler_en ? + adf4350_regs_t::REFERENCE_DOUBLER_ENABLED : + adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; + regs.band_select_clock_div = tuning_settings.band_select_clock_div; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); + regs.rf_divider_select = rfdivsel_to_enum[tuning_settings.rf_divider]; //reset the N and R counter regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index 27fd68b05..8d95b0655 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -66,85 +66,21 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) ; - double actual_freq, pfd_freq; - double ref_freq = self_base->get_iface()->get_clock_rate(unit); - int R=0, BS=0, N=0, FRAC=0, MOD=0; - int RFdiv = 1; - adf4351_regs_t::reference_divide_by_2_t T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - adf4351_regs_t::reference_doubler_t D = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; - - //Reference doubler for 50% duty cycle - // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 - if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; - - //increase RF divider until acceptable VCO frequency - double vco_freq = target_freq; - while (vco_freq < 2.2e9) { - vco_freq *= 2; - RFdiv *= 2; - } - //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; - - /* - * The goal here is to loop though possible R dividers, - * band select clock dividers, N (int) dividers, and FRAC - * (frac) dividers. - * - * Calculate the N and F dividers for each set of values. - * The loop exits when it meets all of the constraints. - * The resulting loop values are loaded into the registers. - * - * from pg.21 - * - * f_pfd = f_ref*(1+D)/(R*(1+T)) - * f_vco = (N + (FRAC/MOD))*f_pfd - * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD - * f_rf = f_vco/RFdiv) - * f_actual = f_rf/2 - */ - for(R = 1; R <= 1023; R+=1){ - //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) - pfd_freq = ref_freq*(1+D)/(R*(1+T)); - - //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) - if (pfd_freq > 25e6) continue; - - //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); - - //keep N > minimum int divider requirement - if (N < prescaler_to_min_int_div[prescaler]) continue; - - for(BS=1; BS <= 255; BS+=1){ - //keep the band select frequency at or below 100KHz - //constraint on band select clock - if (pfd_freq/BS > 100e3) continue; - goto done_loop; - } - } done_loop: - - //Fractional-N calculation - MOD = 4095; //max fractional accuracy - FRAC = int((target_freq/pfd_freq - N)*MOD); - - //Reference divide-by-2 for 50% duty cycle - // if R even, move one divide by 2 to to regs.reference_divide_by_2 - if(R % 2 == 0){ - T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; - R /= 2; - } + adf4351_regs_t::prescaler_t prescaler = target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; - //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); + adf435x_tuning_constraints tuning_constraints; + tuning_constraints.force_frac0 = false; + tuning_constraints.band_sel_freq_max = 100e3; + tuning_constraints.ref_doubler_threshold = 12.5e6; + tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field + tuning_constraints.pfd_freq_max = 25e6; + tuning_constraints.rf_divider_range = uhd::range_t(1, 64); - UHD_LOGV(often) - << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl - << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" - ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + double actual_freq; + adf435x_tuning_settings tuning_settings = _tune_adf435x_synth( + target_freq, self_base->get_iface()->get_clock_rate(unit), + tuning_constraints, actual_freq); //load the register values adf4351_regs_t regs; @@ -154,19 +90,25 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar else regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; - regs.frac_12_bit = FRAC; - regs.int_16_bit = N; - regs.mod_12_bit = MOD; - regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); - regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; - regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; - regs.prescaler = prescaler; - regs.r_counter_10_bit = R; - regs.reference_divide_by_2 = T; - regs.reference_doubler = D; - regs.band_select_clock_div = BS; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + regs.frac_12_bit = tuning_settings.frac_12_bit; + regs.int_16_bit = tuning_settings.int_16_bit; + regs.mod_12_bit = tuning_settings.mod_12_bit; + regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; + regs.feedback_select = tuning_settings.feedback_after_divider ? + adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : + adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.prescaler = prescaler; + regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; + regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? + adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : + adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + regs.reference_doubler = tuning_settings.r_doubler_en ? + adf4351_regs_t::REFERENCE_DOUBLER_ENABLED : + adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; + regs.band_select_clock_div = tuning_settings.band_select_clock_div; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); + regs.rf_divider_select = rfdivsel_to_enum[tuning_settings.rf_divider]; //reset the N and R counter regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp index edee46cd5..e9f60f765 100644 --- a/host/lib/usrp/dboard/db_tvrx.cpp +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -267,7 +267,7 @@ static std::string get_band(double freq) { * \return a voltage to feed the TVRX analog gain */ -static double gain_interp(double gain, boost::array<double, 17> db_vector, boost::array<double, 17> volts_vector) { +static double gain_interp(double gain, const boost::array<double, 17>& db_vector, const boost::array<double, 17>& volts_vector) { double volts; gain = uhd::clip<double>(gain, db_vector.front(), db_vector.back()); //let's not get carried away here diff --git a/host/lib/usrp/dboard/db_wbx_common.hpp b/host/lib/usrp/dboard/db_wbx_common.hpp index 9e984dce7..d1beb160e 100644 --- a/host/lib/usrp/dboard/db_wbx_common.hpp +++ b/host/lib/usrp/dboard/db_wbx_common.hpp @@ -118,7 +118,7 @@ protected: */ class wbx_versionx { public: - wbx_versionx() {} + wbx_versionx():self_base(NULL) {} ~wbx_versionx(void) {} virtual double set_tx_gain(double gain, const std::string &name) = 0; diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 6f64d4b80..813f9cc8f 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -61,7 +61,7 @@ extern void e100_load_fpga(const std::string &bin_file); //! Make an e100 dboard interface uhd::usrp::dboard_iface::sptr make_e100_dboard_iface( - wb_iface::sptr wb_iface, + uhd::wb_iface::sptr wb_iface, uhd::i2c_iface::sptr i2c_iface, uhd::spi_iface::sptr spi_iface, e100_clock_ctrl::sptr clock, diff --git a/host/lib/usrp/gps_ctrl.cpp b/host/lib/usrp/gps_ctrl.cpp index c3af75faa..105a52b30 100644 --- a/host/lib/usrp/gps_ctrl.cpp +++ b/host/lib/usrp/gps_ctrl.cpp @@ -223,7 +223,6 @@ private: boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); } throw uhd::value_error(str(boost::format("get_nmea(): no %s message found") % msgtype)); - return std::string(); } //helper function to retrieve a field from an NMEA sentence @@ -322,7 +321,6 @@ private: boost::this_thread::sleep(milliseconds(GPS_TIMEOUT_DELAY_MS)); } throw uhd::value_error("get_stat(): no servo message found"); - return std::string(); } uart_iface::sptr _uart; diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index d384eb13f..e1f17e5a6 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -63,7 +63,7 @@ static const size_t alignment_padding = 512; * Helper struct to associate an offset with a buffer **********************************************************************/ struct offset_send_buffer{ - offset_send_buffer(void){ + offset_send_buffer(void):offset(0){ /* NOP */ } diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp index 9301721aa..6eff9d3ad 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.cpp +++ b/host/lib/usrp/usrp1/usrp1_iface.cpp @@ -91,11 +91,11 @@ public: return uhd::ntohx(value_out); } - + void poke16(boost::uint32_t, boost::uint16_t) { throw uhd::not_implemented_error("Unhandled command poke16()"); } - + boost::uint16_t peek16(boost::uint32_t) { throw uhd::not_implemented_error("Unhandled command peek16()"); return 0; @@ -141,8 +141,10 @@ public: if (readback) { unsigned char buff[4] = { - (bits >> 0) & 0xff, (bits >> 8) & 0xff, - (bits >> 16) & 0xff, (bits >> 24) & 0xff + (unsigned char)(bits & 0xff), + (unsigned char)((bits >> 8) & 0xff), + (unsigned char)((bits >> 16) & 0xff), + (unsigned char)((bits >> 24) & 0xff) }; //conditions where there are two header bytes if (num_bytes >= 3 and buff[num_bytes-1] != 0 and buff[num_bytes-2] != 0 and buff[num_bytes-3] == 0){ @@ -168,7 +170,7 @@ public: (((boost::uint32_t)buff[1]) << 8) | (((boost::uint32_t)buff[2]) << 16) | (((boost::uint32_t)buff[3]) << 24); - return val; + return val; } else { // Byteswap on num_bytes diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp index 4612d7912..7fc943190 100644 --- a/host/lib/usrp/usrp1/usrp1_iface.hpp +++ b/host/lib/usrp/usrp1/usrp1_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// Copyright 2010-2013 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 @@ -19,7 +19,7 @@ #define INCLUDED_USRP1_IFACE_HPP #include "fx2_ctrl.hpp" -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <uhd/types/serial.hpp> #include <boost/shared_ptr.hpp> #include <boost/utility.hpp> @@ -42,7 +42,8 @@ * Provides a set of functions to implementation layer. * Including spi, peek, poke, control... */ -class usrp1_iface : public wb_iface, public uhd::i2c_iface, public uhd::spi_iface, boost::noncopyable{ +class usrp1_iface : public uhd::wb_iface, public uhd::i2c_iface, public uhd::spi_iface, boost::noncopyable +{ public: typedef boost::shared_ptr<usrp1_iface> sptr; diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 253ac1d6f..625926f36 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -258,12 +258,14 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->create<std::string>(rx_codec_path / "name").set("ad9522"); _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::rx_pga_gain_range); _tree->create<double>(rx_codec_path / "gains/pga/value") - .coerce(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1)); + .coerce(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1)) + .set(0.0); _tree->create<std::string>(tx_codec_path / "name").set("ad9522"); _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::tx_pga_gain_range); _tree->create<double>(tx_codec_path / "gains/pga/value") .subscribe(boost::bind(&usrp1_codec_ctrl::set_tx_pga_gain, _dbc[db].codec, _1)) - .publish(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec)); + .publish(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec)) + .set(0.0); } //////////////////////////////////////////////////////////////////// @@ -407,7 +409,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(_rx_subdev_spec); if (_tree->list(mb_path / "tx_dsps").size() > 0) _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(_tx_subdev_spec); - + } usrp1_impl::~usrp1_impl(void){ diff --git a/host/lib/usrp/usrp2/usrp2_clk_regs.hpp b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp index 8b185eac0..d5e506d8d 100644 --- a/host/lib/usrp/usrp2/usrp2_clk_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp @@ -22,8 +22,18 @@ class usrp2_clk_regs_t { public: - usrp2_clk_regs_t(void) { ; } + usrp2_clk_regs_t(void): + test(0), + fpga(0), + adc(0), + dac(0), + serdes(0), + exp(0), + tx_db(0), + rx_db(0) {} + usrp2_clk_regs_t(usrp2_iface::rev_type rev) { + fpga = adc = serdes = exp = tx_db = 0; test = 0; fpga = 1; dac = 3; @@ -54,7 +64,7 @@ public: //dont throw, it may be unitialized break; } - + rx_db = 7; } diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp index b48d05aa2..13dfb5b46 100644 --- a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp @@ -23,14 +23,15 @@ #include <uhd/transport/zero_copy.hpp> #include <boost/shared_ptr.hpp> #include <boost/utility.hpp> -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <string> /*! * The usrp2 FIFO control class: * Provide high-speed peek/poke interface. */ -class usrp2_fifo_ctrl : public wb_iface, public uhd::spi_iface{ +class usrp2_fifo_ctrl : public uhd::wb_iface, public uhd::spi_iface +{ public: typedef boost::shared_ptr<usrp2_fifo_ctrl> sptr; diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 3b230ca69..5f97045e1 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -271,7 +271,7 @@ public: //send and recv usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data, MIN_PROTO_COMPAT_I2C); UHD_ASSERT_THROW(ntohl(in_data.id) == USRP2_CTRL_ID_HERES_THE_I2C_DATA_DUDE); - UHD_ASSERT_THROW(in_data.data.i2c_args.addr = num_bytes); + UHD_ASSERT_THROW(in_data.data.i2c_args.bytes == num_bytes); //copy out the data byte_vector_t result(num_bytes); diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index ed4de02d5..a01f2ccfa 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2013 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,7 +25,7 @@ #include <boost/utility.hpp> #include <boost/function.hpp> #include "usrp2_regs.hpp" -#include "wb_iface.hpp" +#include <uhd/types/wb_iface.hpp> #include <string> /*! @@ -33,7 +33,8 @@ * Provides a set of functions to implementation layer. * Including spi, peek, poke, control... */ -class usrp2_iface : public wb_iface, public uhd::spi_iface, public uhd::i2c_iface{ +class usrp2_iface : public uhd::wb_iface, public uhd::spi_iface, public uhd::i2c_iface +{ public: typedef boost::shared_ptr<usrp2_iface> sptr; /*! diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index a6c0d87cf..f9988287f 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -57,7 +57,7 @@ static const std::string USRP2_EEPROM_MAP_KEY = "N100"; //! Make a usrp2 dboard interface. uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface( - wb_iface::sptr wb_iface, + uhd::wb_iface::sptr wb_iface, uhd::i2c_iface::sptr i2c_iface, uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clk_ctrl @@ -84,7 +84,7 @@ private: usrp2_iface::sptr iface; usrp2_fifo_ctrl::sptr fifo_ctrl; uhd::spi_iface::sptr spiface; - wb_iface::sptr wbiface; + uhd::wb_iface::sptr wbiface; usrp2_clock_ctrl::sptr clock; usrp2_codec_ctrl::sptr codec; uhd::gps_ctrl::sptr gps; diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index 53055314b..3e2bea1c6 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -34,8 +34,19 @@ namespace fs = boost::filesystem; /*********************************************************************** + * Get a list of paths for an environment variable + **********************************************************************/ +static std::string get_env_var(const std::string &var_name, const std::string &def_val = ""){ + const char *var_value_ptr = std::getenv(var_name.c_str()); + return (var_value_ptr == NULL)? def_val : var_value_ptr; +} + +static std::vector<fs::path> get_env_paths(const std::string &var_name){ + +/*********************************************************************** * Determine the paths separator **********************************************************************/ + #ifdef UHD_PLATFORM_WIN32 static const std::string env_path_sep = ";"; #else @@ -46,16 +57,6 @@ namespace fs = boost::filesystem; boost::tokenizer<boost::char_separator<char> > \ (inp, boost::char_separator<char>(env_path_sep.c_str())) -/*********************************************************************** - * Get a list of paths for an environment variable - **********************************************************************/ -static std::string get_env_var(const std::string &var_name, const std::string &def_val = ""){ - const char *var_value_ptr = std::getenv(var_name.c_str()); - return (var_value_ptr == NULL)? def_val : var_value_ptr; -} - -static std::vector<fs::path> get_env_paths(const std::string &var_name){ - std::string var_value = get_env_var(var_name); //convert to filesystem path, filter blank paths @@ -85,6 +86,7 @@ std::vector<fs::path> get_image_paths(void){ std::vector<fs::path> get_module_paths(void){ std::vector<fs::path> paths = get_env_paths("UHD_MODULE_PATH"); paths.push_back(fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "modules"); + paths.push_back(fs::path(uhd::get_pkg_path()) / "share" / "uhd" / "modules"); return paths; } @@ -113,8 +115,8 @@ std::string uhd::get_tmp_path(void){ //try the stdio define if available #ifdef P_tmpdir - if (P_tmpdir != NULL) return P_tmpdir; - #endif + return P_tmpdir; + #else //try unix environment variables tmp_path = std::getenv("TMPDIR"); @@ -122,6 +124,7 @@ std::string uhd::get_tmp_path(void){ //give up and use the unix default return "/tmp"; + #endif } std::string uhd::get_app_path(void){ diff --git a/host/lib/utils/tasks.cpp b/host/lib/utils/tasks.cpp index 1f735de06..08c32a5fb 100644 --- a/host/lib/utils/tasks.cpp +++ b/host/lib/utils/tasks.cpp @@ -16,11 +16,13 @@ // #include <uhd/utils/tasks.hpp> +#include <uhd/utils/msg_task.hpp> #include <uhd/utils/msg.hpp> #include <boost/thread/thread.hpp> #include <boost/thread/barrier.hpp> #include <exception> #include <iostream> +#include <vector> using namespace uhd; @@ -80,3 +82,100 @@ private: task::sptr task::make(const task_fcn_type &task_fcn){ return task::sptr(new task_impl(task_fcn)); } + +/* + * During shutdown pointers to queues for radio_ctrl_core might not be available anymore. + * msg_task_impl provides a dump_queue for such messages. + * ctrl_cores can check this queue for stranded messages. + */ + +class msg_task_impl : public msg_task{ +public: + + msg_task_impl(const task_fcn_type &task_fcn): + _spawn_barrier(2) + { + _thread_group.create_thread(boost::bind(&msg_task_impl::task_loop, this, task_fcn)); + _spawn_barrier.wait(); + } + + ~msg_task_impl(void){ + _running = false; + _thread_group.interrupt_all(); + _thread_group.join_all(); + } + + /* + * Returns the first message for the given SID. + * This way a radio_ctrl_core doesn't have to die in timeout but can check for stranded messages here. + * This might happen during shutdown when dtors are called. + * See also: comments in b200_io_impl->handle_async_task + */ + msg_payload_t get_msg_from_dump_queue(boost::uint32_t sid) + { + boost::mutex::scoped_lock lock(_mutex); + msg_payload_t b; + for (size_t i = 0; i < _dump_queue.size(); i++) { + if (sid == _dump_queue[i].first) { + b = _dump_queue[i].second; + _dump_queue.erase(_dump_queue.begin() + i); + break; + } + } + return b; + } + +private: + + void task_loop(const task_fcn_type &task_fcn){ + _running = true; + _spawn_barrier.wait(); + + try{ + while (_running){ + boost::optional<msg_type_t> buff = task_fcn(); + if(buff != boost::none){ + /* + * If a message gets stranded it is returned by task_fcn and then pushed to the dump_queue. + * This way ctrl_cores can check dump_queue for missing messages. + */ + boost::mutex::scoped_lock lock(_mutex); + _dump_queue.push_back(buff.get() ); + } + } + } + catch(const boost::thread_interrupted &){ + //this is an ok way to exit the task loop + } + catch(const std::exception &e){ + do_error_msg(e.what()); + } + catch(...){ + //FIXME + //Unfortunately, this is also an ok way to end a task, + //because on some systems boost throws uncatchables. + } + } + + void do_error_msg(const std::string &msg){ + UHD_MSG(error) + << "An unexpected exception was caught in a task loop." << std::endl + << "The task loop will now exit, things may not work." << std::endl + << msg << std::endl + ; + } + + boost::mutex _mutex; + boost::thread_group _thread_group; + boost::barrier _spawn_barrier; + bool _running; + + /* + * This queue holds stranded messages until a radio_ctrl_core grabs them via 'get_msg_from_dump_queue'. + */ + std::vector <msg_type_t> _dump_queue; +}; + +msg_task::sptr msg_task::make(const task_fcn_type &task_fcn){ + return msg_task::sptr(new msg_task_impl(task_fcn)); +} diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp index 9339a9739..316e24779 100644 --- a/host/tests/sph_recv_test.cpp +++ b/host/tests/sph_recv_test.cpp @@ -395,7 +395,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){ //check the received packets size_t num_accum_samps = 0; - std::vector<std::complex<float> > mem(NUM_SAMPS_PER_BUFF*NCHANNELS); + std::complex<float> mem[NUM_SAMPS_PER_BUFF*NCHANNELS]; std::vector<std::complex<float> *> buffs(NCHANNELS); for (size_t ch = 0; ch < NCHANNELS; ch++){ buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF]; @@ -481,7 +481,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){ //check the received packets size_t num_accum_samps = 0; - std::vector<std::complex<float> > mem(NUM_SAMPS_PER_BUFF*NCHANNELS); + std::complex<float> mem[NUM_SAMPS_PER_BUFF*NCHANNELS]; std::vector<std::complex<float> *> buffs(NCHANNELS); for (size_t ch = 0; ch < NCHANNELS; ch++){ buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF]; @@ -574,7 +574,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){ //check the received packets size_t num_accum_samps = 0; - std::vector<std::complex<float> > mem(NUM_SAMPS_PER_BUFF*NCHANNELS); + std::complex<float> mem[NUM_SAMPS_PER_BUFF*NCHANNELS]; std::vector<std::complex<float> *> buffs(NCHANNELS); for (size_t ch = 0; ch < NCHANNELS; ch++){ buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF]; @@ -659,7 +659,7 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){ //check the received packets size_t num_accum_samps = 0; - std::vector<std::complex<float> > mem(NUM_SAMPS_PER_BUFF*NCHANNELS); + std::complex<float> mem[NUM_SAMPS_PER_BUFF*NCHANNELS]; std::vector<std::complex<float> *> buffs(NCHANNELS); for (size_t ch = 0; ch < NCHANNELS; ch++){ buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF]; diff --git a/host/tests/time_spec_test.cpp b/host/tests/time_spec_test.cpp index 37a039cc5..c9b9652f9 100644 --- a/host/tests/time_spec_test.cpp +++ b/host/tests/time_spec_test.cpp @@ -117,6 +117,7 @@ BOOST_AUTO_TEST_CASE(test_time_error_irrational_rate) const uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(tick_in, rate); const long long tick_out = ts.to_ticks(rate); const long long err = tick_in - tick_out; + std::streamsize precision = std::cout.precision(); std::cout << std::setprecision(18); std::cout << "time ............ " << ts.get_real_secs() << std::endl; @@ -124,6 +125,7 @@ BOOST_AUTO_TEST_CASE(test_time_error_irrational_rate) std::cout << "tick out ........ " << tick_out << std::endl; std::cout << "tick error ...... " << err << std::endl; std::cout << std::endl; + std::cout.precision(precision); BOOST_CHECK_EQUAL(err, (long long)(0)); } diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 92df0b7cf..abf2a546b 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -50,6 +50,8 @@ IF(ENABLE_USB) b2xx_fx3_utils.cpp ) INCLUDE_DIRECTORIES(${LIBUSB_INCLUDE_DIRS}) + # Additional include directories for b2xx_fx3_utils + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../lib/usrp/b200 ${CMAKE_CURRENT_SOURCE_DIR}/../lib/usrp/common) ENDIF(ENABLE_USB) IF(LINUX AND ENABLE_USB) diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp index 36688c6c7..96cd1f3e7 100644 --- a/host/utils/b2xx_fx3_utils.cpp +++ b/host/utils/b2xx_fx3_utils.cpp @@ -34,43 +34,37 @@ #include <boost/thread/thread.hpp> #include <boost/functional/hash.hpp> +#include <b200_iface.hpp> #include <uhd/config.hpp> -#include <uhd/utils/msg.hpp> - -const static boost::uint16_t FX3_VID = 0x04b4; -const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; -const static boost::uint16_t FX3_REENUM_PID = 0x00f0; -const static boost::uint16_t B2XX_VID = 0x2500; -const static boost::uint16_t B2XX_PID = 0x0020; - -const static boost::uint8_t VRT_VENDOR_OUT = (LIBUSB_REQUEST_TYPE_VENDOR - | LIBUSB_ENDPOINT_OUT); -const static boost::uint8_t VRT_VENDOR_IN = (LIBUSB_REQUEST_TYPE_VENDOR - | LIBUSB_ENDPOINT_IN); -const static boost::uint8_t FX3_FIRMWARE_LOAD = 0xA0; - -const static boost::uint8_t B2XX_VREQ_FPGA_START = 0x02; -const static boost::uint8_t B2XX_VREQ_FPGA_DATA = 0x12; -const static boost::uint8_t B2XX_VREQ_SET_FPGA_HASH = 0x1C; -const static boost::uint8_t B2XX_VREQ_GET_FPGA_HASH = 0x1D; -const static boost::uint8_t B2XX_VREQ_FPGA_RESET = 0x62; -const static boost::uint8_t B2XX_VREQ_GET_USB = 0x80; -const static boost::uint8_t B2XX_VREQ_GET_STATUS = 0x83; -const static boost::uint8_t B2XX_VREQ_FX3_RESET = 0x99; -const static boost::uint8_t B2XX_VREQ_EEPROM_WRITE = 0xBA; - -const static boost::uint8_t FX3_STATE_FPGA_READY = 0x00; -const static boost::uint8_t FX3_STATE_CONFIGURING_FPGA = 0x01; -const static boost::uint8_t FX3_STATE_BUSY = 0x02; -const static boost::uint8_t FX3_STATE_RUNNING = 0x03; - -typedef boost::uint32_t hash_type; -typedef std::vector<boost::uint8_t> byte_vector_t; - +#include <uhd/transport/usb_control.hpp> +#include <uhd/transport/usb_device_handle.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/images.hpp> namespace po = boost::program_options; namespace fs = boost::filesystem; +struct vid_pid_t { + boost::uint16_t vid; + boost::uint16_t pid; +}; +const static vid_pid_t known_vid_pids[] = { + {FX3_VID, FX3_DEFAULT_PID}, + {FX3_VID, FX3_REENUM_PID}, + {B200_VENDOR_ID, B200_PRODUCT_ID} + }; +const static std::vector<vid_pid_t> known_vid_pid_vector(known_vid_pids, known_vid_pids + (sizeof(known_vid_pids) / sizeof(known_vid_pids[0]))); +const static boost::uint8_t eeprom_init_values[] = { + 0x43, + 0x59, + 0x14, + 0xB2, + (B200_PRODUCT_ID & 0xff), + (B200_PRODUCT_ID >> 8), + (B200_VENDOR_ID & 0xff), + (B200_VENDOR_ID >> 8) + }; +const static uhd::byte_vector_t eeprom_init_value_vector(eeprom_init_values, eeprom_init_values + (sizeof(eeprom_init_values) / sizeof(eeprom_init_values[0]))); //!used with lexical cast to parse a hex string template <class T> struct to_hex{ @@ -90,657 +84,472 @@ boost::uint16_t atoh(const std::string &string){ return boost::lexical_cast<boost::uint16_t>(string); } - -/*! - * Create a file hash - * The hash will be used to identify the loaded firmware and fpga image - * \param filename file used to generate hash value - * \return hash value in a size_t type - */ -static hash_type generate_hash(const char *filename) +int reset_usb() { - std::ifstream file(filename); - if (not file){ - std::cerr << std::string("cannot open input file ") + filename - << std::endl; - } - - size_t hash = 0; + /* Okay, first, we need to discover what the path is to the ehci and + * xhci device files. */ + std::set<fs::path> path_list; + path_list.insert("/sys/bus/pci/drivers/xhci-pci/"); + path_list.insert("/sys/bus/pci/drivers/ehci-pci/"); + path_list.insert("/sys/bus/pci/drivers/xhci_hcd/"); + path_list.insert("/sys/bus/pci/drivers/ehci_hcd/"); - char ch; - while (file.get(ch)) { - boost::hash_combine(hash, ch); - } - - if (not file.eof()){ - std::cerr << std::string("file error ") + filename << std::endl; - } + /* Check each of the possible paths above to find which ones this system + * uses. */ + for(std::set<fs::path>::iterator found = path_list.begin(); + found != path_list.end(); ++found) { - file.close(); - return hash_type(hash); -} + if(fs::exists(*found)) { -/*! - * Verify checksum of a Intel HEX record - * \param record a line from an Intel HEX file - * \return true if record is valid, false otherwise - */ -bool checksum(std::string *record) { - - size_t len = record->length(); - unsigned int i; - unsigned char sum = 0; - unsigned int val; - - for (i = 1; i < len; i += 2) { - std::istringstream(record->substr(i, 2)) >> std::hex >> val; - sum += val; - } + fs::path devpath = *found; - if (sum == 0) - return true; - else - return false; -} + std::set<fs::path> globbed; + /* Now, glob all of the files in the directory. */ + fs::directory_iterator end_itr; + for(fs::directory_iterator itr(devpath); itr != end_itr; ++itr) { + globbed.insert((*itr).path()); + } -/*! - * Parse Intel HEX record - * - * \param record a line from an Intel HEX file - * \param len output length of record - * \param addr output address - * \param type output type - * \param data output data - * \return true if record is sucessfully read, false on error - */ -bool parse_record(std::string *record, boost::uint16_t &len, boost::uint16_t &addr, - uint16_t &type, unsigned char* data) { - - unsigned int i; - std::string _data; - unsigned int val; - - if (record->substr(0, 1) != ":") - return false; - - std::istringstream(record->substr(1, 2)) >> std::hex >> len; - std::istringstream(record->substr(3, 4)) >> std::hex >> addr; - std::istringstream(record->substr(7, 2)) >> std::hex >> type; - - for (i = 0; i < len; i++) { - std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; - data[i] = (unsigned char) val; + /* Check each file path string to see if it is a device file. */ + for(std::set<fs::path>::iterator it = globbed.begin(); + it != globbed.end(); ++it) { + + std::string file = fs::path((*it).filename()).string(); + + if (file.length() < 5) + continue; + + if(file.compare(0, 5, "0000:") == 0) { + /* Un-bind the device. */ + std::fstream unbind((devpath.string() + "unbind").c_str(), + std::fstream::out); + unbind << file; + unbind.close(); + + /* Re-bind the device. */ + std::cout << "Re-binding: " << file << " in " + << devpath.string() << std::endl; + std::fstream bind((devpath.string() + "bind").c_str(), + std::fstream::out); + bind << file; + bind.close(); + } + } + } } - return true; + return 0; } +uhd::transport::usb_device_handle::sptr open_device(const boost::uint16_t vid, const boost::uint16_t pid, const bool user_supplied = false) +{ + std::vector<uhd::transport::usb_device_handle::sptr> handles; + uhd::transport::usb_device_handle::sptr handle; + vid_pid_t vp = {vid, pid}; + + try { + // try caller's VID/PID first + handles = uhd::transport::usb_device_handle::get_device_list(vp.vid,vp.pid); + if (user_supplied && handles.size() == 0) + std::cerr << (boost::format("Failed to open device with VID 0x%04x and PID 0x%04x - trying other known VID/PIDs") % vid % pid).str() << std::endl; + + // try known VID/PIDs next + for (size_t i = 0; handles.size() == 0 && i < known_vid_pid_vector.size(); i++) + { + vp = known_vid_pid_vector[i]; + handles = uhd::transport::usb_device_handle::get_device_list(vp.vid,vp.pid); + } -/*! - * Write data to the FX3. - * - * \param dev_handle the libusb-1.0 device handle - * \param request the usb transfer request type - * \param value the USB bValue - * \param index the USB bIndex - * \param buff the data to write - * \param length the number of bytes to write - * \return the number of bytes written - */ -libusb_error fx3_control_write(libusb_device_handle *dev_handle, boost::uint8_t request, - boost::uint16_t value, boost::uint16_t index, unsigned char *buff, - boost::uint16_t length, boost::uint32_t timeout = 0) { - -#if 0 - if(DEBUG) { - std::cout << "Writing: <" << std::hex << std::setw(6) << std::showbase \ - << std::internal << std::setfill('0') << (int) request \ - << ", " << std::setw(6) << (int) VRT_VENDOR_OUT \ - << ", " << std::setw(6) << value \ - << ", " << std::setw(6) << index \ - << ", " << std::dec << std::setw(2) << length \ - << ">" << std::endl; - - std::cout << "\t\tData: 0x " << std::noshowbase; - - for(int count = 0; count < length; count++) { - std::cout << std::hex << std::setw(2) << (int) buff[count] << " "; + if (handles.size() > 0) + { + handle = handles[0]; + std::cout << (boost::format("Device opened (VID=0x%04x,PID=0x%04x)") % vp.vid % vp.pid).str() << std::endl; } - std::cout << std::showbase << std::endl; + if (!handle) + std::cerr << "Cannot open device" << std::endl; } -#endif - - return (libusb_error) libusb_control_transfer(dev_handle, VRT_VENDOR_OUT, request, \ - value, index, buff, length, timeout); -} - - -/*! - * Read data from the FX3. - * - * \param dev_handle the libusb-1.0 device handle - * \param request the usb transfer request type - * \param value the USB bValue - * \param index the USB bIndex - * \param buff a buffer to store the read bytes to - * \param length the number of bytes to read - * \return the number of bytes read - */ -libusb_error fx3_control_read(libusb_device_handle *dev_handle, boost::uint8_t request, - boost::uint16_t value, boost::uint16_t index, unsigned char *buff, - boost::uint16_t length, boost::uint32_t timeout = 0) { - -#if 0 - if(DEBUG) { - std::cout << "Reading: <" << std::hex << std::setw(6) << std::showbase \ - << std::internal << std::setfill('0') << (int) request \ - << ", " << std::setw(6) << (int) VRT_VENDOR_IN \ - << ", " << std::setw(6) << value \ - << ", " << std::setw(6) << index \ - << ", " << std::dec << std::setw(2) << length \ - << ">" << std::endl << std::endl; + catch(const std::exception &e) { + std::cerr << "Failed to communicate with the device!" << std::endl; + #ifdef UHD_PLATFORM_WIN32 + std::cerr << "The necessary drivers are not installed. Read the UHD Transport Application Notes for details:\nhttp://files.ettus.com/uhd_docs/manual/html/transport.html" << std::endl; + #endif /* UHD_PLATFORM_WIN32 */ + handle.reset(); } -#endif - return (libusb_error) libusb_control_transfer(dev_handle, VRT_VENDOR_IN, request, \ - value, index, buff, length, timeout); + return handle; } +b200_iface::sptr make_b200_iface(const uhd::transport::usb_device_handle::sptr &handle) +{ + b200_iface::sptr b200; -void write_eeprom(libusb_device_handle *dev_handle, boost::uint8_t addr, - boost::uint8_t offset, const byte_vector_t &bytes) { - fx3_control_write(dev_handle, B2XX_VREQ_EEPROM_WRITE, - 0, offset | (boost::uint16_t(addr) << 8), - (unsigned char *) &bytes[0], - bytes.size()); -} - - -boost::uint8_t get_fx3_status(libusb_device_handle *dev_handle) { - - unsigned char rx_data[1]; + try { + uhd::transport::usb_control::sptr usb_ctrl = uhd::transport::usb_control::make(handle, 0); + b200 = b200_iface::make(usb_ctrl); - fx3_control_read(dev_handle, B2XX_VREQ_GET_STATUS, 0x00, 0x00, rx_data, 1); + if (!b200) + std::cerr << "Cannot create device interface" << std::endl; + } + catch(const std::exception &e) { + std::cerr << "Failed to communicate with the device!" << std::endl; + #ifdef UHD_PLATFORM_WIN32 + std::cerr << "The necessary drivers are not installed. Read the UHD Transport Application Notes for details:\nhttp://files.ettus.com/uhd_docs/manual/html/transport.html" << std::endl; + #endif /* UHD_PLATFORM_WIN32 */ + b200.reset(); + } - return boost::lexical_cast<boost::uint8_t>(rx_data[0]); + return b200; } -void usrp_get_fpga_hash(libusb_device_handle *dev_handle, hash_type &hash) { - fx3_control_read(dev_handle, B2XX_VREQ_GET_FPGA_HASH, 0x00, 0x00, - (unsigned char*) &hash, 4, 500); -} +int read_eeprom(b200_iface::sptr& b200, uhd::byte_vector_t& data) +{ + try { + data = b200->read_eeprom(0x0, 0x0, 8); + } catch (std::exception &e) { + std::cerr << "Exception while reading EEPROM: " << e.what() << std::endl; + return -1; + } -void usrp_set_fpga_hash(libusb_device_handle *dev_handle, hash_type hash) { - fx3_control_write(dev_handle, B2XX_VREQ_SET_FPGA_HASH, 0x00, 0x00, - (unsigned char*) &hash, 4); + return 0; } -boost::int32_t load_fpga(libusb_device_handle *dev_handle, - const std::string filestring) { - - if (filestring.empty()) - { - std::cerr << "load_fpga: input file is empty." << std::endl; - exit(-1); +int write_eeprom(b200_iface::sptr& b200, const uhd::byte_vector_t& data) +{ + try { + b200->write_eeprom(0x0, 0x0, data); + } catch (std::exception &e) { + std::cerr << "Exception while writing EEPROM: " << e.what() << std::endl; + return -1; } - boost::uint8_t fx3_state = 0; - - const char *filename = filestring.c_str(); + return 0; +} - hash_type hash = generate_hash(filename); - hash_type loaded_hash; usrp_get_fpga_hash(dev_handle, loaded_hash); - if (hash == loaded_hash) return 0; +int verify_eeprom(b200_iface::sptr& b200, const uhd::byte_vector_t& data) +{ + bool verified = true; + uhd::byte_vector_t read_bytes; + if (read_eeprom(b200, read_bytes)) + return -1; - size_t file_size = 0; + if (data.size() != read_bytes.size()) { - std::ifstream file(filename, std::ios::in | std::ios::binary | std::ios::ate); - file_size = file.tellg(); + std::cerr << "ERROR: Only able to verify first " << std::min(data.size(), read_bytes.size()) << " bytes." << std::endl; + verified = false; } - std::ifstream file; - file.open(filename, std::ios::in | std::ios::binary); - - if(!file.good()) { - std::cerr << "load_fpga: cannot open FPGA input file." << std::endl; - exit(-1); + for (size_t i = 0; i < std::min(data.size(), read_bytes.size()); i++) { + if (data[i] != read_bytes[i]) { + verified = false; + std::cerr << "Byte " << i << " Expected: " << data[i] << ", Got: " << read_bytes[i] << std::endl; + } } - do { - fx3_state = get_fx3_status(dev_handle); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } while(fx3_state != FX3_STATE_FPGA_READY); - - std::cout << "Loading FPGA image: " \ - << filestring << "..." << std::flush; - - unsigned char out_buff[64]; - memset(out_buff, 0x00, sizeof(out_buff)); - fx3_control_write(dev_handle, B2XX_VREQ_FPGA_START, 0, 0, out_buff, 1, 1000); - - do { - fx3_state = get_fx3_status(dev_handle); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } while(fx3_state != FX3_STATE_CONFIGURING_FPGA); - - - size_t bytes_sent = 0; - while(!file.eof()) { - file.read((char *) out_buff, sizeof(out_buff)); - const std::streamsize n = file.gcount(); - if(n == 0) continue; - - boost::uint16_t transfer_count = boost::uint16_t(n); - - /* Send the data to the device. */ - fx3_control_write(dev_handle, B2XX_VREQ_FPGA_DATA, 0, 0, out_buff, - transfer_count, 5000); - - if (bytes_sent == 0) std::cout << " 0%" << std::flush; - const size_t percent_before = size_t((bytes_sent*100)/file_size); - bytes_sent += transfer_count; - const size_t percent_after = size_t((bytes_sent*100)/file_size); - if (percent_before/10 != percent_after/10) { - std::cout << "\b\b\b\b" << std::setw(3) << percent_after - << "%" << std::flush; - } + if (!verified) { + std::cerr << "Verification failed" << std::endl; + return -1; } - file.close(); + return 0; +} + +int write_and_verify_eeprom(b200_iface::sptr& b200, const uhd::byte_vector_t& data) +{ + if (write_eeprom(b200, data)) + return -1; + if (verify_eeprom(b200, data)) + return -1; - do { - fx3_state = get_fx3_status(dev_handle); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } while(fx3_state != FX3_STATE_RUNNING); + return 0; +} - usrp_set_fpga_hash(dev_handle, hash); +int erase_eeprom(b200_iface::sptr& b200) +{ + uhd::byte_vector_t bytes(8); - std::cout << "\b\b\b\b done" << std::endl; + memset(&bytes[0], 0xFF, 8); + if (write_and_verify_eeprom(b200, bytes)) + return -1; return 0; } +boost::int32_t main(boost::int32_t argc, char *argv[]) { + boost::uint16_t vid, pid; + std::string pid_str, vid_str, fw_file, fpga_file; + bool user_supplied_vid_pid = false; -/*! - * Program the FX3 with a firmware file (Intel HEX format) - * - * \param dev_handle the libusb-1.0 device handle - * \param filestring the filename of the firmware file - * \return 0 for success, otherwise error code - */ -boost::int32_t fx3_load_firmware(libusb_device_handle *dev_handle, \ - std::string filestring) { - - if (filestring.empty()) - { - std::cerr << "fx3_load_firmware: input file is empty." << std::endl; - exit(-1); - } + po::options_description visible("Allowed options"); + visible.add_options() + ("help,h", "help message") + ("vid,v", po::value<std::string>(&vid_str), + "Specify VID of device to use.") + ("pid,p", po::value<std::string>(&pid_str), + "Specify PID of device to use.") + ("speed,S", "Read back the USB mode currently in use.") + ("reset-device,D", "Reset the B2xx Device.") + ("reset-fpga,F", "Reset the FPGA (does not require re-programming.") + ("reset-usb,U", "Reset the USB subsystem on your host computer.") + ("init-device,I", "Initialize a B2xx device.") + ("load-fw,W", po::value<std::string>(&fw_file), + "Load a firmware (hex) file into the FX3.") + ("load-fpga,L", po::value<std::string>(&fpga_file), + "Load a FPGA (bin) file into the FPGA.") + ; - const char *filename = filestring.c_str(); + // Hidden options provided for testing - use at your own risk! + po::options_description hidden("Hidden options"); + hidden.add_options() + ("uninit-device,U", "Uninitialize a B2xx device.") + ("read-eeprom,R", "Read first 8 bytes of EEPROM") + ("erase-eeprom,E", "Erase first 8 bytes of EEPROM"); - /* Fields used in each USB control transfer. */ - boost::uint16_t len = 0; - boost::uint16_t type = 0; - boost::uint16_t lower_address_bits = 0x0000; - unsigned char data[512]; + po::options_description desc; + desc.add(visible); + desc.add(hidden); - /* Can be set by the Intel HEX record 0x04, used for all 0x00 records - * thereafter. Note this field takes the place of the 'index' parameter in - * libusb calls, and is necessary for FX3's 32-bit addressing. */ - boost::uint16_t upper_address_bits = 0x0000; + po::variables_map vm; - std::ifstream file; - file.open(filename, std::ifstream::in); + try { + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + } catch (std::exception &e) { + std::cerr << "Exception while parsing arguments: " << e.what() << std::endl; + std::cout << boost::format("B2xx Utility Program %s") % visible << std::endl; + return ~0; + } - if(!file.good()) { - std::cerr << "fx3_load_firmware: cannot open firmware input file" - << std::endl; - exit(-1); + if (vm.count("help")){ + try { + std::cout << boost::format("B2xx Utility Program %s") % visible << std::endl; + } catch(...) {} + return ~0; } - std::cout << "Loading firmware image: " \ - << filestring << "..." << std::flush; + if (vm.count("reset-usb")) { + return reset_usb(); + } - while (!file.eof()) { - boost::int32_t ret = 0; - std::string record; - file >> record; + uhd::transport::usb_device_handle::sptr handle; + b200_iface::sptr b200; - /* Check for valid Intel HEX record. */ - if (!checksum(&record) || !parse_record(&record, len, \ - lower_address_bits, type, data)) { - std::cerr << "fx3_load_firmware: bad intel hex record checksum" - << std::endl; + vid = B200_VENDOR_ID; // Default + pid = B200_PRODUCT_ID; // Default + if (vm.count("vid") && vm.count("pid")) + { + try { + vid = atoh(vid_str); + pid = atoh(pid_str); + } catch (std::exception &e) { + std::cerr << "Exception while parsing VID and PID: " << e.what() << std:: endl; + return ~0; } + user_supplied_vid_pid = true; + } - /* Type 0x00: Data. */ - if (type == 0x00) { - ret = fx3_control_write(dev_handle, FX3_FIRMWARE_LOAD, \ - lower_address_bits, upper_address_bits, data, len); - - if (ret < 0) { - std::cerr << "usrp_load_firmware: usrp_control_write failed" - << std::endl; - } - } + // open the device + handle = open_device(vid, pid, user_supplied_vid_pid); + if (!handle) + return -1; + std::cout << "B2xx detected..." << std::flush; - /* Type 0x01: EOF. */ - else if (type == 0x01) { - if (lower_address_bits != 0x0000 || len != 0 ) { - std::cerr << "fx3_load_firmware: For EOF record, address must be 0, length must be 0." << std::endl; - } + // make the interface + b200 = make_b200_iface(handle); + if (!b200) + return -1; + std::cout << " Control of B2xx granted..." << std::endl << std::endl; - /* Successful termination! */ - file.close(); + // if we are supposed to load a new firmware image and one already exists, reset the FX3 so we can load the new one + if (vm.count("load-fw") && handle->firmware_loaded()) + { + std::cout << "Overwriting existing firmware" << std::endl; - /* Let the system settle. */ - boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - return 0; + // before we reset, make sure we have a good firmware file + if(!(fs::exists(fw_file))) + { + std::cerr << "Invalid firmware filepath: " << fw_file << std::endl; + return -1; } - /* Type 0x04: Extended Linear Address Record. */ - else if (type == 0x04) { - if (lower_address_bits != 0x0000 || len != 2 ) { - std::cerr << "fx3_load_firmware: For ELA record, address must be 0, length must be 2." << std::endl; - } - - upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[1] & 0x00FF)); + // reset the device + try { + b200->reset_fx3(); + } catch (std::exception &e) { + std::cerr << "Exception while reseting FX3: " << e.what() << std::endl; } - /* Type 0x05: Start Linear Address Record. */ - else if (type == 0x05) { - if (lower_address_bits != 0x0000 || len != 4 ) { - std::cerr << "fx3_load_firmware: For SLA record, address must be 0, length must be 4." << std::endl; - } + // re-open device + b200.reset(); + handle.reset(); + boost::this_thread::sleep(boost::posix_time::seconds(2)); // wait 2 seconds for FX3 to reset + handle = open_device(vid, pid); + if (!handle) + return -1; + b200 = make_b200_iface(handle); + if (!b200) + return -1; + } - /* The firmware load is complete. We now need to tell the CPU - * to jump to an execution address start point, now contained within - * the data field. Parse these address bits out, and then push the - * instruction. */ - upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[1] & 0x00FF)); - lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[3] & 0x00FF)); + // Check to make sure firmware is loaded + if (!(handle->firmware_loaded())) + { + std::cout << "Loading firmware" << std::endl; - fx3_control_write(dev_handle, FX3_FIRMWARE_LOAD, lower_address_bits, \ - upper_address_bits, 0, 0); + if (fw_file.empty()) + fw_file = uhd::find_image_path(B200_FW_FILE_NAME); - std::cout << " done" << std::endl; + if(fw_file.empty()) { + std::cerr << "Firmware image not found!" << std::endl; + return -1; } - /* If we receive an unknown record type, error out. */ - else { - std::cerr << "fx3_load_firmware: unsupported record type." << std::endl; + if(!(fs::exists(fw_file))) { + std::cerr << "Invalid filepath: " << fw_file << std::endl; + return -1; } - } - - /* There was no valid EOF. */ - std::cerr << "fx3_load_firmware: No EOF record found." << std::cout; - return ~0; -} + // load firmware + try { + b200->load_firmware(fw_file); + } catch (std::exception &e) { + std::cerr << "Exception while loading firmware: " << e.what() << std::endl; + return ~0; + } -boost::int32_t main(boost::int32_t argc, char *argv[]) { - boost::uint16_t vid, pid; - std::string pid_str, vid_str, fw_file, fpga_file; + // re-open device + b200.reset(); + handle.reset(); + handle = open_device(vid, pid); + if (!handle) + return -1; + b200 = make_b200_iface(handle); + if (!b200) + return -1; - po::options_description desc("Allowed options"); - desc.add_options() - ("help,h", "help message") - ("vid,v", po::value<std::string>(&vid_str)->default_value("0x2500"), - "Specify VID of device to use.") - ("pid,p", po::value<std::string>(&pid_str)->default_value("0x0020"), - "Specify PID of device to use.") - ("speed,S", "Read back the USB mode currently in use.") - ("reset-device,D", "Reset the B2xx Device.") - ("reset-fpga,F", "Reset the FPGA (does not require re-programming.") - ("reset-usb,U", "Reset the USB subsystem on your host computer.") - ("init-device,I", "Initialize a B2xx device.") - ("load-fw,W", po::value<std::string>(&fw_file)->default_value(""), - "Load a firmware (hex) file into the FX3.") - ("load-fpga,L", po::value<std::string>(&fpga_file)->default_value(""), - "Load a FPGA (bin) file into the FPGA.") - ; + std::cout << "Firmware loaded" << std::endl; + } - po::variables_map vm; - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); + // Added for testing purposes - not exposed + if (vm.count("read-eeprom")) + { + uhd::byte_vector_t data; - if (vm.count("help")){ - std::cout << boost::format("B2xx Utilitiy Program %s") % desc << std::endl; - return ~0; - } else if (vm.count("reset-usb")) { - /* Okay, first, we need to discover what the path is to the ehci and - * xhci device files. */ - std::set<fs::path> path_list; - path_list.insert("/sys/bus/pci/drivers/xhci-pci/"); - path_list.insert("/sys/bus/pci/drivers/ehci-pci/"); - path_list.insert("/sys/bus/pci/drivers/xhci_hcd/"); - path_list.insert("/sys/bus/pci/drivers/ehci_hcd/"); - - /* Check each of the possible paths above to find which ones this system - * uses. */ - for(std::set<fs::path>::iterator found = path_list.begin(); - found != path_list.end(); ++found) { - - if(fs::exists(*found)) { - - fs::path devpath = *found; - - std::set<fs::path> globbed; - - /* Now, glob all of the files in the directory. */ - fs::directory_iterator end_itr; - for(fs::directory_iterator itr(devpath); itr != end_itr; ++itr) { - globbed.insert((*itr).path()); - } + if (read_eeprom(b200, data)) + return -1; - /* Check each file path string to see if it is a device file. */ - for(std::set<fs::path>::iterator it = globbed.begin(); - it != globbed.end(); ++it) { - - std::string file = fs::path((*it).filename()).string(); - - if(file.compare(0, 5, "0000:") == 0) { - /* Un-bind the device. */ - std::fstream unbind((devpath.string() + "unbind").c_str(), - std::fstream::out); - unbind << file; - unbind.close(); - - /* Re-bind the device. */ - std::cout << "Re-binding: " << file << " in " - << devpath.string() << std::endl; - std::fstream bind((devpath.string() + "bind").c_str(), - std::fstream::out); - bind << file; - bind.close(); - } - } - } - } + for (int i = 0; i < 8; i++) + std::cout << i << ": " << boost::format("0x%X") % (int)data[i] << std::endl; return 0; } - vid = atoh(vid_str); - pid = atoh(pid_str); - - /* Pointer to pointer of device, used to retrieve a list of devices. */ - libusb_device **devs; - libusb_device_handle *dev_handle; - libusb_context *ctx = NULL; - libusb_error error_code; - - libusb_init(&ctx); - libusb_set_debug(ctx, 3); - libusb_get_device_list(ctx, &devs); - - /* If we are initializing the device, the VID/PID will default to the - * Cypress VID/PID for the initial FW load. */ - if (vm.count("init-device")) { - dev_handle = libusb_open_device_with_vid_pid(ctx, FX3_VID, - FX3_DEFAULT_PID); - if(dev_handle == NULL) { - std::cerr << "Cannot open device with vid: " << vid << " and pid: " - << pid << std::endl; + // Added for testing purposes - not exposed + if (vm.count("erase-eeprom")) + { + if (erase_eeprom(b200)) return -1; - } else { std::cout << "Uninitialized B2xx detected..." << std::flush; } - libusb_free_device_list(devs, 1); - /* Find out if kernel driver is attached, and if so, detach it. */ - if(libusb_kernel_driver_active(dev_handle, 0) == 1) { - std::cout << " Competing Driver Identified... " << std::flush; + std::cout << "Erase Successful!" << std::endl; - if(libusb_detach_kernel_driver(dev_handle, 0) == 0) { - std::cout << " Competing Driver Destroyed!" << std::flush; - } - } - libusb_claim_interface(dev_handle, 0); - std::cout << " Control of B2xx granted..." << std::endl << std::endl; - - /* Load the FW. */ - error_code = (libusb_error) fx3_load_firmware(dev_handle, fw_file); - if(error_code != 0) { - std::cerr << std::flush << "Error loading firmware. Error code: " - << error_code << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); - return ~0; - } + return 0; + } - /* Let the device re-enumerate. */ - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - dev_handle = libusb_open_device_with_vid_pid(ctx, FX3_VID, - FX3_REENUM_PID); - if(dev_handle == NULL) { - std::cerr << "Cannot open device with vid: " << vid << " and pid: " - << pid << std::endl; - return -1; - } else { - std::cout << "Detected in-progress init of B2xx..." << std::flush; - } - //libusb_free_device_list(devs, 1); - libusb_claim_interface(dev_handle, 0); - std::cout << " Reenumeration complete, Device claimed..." - << std::endl; + // Added for testing purposes - not exposed + if (vm.count("uninit-device")) + { + // erase EEPROM + erase_eeprom(b200); - /* Now, initialize the device. */ - byte_vector_t bytes(8); - bytes[0] = 0x43; - bytes[1] = 0x59; - bytes[2] = 0x14; - bytes[3] = 0xB2; - bytes[4] = (B2XX_PID & 0xff); - bytes[5] = (B2XX_PID >> 8); - bytes[6] = (B2XX_VID & 0xff); - bytes[7] = (B2XX_VID >> 8); - write_eeprom(dev_handle, 0x0, 0x0, bytes); - std::cout << "EEPROM initialized, resetting device..." + std::cout << "EEPROM uninitialized, resetting device..." << std::endl << std::endl; - /* Reset the device! */ - boost::uint8_t data_buffer[1]; - fx3_control_write(dev_handle, B2XX_VREQ_FX3_RESET, - 0x00, 0x00, data_buffer, 1, 5000); + // reset the device + try { + b200->reset_fx3(); + } catch (uhd::exception &e) { + std::cerr << "Exception while resetting FX3: " << e.what() << std::endl; + return -1; + } - std::cout << "Initialization Process Complete." + std::cout << "Uninitialization Process Complete." << std::endl << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); + return 0; } - dev_handle = libusb_open_device_with_vid_pid(ctx, vid, pid); - if(dev_handle == NULL) { - std::cerr << "Cannot open device with vid: " << vid << " and pid: " - << pid << std::endl; + /* If we are initializing the device, the VID/PID should default to the + * Cypress VID/PID for the initial FW load, but we can initialize from any state. */ + if (vm.count("init-device")) + { + /* Now, initialize the device. */ + if (write_and_verify_eeprom(b200, eeprom_init_value_vector)) return -1; - } else { std::cout << "Reactor Core Online..." << std::flush; } - libusb_free_device_list(devs, 1); - /* Find out if kernel driver is attached, and if so, detach it. */ - if(libusb_kernel_driver_active(dev_handle, 0) == 1) { - std::cout << " Competing Driver Identified... " << std::flush; + std::cout << "EEPROM initialized, resetting device..." + << std::endl << std::endl; - if(libusb_detach_kernel_driver(dev_handle, 0) == 0) { - std::cout << " Competing Driver Destroyed!" << std::flush; + /* Reset the device! */ + try { + b200->reset_fx3(); + } catch (const std::exception &e) { + std::cerr << "Exception while resetting device: " << e.what() << std::endl; + return -1; } - } - /* Claim interface 0 of device. */ - error_code = (libusb_error) libusb_claim_interface(dev_handle, 0); - std::cout << " All Systems Nominal..." << std::endl << std::endl; + std::cout << "Initialization Process Complete." + << std::endl << std::endl; + return 0; + } boost::uint8_t data_buffer[16]; memset(data_buffer, 0x0, sizeof(data_buffer)); if (vm.count("speed")){ - error_code = fx3_control_read(dev_handle, B2XX_VREQ_GET_USB, - 0x00, 0x00, data_buffer, 1, 5000); - - boost::uint8_t speed = boost::lexical_cast<boost::uint8_t>(data_buffer[0]); - + boost::uint8_t speed; + try {speed = b200->get_usb_speed();} + catch (uhd::exception &e) { + std::cerr << "Exception while getting USB speed: " << e.what() << std::endl; + return -1; + } std::cout << "Currently operating at USB " << (int) speed << std::endl; + } - } else if (vm.count("reset-device")) { - error_code = fx3_control_write(dev_handle, B2XX_VREQ_FX3_RESET, - 0x00, 0x00, data_buffer, 1, 5000); + if (vm.count("reset-device")) { + try {b200->reset_fx3();} + catch (uhd::exception &e) { + std::cerr << "Exception while resetting FX3: " << e.what() << std::endl; + return -1; + } } else if (vm.count("reset-fpga")) { - error_code = fx3_control_write(dev_handle, B2XX_VREQ_FPGA_RESET, - 0x00, 0x00, data_buffer, 1, 5000); - - } else if (vm.count("load-fw")) { - error_code = (libusb_error) fx3_load_firmware(dev_handle, fw_file); - - if(error_code != 0) { - std::cerr << std::flush << "Error loading firmware. Error code: " - << error_code << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); - return ~0; + try {b200->set_fpga_reset_pin(true);} + catch (uhd::exception &e) { + std::cerr << "Exception while resetting FPGA: " << e.what() << std::endl; + return -1; } - std::cout << "Firmware load complete, releasing USB interface..." - << std::endl; - } else if (vm.count("load-fpga")) { - error_code = (libusb_error) load_fpga(dev_handle, fpga_file); - - if(error_code != 0) { - std::cerr << std::flush << "Error loading FPGA. Error code: " - << error_code << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); + std::cout << "Loading FPGA image (" << fpga_file << ")" << std::endl; + uint32_t fx3_state; + try {fx3_state = b200->load_fpga(fpga_file);} // returns 0 on success, or FX3 state on error + catch (uhd::exception &e) { + std::cerr << "Exception while loading FPGA: " << e.what() << std::endl; + return ~0; + } + + if (fx3_state != 0) { + std::cerr << std::flush << "Error loading FPGA. FX3 state (" + << fx3_state << "): " << b200_iface::fx3_state_string(fx3_state) << std::endl; return ~0; } std::cout << "FPGA load complete, releasing USB interface..." << std::endl; - - } else { - std::cout << boost::format("B2xx Utilitiy Program %s") % desc << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); - return ~0; } - std::cout << std::endl << "Reactor Shutting Down..." << std::endl; - - error_code = (libusb_error) libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); + std::cout << "Operation complete! I did it! I did it!" << std::endl; return 0; } diff --git a/host/utils/uhd_images_downloader.py.in b/host/utils/uhd_images_downloader.py.in index e7fc9e8a5..697bd4e16 100644 --- a/host/utils/uhd_images_downloader.py.in +++ b/host/utils/uhd_images_downloader.py.in @@ -16,117 +16,341 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -import atexit -import hashlib +import sys, os, string, tempfile, math +import traceback +import shutil, hashlib, urllib2, zipfile + from optparse import OptionParser -import os -import os.path -import shutil -import string -import sys -import tempfile -import urllib2 -import zipfile + +_DEFAULT_BUFFER_SIZE = 8192 +_BASE_DIR_STRUCTURE_PARTS = ["share", "uhd", "images"] +_BASE_DIR_STRUCTURE = os.path.join(_BASE_DIR_STRUCTURE_PARTS) +_DEFAULT_INSTALL_PATH = os.path.join("@CMAKE_INSTALL_PREFIX@", *_BASE_DIR_STRUCTURE) +_AUTOGEN_IMAGES_SOURCE = "@UHD_IMAGES_DOWNLOAD_SRC@" +_AUTOGEN_IMAGES_CHECKSUM = "@UHD_IMAGES_MD5SUM@" +_IMAGES_CHECKSUM_TYPE = "md5" +_CONTACT = "support@ettus.com" def md5Checksum(filePath): - with open(filePath, 'rb') as fh: - m = hashlib.md5() - while True: - data = fh.read(8192) - if not data: - break - m.update(data) - return m.hexdigest() + try: + with open(filePath, 'rb') as fh: + m = hashlib.md5() + while True: + data = fh.read(_DEFAULT_BUFFER_SIZE) + if not data: + break + m.update(data) + return m.hexdigest() + except Exception, e: + print "Failed to calculated MD5 sum of: %s (%s)" % (filePath, e) + raise e -class temp_dir(): +_checksum_fns = { + 'md5': md5Checksum +} +class temporary_directory(): def __enter__(self): - self.name = tempfile.mkdtemp() - return self.name + try: + self.name = tempfile.mkdtemp() + return self.name + except Exception, e: + print "Failed to create a temporary directory (%s)" % (e) + raise e + + # Can return 'True' to suppress incoming exception def __exit__(self, type, value, traceback): - os.removedirs(self.name) - -if __name__ == "__main__": + try: + shutil.rmtree(self.name) + except Exception, e: + print "Could not delete temporary directory: %s (%s)" % (self.name, e) - #Command line options - parser = OptionParser() - parser.add_option("--install-location", type="string", default="", help="Set custom install location for images") - parser.add_option("--buffer-size", type="int", default=8192, help="Set download buffer size, [default=%default]",) - (options, args) = parser.parse_args() +class uhd_images_downloader(): + def __init__(self): + pass - #Configuring image download info - images_src = "@UHD_IMAGES_DOWNLOAD_SRC@" - images_zip_md5sum = "@UHD_IMAGES_MD5SUM@" - filename = images_src.split("/")[-1] - - with temp_dir() as dirname: - os.chdir(dirname) - - #Configuring image destination - if options.install_location != "": - images_dir = options.install_location - elif os.environ.get("UHD_IMAGES_DIR") != "" and os.environ.get("UHD_IMAGES_DIR") != None: - images_dir = os.environ.get("UHD_IMAGES_DIR") - else: - images_dir = "@CMAKE_INSTALL_PREFIX@/share/uhd/images" - + def download(self, images_url, filename, buffer_size=_DEFAULT_BUFFER_SIZE, print_progress=False): opener = urllib2.build_opener() opener.add_headers = [('User-Agent', 'UHD Images Downloader')] - u = opener.open(images_src) - f = open(filename, "wb") + u = opener.open(images_url) meta = u.info() filesize = float(meta.getheaders("Content-Length")[0]) - print "Downloading images from: %s" % images_src + filesize_dl = 0 - filesize_dl = 0.0 - - #Downloading file - while True: - buffer = u.read(options.buffer_size) - if not buffer: - break - - filesize_dl -= len(buffer) - f.write(buffer) + with open(filename, "wb") as f: + while True: + buffer = u.read(buffer_size) + if not buffer: + break + + f.write(buffer) - status = r"%2.2f MB/%2.2f MB (%3.2f" % (-filesize_dl/1e6, filesize/1e6, (-filesize_dl*100.)/filesize) + r"%)" - status += chr(8)*(len(status)+1) - print status, + filesize_dl += len(buffer) + + if print_progress: + status = r"%05d kB / %05d kB (%03d%%)" % (int(math.ceil(filesize_dl/1000.)), int(math.ceil(filesize/1000.)), int(math.ceil(filesize_dl*100.)/filesize)) + status += chr(8)*(len(status)+1) + print status, - f.close() - - #Checking md5sum of zip file - downloaded_zip_md5sum = md5Checksum(filename) - if images_zip_md5sum != downloaded_zip_md5sum: - print "\nMD5 checksum does not match!" - print "Expected %s, got %s" % (images_zip_md5sum, downloaded_zip_md5sum) - os.remove(filename) - os.chdir("/".join(images_dir.split("/")[:-1])) + if print_progress: + print + + return (filesize, filesize_dl) + + def check_directories(self, dirs, print_progress=False): + if dirs is None or dirs == "": + dirs = "." + dirs = os.path.abspath(dirs) + + def _check_part(head, tail=None): + if print_progress: + print "Checking: %s" % (head) + if tail is not None and tail == "": + return True + if not os.path.exists(head): + if print_progress: + print "Does not exist: %s" % (head) + return _check_part(*os.path.split(head)) + if not os.path.isdir(head): + if print_progress: + print "Is not a directory: %s" % (head) + return (False, head) + if not os.access(head, os.W_OK): + if print_progress: + print "Write permission denied on: %s" % (head) + return (False, head) + if print_progress: + print "Write permission granted on: %s" % (head) + return (True, head) + + return _check_part(dirs) + + def validate_checksum(self, checksum_fn, file_path, expecting, print_progress=False): + if checksum_fn is None: + return (True, "") + + calculated_checksum = checksum_fn(file_path) + + if (expecting is not None) and (expecting != "") and calculated_checksum != expecting: + return (False, calculated_checksum) + + return (True, calculated_checksum) + + def extract_images_archive(self, archive_path, destination=None, print_progress=False): + if not os.path.exists(archive_path): + if print_progress: + print "Path does not exist: %s" % (archive_path) + raise Exception("path does not exist: %s" % (archive_path)) + + if print_progress: + print "Archive path: %s" % (archive_path) + + (head, tail) = os.path.split(archive_path) + + if not os.access(head, os.W_OK): + if print_progress: + print "Write access denied on: %s" % (head) + raise Exception("write access denied on: %s" % (head)) + + (root, ext) = os.path.splitext(tail) + temp_dir = os.path.join(head, root) + + if print_progress: + print "Temporary extraction location: %s" % (temp_dir) + + if os.path.exists(temp_dir): + if print_progress: + print "Deleting existing location: %s" % (temp_dir) + shutil.rmtree(temp_dir) + + if print_progress: + print "Creating directory: %s" % (temp_dir) + os.mkdir(temp_dir) + + if print_progress: + print "Extracting archive %s to %s" % (archive_path, temp_dir) + + images_zip = zipfile.ZipFile(archive_path) + images_zip.extractall(temp_dir) + images_zip.close() + + return temp_dir + + def install_images(self, source, dest, keep=False, print_progress=False): + if not os.path.exists(source): + if print_progress: + print "Source path does not exist: %s" % (source) + return + + if keep: + if print_progress: + print "Not wiping directory tree (existing files will be overwritten): %s" % (dest) + elif os.path.exists(dest): + if print_progress: + print "Deleting directory tree: %s" % (dest) + shutil.rmtree(dest) + + (head, tail) = os.path.split(source) + + if print_progress: + print "Source install path: %s" % (source) + + uhd_source = os.path.join(source, tail, *_BASE_DIR_STRUCTURE_PARTS) + + if print_progress: + print "Copying files from: %s" % (uhd_source) + print "Copying files to: %s" % (dest) + + if keep: + # mgrant @ http://stackoverflow.com/questions/12683834/how-to-copy-directory-recursively-in-python-and-overwrite-all + def _recursive_overwrite(src, dest, ignore=None): + if os.path.isdir(src): + if not os.path.isdir(dest): + os.makedirs(dest) + files = os.listdir(src) + if ignore is not None: + ignored = ignore(src, files) + else: + ignored = set() + for f in files: + if f not in ignored: + _recursive_overwrite(os.path.join(src, f), os.path.join(dest, f), ignore) + else: + shutil.copyfile(src, dest) + + _recursive_overwrite(uhd_source, dest) else: - #Extracting contents of zip file - if os.path.exists("tempdir"): - shutil.rmtree("tempdir") - os.mkdir("tempdir") - - images_zip = zipfile.ZipFile(filename) - images_zip.extractall("tempdir") + shutil.copytree(uhd_source, dest) - #Removing images currently in images_dir - if os.path.exists(images_dir): - try: - shutil.rmtree(images_dir) - except: - sys.stderr.write("\nMake sure you have write permissions in the images directory.\n") - sys.exit(0) - - #Copying downloaded images into images_dir - shutil.copytree("tempdir/%s/share/uhd/images" % filename[:-4],images_dir) - - #Removing tempdir and zip file - shutil.rmtree("tempdir") - images_zip.close() - os.remove(filename) +def main(): + if os.environ.get("UHD_IMAGES_DIR") != None and os.environ.get("UHD_IMAGES_DIR") != "": + default_images_dir = os.environ.get("UHD_IMAGES_DIR") + print "UHD_IMAGES_DIR environment variable is set. Default install location: %s" % default_images_dir + else: + default_images_dir = _DEFAULT_INSTALL_PATH + + parser = OptionParser() + parser.add_option("-i", "--install-location", type="string", default=default_images_dir, + help="Set custom install location for images [default=%default]") + parser.add_option("--buffer-size", type="int", default=_DEFAULT_BUFFER_SIZE, + help="Set download buffer size [default=%default]") + parser.add_option("-u", "--url", type="string", default=_AUTOGEN_IMAGES_SOURCE, + help="Set images download location [default=%default]") + parser.add_option("-c", "--checksum", type="string", default=_AUTOGEN_IMAGES_CHECKSUM, + help="Validate images archive against this checksum (blank to skip) [default=%default]") + parser.add_option("-t", "--checksum-type", type="string", default=_IMAGES_CHECKSUM_TYPE, + help=("Select checksum hash function (options: %s) [default=%%default]" % (",".join(_checksum_fns.keys())))) + parser.add_option("-k", "--keep", action="store_true", default=False, + help="Do not clear images directory before extracting new files [default=%default]") + parser.add_option("-v", "--verbose", action="store_true", default=False, + help="Enable verbose output [default=%default]") + + (options, args) = parser.parse_args() + + if options.buffer_size <= 0: + print "Invalid buffer size: %s" % (options.buffer_size) + return 1 + + checksum_fn = None + if options.checksum != "": + options.checksum_type = options.checksum_type.lower() + if not _checksum_fns.has_key(options.checksum_type): + print "Not a supported checksum function: %s" % (options.checksum_type) + return 1 + checksum_fn = _checksum_fns[options.checksum_type] + + url_parts = options.url.split("/") + if len(url_parts) <= 1 or url_parts[-1] == "": + print "Not a valid URL: %s" % (options.url) + return 1 + images_filename = url_parts[-1] + + images_dir = os.path.abspath(options.install_location) # This will use the current working directory if it's not absolute + + if options.verbose: + print "Requested install location: %s" % (options.install_location) + print "Images source: %s" % (options.url) + print "Images filename: %s" % (images_filename) + print "Images checksum: %s (%s)" % (options.checksum, _IMAGES_CHECKSUM_TYPE) + print "Final install location: %s" % (images_dir) + else: + print "Images destination: %s" % (images_dir) + + downloader = uhd_images_downloader() + + try: + (access, last_path) = downloader.check_directories(images_dir, print_progress=options.verbose) + if access: + with temporary_directory() as temp_dir: + if options.verbose: + print "Using temporary directory: %s" % (temp_dir) + + print "Downloading images from: %s" % options.url + + temp_images_dest = os.path.join(temp_dir, images_filename) + + print "Downloading images to: %s" % (temp_images_dest) + + (reported_size, downloaded_size) = downloader.download(images_url=options.url, filename=temp_images_dest, buffer_size=options.buffer_size, print_progress=True) + + if options.verbose: + print "Downloaded %d of %d bytes" % (downloaded_size, reported_size) + + (checksum_match, calculated_checksum) = downloader.validate_checksum(checksum_fn, temp_images_dest, options.checksum, print_progress=options.verbose) + + if options.verbose: + print "Calculated checksum: %s" % (calculated_checksum) + + if checksum_match: + if options.verbose: + if options.checksum == "": + print "Ignoring checksum" + else: + print "Checksum OK" + + try: + extract_path = downloader.extract_images_archive(temp_images_dest, print_progress=options.verbose) + + if options.verbose: + print "Image archive extracted to: %s" % (extract_path) + + downloader.install_images(extract_path, images_dir, options.keep, print_progress=options.verbose) + + print + print "Images successfully installed to: %s" % (images_dir) + except Exception, e: + print "Failed to install image archive: %s" % (e) + print "This is usually a permissions problem." + print "Please check your file system access rights and try again." + + if options.verbose: + traceback.print_exc() + else: + print "You can run this again with the '--verbose' flag to see more information" + print "If the problem persists, please email the output to: %s" % (_CONTACT) + else: + print "Checksum of downloaded file is not correct (not installing - see options to override)" + print "Expected: %s" % (options.checksum) + print "Calculated: %s" % (calculated_checksum) + print "Please try downloading again." + print "If the problem persists, please email the output to: %s" % (_CONTACT) + else: + print "You do not have sufficient permissions to write to: %s" % (last_path) + print "Are you root?" + except KeyboardInterrupt: + print + print "Cancelled at user request" + except Exception, e: + print "Downloader raised an unhandled exception: %s" % (e) + if options.verbose: + traceback.print_exc() + else: + print "You can run this again with the '--verbose' flag to see more information" + print "If the problem persists, please email the output to: %s" % (_CONTACT) + return 1 + + return 0 - os.chdir(images_dir) - print "\nImages successfully installed to: %s" % images_dir +if __name__ == "__main__": + sys.exit(main()) diff --git a/host/utils/usrp_burn_mb_eeprom.cpp b/host/utils/usrp_burn_mb_eeprom.cpp index 1b13fb615..ce0879c8e 100644 --- a/host/utils/usrp_burn_mb_eeprom.cpp +++ b/host/utils/usrp_burn_mb_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010,2013 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 @@ -19,9 +19,11 @@ #include <uhd/device.hpp> #include <uhd/property_tree.hpp> #include <uhd/usrp/mboard_eeprom.hpp> +#include <boost/algorithm/string.hpp> #include <boost/program_options.hpp> #include <boost/format.hpp> #include <iostream> +#include <vector> namespace po = boost::program_options; @@ -32,8 +34,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]") - ("key", po::value<std::string>(&key), "the indentifier for a value in EEPROM") - ("val", po::value<std::string>(&val), "the new value to set, omit for readback") + ("key", po::value<std::string>(&key), "identifiers for new values in EEPROM, separate multiple by \",\"") + ("val", po::value<std::string>(&val), "the new values to set, omit for readback, separate multiple by \",\"") ; po::variables_map vm; @@ -55,20 +57,35 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::property_tree::sptr tree = dev->get_tree(); std::cout << std::endl; - if (true /*always readback*/){ - std::cout << "Fetching current settings from EEPROM..." << std::endl; - uhd::usrp::mboard_eeprom_t mb_eeprom = tree->access<uhd::usrp::mboard_eeprom_t>("/mboards/0/eeprom").get(); - if (not mb_eeprom.has_key(key)){ - std::cerr << boost::format("Cannot find value for EEPROM[%s]") % key << std::endl; + //remove whitespace, split arguments and values + boost::algorithm::erase_all(key, " "); + boost::algorithm::erase_all(val, " "); + + std::vector<std::string> keys_vec, vals_vec; + boost::split(keys_vec, key, boost::is_any_of("\"',")); + boost::split(vals_vec, val, boost::is_any_of("\"',")); + + if((keys_vec.size() != vals_vec.size()) and val != "") { + //If zero values are given, then user just wants values read to them + throw std::runtime_error("Number of keys must match number of values!"); + } + + std::cout << "Fetching current settings from EEPROM..." << std::endl; + uhd::usrp::mboard_eeprom_t mb_eeprom = tree->access<uhd::usrp::mboard_eeprom_t>("/mboards/0/eeprom").get(); + for(size_t i = 0; i < keys_vec.size(); i++){ + if (not mb_eeprom.has_key(keys_vec[i])){ + std::cerr << boost::format("Cannot find value for EEPROM[%s]") % keys_vec[i] << std::endl; return EXIT_FAILURE; } - std::cout << boost::format(" EEPROM [\"%s\"] is \"%s\"") % key % mb_eeprom[key] << std::endl; - std::cout << std::endl; + std::cout << boost::format(" EEPROM [\"%s\"] is \"%s\"") % keys_vec[i] % mb_eeprom[keys_vec[i]] << std::endl; } + std::cout << std::endl; if (vm.count("val")){ - uhd::usrp::mboard_eeprom_t mb_eeprom; mb_eeprom[key] = val; - std::cout << boost::format("Setting EEPROM [\"%s\"] to \"%s\"...") % key % val << std::endl; - tree->access<uhd::usrp::mboard_eeprom_t>("/mboards/0/eeprom").set(mb_eeprom); + for(size_t i = 0; i < vals_vec.size(); i++){ + uhd::usrp::mboard_eeprom_t mb_eeprom; mb_eeprom[keys_vec[i]] = vals_vec[i]; + std::cout << boost::format("Setting EEPROM [\"%s\"] to \"%s\"...") % keys_vec[i] % vals_vec[i] << std::endl; + tree->access<uhd::usrp::mboard_eeprom_t>("/mboards/0/eeprom").set(mb_eeprom); + } std::cout << "Power-cycle the USRP device for the changes to take effect." << std::endl; std::cout << std::endl; } diff --git a/host/utils/usrp_n2xx_simple_net_burner.cpp b/host/utils/usrp_n2xx_simple_net_burner.cpp index c3ccba173..1898ee9ae 100644 --- a/host/utils/usrp_n2xx_simple_net_burner.cpp +++ b/host/utils/usrp_n2xx_simple_net_burner.cpp @@ -468,13 +468,21 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << "Searching for specified images." << std::endl << std::endl; if(burn_fpga){ if(!use_custom_fpga) fpga_path = find_image_path(default_fpga_filename); - else validate_custom_fpga_file(filename_map[hw_rev], fpga_path); + else{ + //Replace ~ with home directory + if(fpga_path.find("~/") == 0) fpga_path.replace(0,1,getenv("HOME")); + validate_custom_fpga_file(filename_map[hw_rev], fpga_path); + } grab_fpga_image(fpga_path); } if(burn_fw){ if(!use_custom_fw) fw_path = find_image_path(default_fw_filename); - else validate_custom_fw_file(filename_map[hw_rev], fw_path); + else{ + //Replace ~ with home directory + if(fw_path.find("~/") == 0) fw_path.replace(0,1,getenv("HOME")); + validate_custom_fw_file(filename_map[hw_rev], fw_path); + } grab_fw_image(fw_path); } |